C Histroy
C Histroy
A
Tutorial for Beginners
Dionysia Lemonaki
This is not a complete guide to the language, but will rather give
you a high level understanding of important C concepts and ideas as
an absolute beginner to coding.
Each language has its own syntax and specific ways of doing
things, but the concepts covered here are common and applied to all
programming languages.
They did this by handing the sheets of paper the program was
written on to operators who used a key punch machine that would
punch the card's holes and represent the data and instructions on the
card.
Something that might have taken days to complete now took much
less time. And this started enabling greater access to computing.
There were many problems with the game – it did not work as well
on GECOS as it did on MULTICS and he needed a different and
less expensive machine to run it on.
The DEC PDP-7 had very little memory, just 8KB. To deal with
this restriction, the filesystem, the first version of the Unix kernel,
and practically everything else in the project were coded in
Assembly. Using Assembly allowed Thompson to directly
manipulate and control each part of the memory on that computer.
Using high level languages also means writing less code to achieve
something, whereas assembly programs were extremely long.
Thompson wanted to use a higher level language for Unix from the
very start, but was limited by the DEC PDP-7.
In 1970 they managed to get funding for the bigger and more
powerful DEC PDP-11 that had substantially more memory.
So in 1972 C was born, and the first C compiler was written and
implemented for the first time on the DEC PDP-11 machine.
The famous picture of Thompson and Ritchie working on a PDP-11, Image source
Wikipedia
The C Programming Language
In 1973 Dennis Ritchie rewrote the Unix source code and most
Unix programs and applications using the C programming
language. This made it the standard implementation language of the
operating system.
Towards the late 1970's, C's popularity started to rise and the
language started getting more widespread support and use. Up until
that point, C was still only available for Unix systems and
compilers were not available outside of Bell labs.
This increase in popularity came from not only the power C gave to
the machine but also to the programmer. It also helped that the Unix
operating system was gaining the same popularity at an even faster
rate.
Unix stood out from what came before because of its portability and
its ability to run on a variety of different machines, systems, and
environments.
C made that portability possible and since it was the language of the
Unix system, it gained more notariety – so more and more
programmers wanted to try it out.
With all the iterations and adjustments, though, this book no longer
described the language as it was, and the changes to the language
started to cause problems.
The C Standard
To make sure there was a standard, machine independent definition
of the language, ANSI (the American National Standards Institute)
formed a committee in 1983. This committee was named the X3J11
committee, and their mission was to provide a clear, comprehensive
definition and standardization of C.
After a few years, in 1989, the committee's work was done and
made official. They defined a commercial standard for the
language. That version of the language is known as 'ANSI C' or
C89.
C was used all around the world, so a year later in 1990 the
standard was approved and adopted by ISO, the International
Standards Organization. The first version, C90, was called ISO/IEC
9899:1990.
Since then, many revisions to the language have taken place.
The Continuation of C
C forged a path for the creation of many different programming
languages. Many of the modern high level programming languages
that we use and love today are based on C.
It owes its popularity to the rise and success of Unix, but nowadays
it has gone far beyond just being the 'native' language of Unix. It
now powers most, if not all, of the world's servers and systems.
The programmer does a lot of the heavy work and the language lets
you manage and structure memory in an efficient way for the
machine delivering high performance, optimisation, and speed. C
lets the programmer do what needs to get done.
Where Is C used?
There is a lot of C code in the devices, products, and tools that
billions of us use in our everyday lives. This code powers
everything from the world's supercomputers to the smallest gadgets.
C code makes embedded systems and smart devices of all kinds
work. Some examples are household appliances like fridges, TVs,
coffee makers, DVD players, and digital cameras.
Many browsers and their extensions are built with C, like Google
Chromium and the Google file system. Developers also use C often
in database design (MySql and Oracle are two of the most popular
database systems built in C), and it powers advanced graphics in
many computer games.
From this general overview, we can see that C and it's derivative C+
+ run a large part of the internet and the world at large. Many of the
devices and technologies we use in our daily lives are written in or
depend on C.
Essentially all programs are just plain text files stored on your
computer’s hard drive that use a special syntax which is defined by
the programming language you're using.
Each language has its own rules that dictate what you can write and
what's considered valid, and what is not.
What is a compiler?
Programs are written by us and for us. They are meant to be
understood by humans.
After this, the program can run and the computer does what the
program tells it to do. Compiled programs have a stronger
correspondence with the underlying hardware and can more easily
manipulate the computer's CPU and memory.
In the rest of this section we'll see examples using this compiler and
I've based these examples on a Unix or Unix-like system. So if you
have a Windows system, make sure to enable the Windows
Subsystem for Linux.
First, make sure you have the GCC compiler installed. You can
check by opening your terminal and typing gcc --version in the
prompt which is typically after the $ character.
If you're using MacOS and have not installed the command line
developer tools, you will get a dialog box pop up asking you to
install them – so if you see that, go ahead and do so.
Once you have those installed, open a new terminal session and re-
type the gcc --version command. If you have already installed the
command line tools, you should get the output below:
The term compiling alone is an abstraction and simplification,
though, since in reality there are many steps happening behind the
scenes. These are the finer lower level details that happen between
us writing, compiling, and then running our C program. Most even
happen automatically, without us even realising it.
For this you can use a command-line text editor like nano or Vim if
you are comfortable with those.
In order to see what our code does, we have to run the program we
have just written. Before running it, though, we have to first
compile it by typing some commands in the terminal.
We can continue using the command line on our computer or we
can use the integrated terminal in VSCode (by holding the control
~ keys at the same time a new terminal window opens).
So far we can see on the left panel that there is only one file in
our cprogram directory, hello.c, which contains our C code.
The term 'compiling our C code' doesn't just happen in one step. It
also involves some smaller actions that occur automatically for us.
If there are any mistakes with the correctness of our code or we're
not following the semantics of the language, we'll see some errors
and the compilation will end. We would have to correct the
mistakes and start the process from the beginning.
Compiling in C
After the preprocessing step which produces preprocessed C source
code, next we have to compile the code. This involves taking the
code that is still source code and changing it into another
intermediate form. We use a compiler for this step.
Every CPU – the brains of the computer – has its own set of
instructions. Assembly code uses specific statements and
commands that directly correlate to those instructions and low level
operations that a CPU performs and carries out.
Assembling in C
Assembling means taking the hello.s file containing assembly
code statements as input and, with the help of another program that
is executed automatically in the compilation process, assembling it
to machine code instructions. This means it will have as output
actual 0s and 1s, or binary format statements.
This step also happens behind the scenes, and it results in the final
language the instructions in our source code are translated to. And
now the computer can finally understand those instructions.
Each of the commands we wrote in our C source code were
transformed to assembly language statements and finally into the
equivalent binary instructions. All this happened just with the
command gcc. Whew!
The code we wrote is now called object code, which a specific
computer's CPU can understand. The language is incomprehensible
to us humans.
Linking in C
In the images above, you might have noticed an a.out file in our
directory.
This is the default step and file that gets created when we type the
compiler command and our filename, gcc hello.c in our case.
If we had used the command gcc -o hello hello.c mentioned
earlier, we would have seen a custom named hello executable
program in place of a.out.
The a.out stands for assembly output. If we type ls in the terminal
to list the files in our directory, we see that a.out even looks
different from the rest:
Linking is the final stage of the compilation process where the final
binary file hello.o is linked with all the other object code in our
project.
So if there are other files containing C source code (like files
included in our program that implement C libraries which are
already processed and compiled, or another file we have written
named, for example, filename.cbesides hello.c), this is when the
object file filename.o will be combined with hello.o and the other
object codes, linking them all together.
This forms one big executable file with the combined machine
code, a.out or hello, which represents our program.
Since we're finally done compiling, the program is in its final form.
And now we can execute and run the file on our machine by
typing ./a.out. This means 'run the a.out file that is in the current
directory', since ./ represents the folder we are in. We then see the
output of our program in the terminal:
#include<stdio.h>
int main(void)
{
// print hello world to the screen
printf("Hello world\n");
}
Header files in C
Header files are external libraries. This means they are a set of code
already written by some developers for other developers to use.
They provide features that are not included at the core of the C
language. By adding header files to our code, we in return get
additional functionality that we can use in our programs.
int main(void)
{
}
This is the main starting function of a C program. The curly braces
({}) are the body which wraps all the code that should be in our
program.
This line acts as a boilerplate and starting point for all C programs.
It lets the computer know where to begin reading the code when it
executes our programs.
Comments in C
Whatever we write after the // will not affect how our code runs
and the computer will not take it into account during compilation
and execution time.
Those two lines indicate that you're adding comments, which are
notes to our future selves and to our coworkers. Comments can help
us remember and remind others what a certain line of code does or
why we wrote that code in the first place. It also reminds us what
exactly is the purpose of that code when we come back to it the next
day of even months later.
A data item that may take on more than one value during the
runtime of a program.
In the simplest terms, you can think of variables as a named box. A
box that acts as a storage place and location for holding different
information that can vary in content.
Each box has a unique name which acts like a label put on the
outside that is a unique identifier, and the information/content lives
on the inside. The content is the variable's value.
Variables hold and point to a value, to some useful data. They act as
a reference or abstraction to literal data. That data is stored in the
computer's memory, and takes up an certain amount of space. It
lives there so we can retrieve it later and use it in our programs
when we need to.
As the name suggests, what variables point to can vary. They are
able to take different values over time as information changes
during the life of the program.
Variable Assignment in C
The process of naming a variable is called assignment. You set a
specific value that is on the right, to a specific variable name that is
on the left. You use the = or the assignment operator to do this.
As I mentioned, you can change a variable's value, so you can
assign and reassign variables. When you reassign a value, the new
value points to the variable name. So the value can be a new one,
but the variable name stays the same.
A variable's type is the type of the value it holds. This lets the
program and later the compiler know what kind of information it's
storing.
int main(void)
{
int n = 27;
// int is the data type
// n is the name
// n is capable of holding integer values
// positive/negative whole numbers or 0
// = is the assignment operator
// 27 is the value
}
What is the difference between initialising and declaring a variable?
In summary:
#include<stdio.h>
int main(void)
{
int age = 27;
age = 37;
// the new value of age is 37
}
Rules for naming variables in C
Variable names must begin either with a letter or an underscore, for
example age and _age are valid.
A variable name can contain letters (uppercase or lowercase),
numbers, or an underscore.
There can be no other special symbols besides an underscore.
Variable names are case sensitive, for example age is different
from Age.
The scope of a variable in C
The scope of a variable refers to where the variable can be
referenced and accessed from. It is essentially where the variable
lives and is valid and how visible it is to the rest of the program.
Local scope
If a variable is declared within a set of culry braces, {}, like for
example a specific function, that will be its scope and we can't
access it and use it outside those braces in the rest of the program.
The rest of the program won't know it exists.
Therefore it is not a good idea to declare variables that way since
their scope and use is so limited which can lead to errors. This
scope is called local scope.
Global scope
If variables are declared outside of functions, they
have global scope. Having a global scope means they are visible
within the whole program and can be accessed from anywhere.
But keep in mind that it can be difficult to keep track of them. Also,
any changes we make to them along the way can get confusing
since they can happen in any part and location of the program.
Data Types in C
Data types specify in what form we can represent and store
information in our C programs. They let us know how that
information will be used and what operations can be performed on
it.
Data types also determine what type of data our variables can hold,
as each variable in C needs to declare what data type it represents.
There are 6 data types built into the language. But you can convert
between different types which makes it not as strongly typed.
Each of the data types requires a different allocation of memory and
each data type can have different ranges up to which they can store
values.
Operators in C
Arithmetic operators in C
Arithmetic operators are mathematical operators that perform
mathematical functions on numbers. Operations can include
addition, subtraction, multiplication, and division.
+ for addition
- for subtraction
* for multiplication
/ for division
% for modulo division (calculating the remainder of the division)
Assignment operator in C
The assignment operator, =, assigns a value to a variable. It 'puts' a
value into a variable.
In other words, it sets whatever is on the right side of the = to be the
value of the variable on the left side of the =.
There are specific assignment operators for updating a variable by
modifying the value.
Greater than >
Greater than or equal to >=
Less than <
Less than or equal to =<
There is also an equality comparisson operator, ==. Don't confuse
this with =, the assignment operator.
We use the == to compare two values and test to see if they are
equal or not. This operator asks the question 'Are these two equal?',
whereas = assigns a value to a variable.
When using the equality comparisson operator and asking the above
question, there is always a return value that can either
be true or false, otherwsie knokn as a Boolean value in the
context of computer programming.
Lastly, there is the inequality operator, !=, that we use to test
whether two values are NOT equal.
Functions in C
Functions are verbs, that is, small actions. They do something. They
perform a particular, specific task.
You can't do anything more with that output after the effect. Like in
the case of printf("Hello world!");, the output is the string
"Hello world!" printed to the screen, and that's it. You can't use that
string in some other way, because printf has no return value.
These types of functions are known as side effects, meaning they
have an immediate observable effect without returning a value.
Also, a function like printf is a function invocation and in
the stdio library is defined as int printf(const char
*format,...);.
Second, the output can be reusable, and has a return value. A return
value is a value passed back to the programmer and stored in a
variable for later use.
void say_hi(void)
{
printf("hello");
}
By writing the function's name, followed by any arguments in
parentheses and a semicolon like say_hi();. The say_hi function
takes in no inputs and has no return value. When called it just prints
'hello' to the screen.
Another function like:
int square(int n)
{
return n * n
}
is called in the same way as the previous example. In this case,
the square function take in an input and has a return value (both
are ints). The input it takes in is the parameter called n, that returns
an int when the function is called.
The word return specifies that what will get returned, the
input n multiplied by itself.
For example, when the function is called square(3);, n acts as a
variable that points to the parameter that has been passed in to the
function, like 3. It is like we have set n = 3. The value that gets
returned is 9.
Functions are meant to be reused, so we can use it anytime we wish
to square a number:
#include <stdio.h>
int square(int x)
{
return x * x;
}
int main(void)
{
printf("%i\n", square(2));
printf("%i\n", square(4));
printf("%i\n", square(8));
}
if (x < y)
if ( x > y)
{
printf("x is larger than y");
}
else
{
printf("x is less than y");
// Because x > y is false ,
// this block of code will be executed
// resulting in printing the statement of the else branch
}
}
If we wish to chose between more than just two options and want to
have a greater variety in statement and actions, then we can
introduce an else if condition.
This means that 'If this condition is true, do this. If it is not, do this
thing instead. However, if none of the above is true, finally do this
instead.'
#include <stdio.h>
int main(void)
{
int x = 2;
int y = 2;
if(x < y)
// if this condition is true run this block
{
printf("x is less than y");
}
else if(x > y)
/ / if the above statement was true run this block instead
{
printf("x is greater than y");
}
else
// if this block of code runs
//it runs because x < y was false
//and so was x > y
//so it means x == y
{
printf("x is equal to y");
}
}
While loops in C
Before they run any code, while loops have to check a condition. If
it is met, the code runs. If not, the code doesn't take any action. So,
code is not guaranteed to run even at least one time if a condition is
not met.
#include <stdio.h>
int main(void)
{
while(true)
{
printf("Hello world");
}
}
The while keyword is used along with a required boolean
expression, true in this case (which always stays true).
After printing the line of code inside the curly braces, it
continuously checks wether it should run the code again. As the
answer is always yes (since the condition it needs to check is
always true each and every time), it runs the code again and again
and again.
In this example the only way to stop the program and escape from
the endless loop is running Ctrl + C in the terminal.
If the condition was false, it would never run the code inside the
curly braces.
Another loop, is a loop that repeats something a certain number of
times.
#include <stdio.h>
int main(void)
{
int i = 0;
In our example, the code will run at least one time and the statement
will be printed at least once. Next, the value is incremented. It then
checks if the value is less than 20, and if so, it runs the code again.
It will stop running the code once the value being incremented each
time is no longer less than 20.
I hope this gave you an insight into the 'whys' and the 'hows' of the
language and the fundamentals you need to know to start writing
basic programs in C.