Introduction To C Programming
Introduction To C Programming
Introduction To C Programming
4.1 Introduction
This is only a very basic guide to get you started with the C programming language. There's lots
more where this came from....
All C programs will have a part of them that looks like this:
int main ()
{
//There will be a whole lot of C program code here
}
This piece of code is the “entry point” into the program. The name “main” is reserved for the main
piece of code in your software.
The curly brackets “{}” form a pair. Within these brackets will be the main program code. In
general curly brackets are used to group multiple statements into one compound statement.
Comments come in two forms, the double slash as used above means that everything following on
the line is a comment. The other form of comment is like this:
/* This is
a multiple line
comment */
Everything between the slash and stars is a comment, no matter how many lines it spans. Not all
compilers support the use of the double slash form. Comments are ignored by the compiler; they are
purely for documentation purposes.
C is CASE SENSITIVE. A small letter is not the same thing as a capital letter. By default the C
function names will use small letters.
• char
• unsigned char
• int
• unsigned int
• float
• double
87
88
The “char” type is used to represent a single character. This requires 8 bits (1 byte) of storage.
Chars may be stored in “signed” or “unsigned” format. If it is signed then the range of this
number is -128 to +127 decimal. If unsigned format is used then the range will be 0 to 255 decimal.
All of the ranges shown here include both ends. Two's compliment is used to represent the signed
numbers. A char may be thought of in two different ways. You could either regard it as a simple 8
bit number, or you could regard it as a letter of other ASCII symbol. Some people find this
confusing, but remember that each symbol that can be represented is simply an 8 bit number. The
variable stores that number. The only difference is in the interpretation that is used when reading
the number. A copy of the ASCII table is shown here: (sourced from
http://www.jimprice.com/ascii-0-127.gif)
Ignore the “control character” column for now; it will become clearer later on.
The integer (int) type is used to store integers of a moderate size in an efficient manner. The range
of storage depends on the compiler being used. For our PC based compiler it is 32 bits and for our
microcontroller compilers it will be either 8 or 16 bits.
The “float” data type is a 32bit floating point number. This is a number represented using a 32
bit representation in which precision is traded off for size. The bigger the number the fewer digits
on the right hand side of the decimal point.
The option to make some types unsigned is exercised by means of “type modifiers”, other type
modifiers are “long” and “const”.
int main ()
{
unsigned int length, breadth; /*declare 2 variables
called length and breadth*/
char fred;
//still no code here...
}
I can also declare arrays of variables by putting the size of the array (number of elements) after the
array declaration:
This will create an array of 10 integers. The array can be represented graphically as follows:
arr1[0] arr1[1] arr1[2] arr1[3] arr1[4] arr1[5] arr1[6] arr1[7] arr1[8] arr1[9]
Each of the empty boxes can hold one integer. Note the syntax that is used if we wish to access one
element of the array. When we access an array element we state its index in square brackets. In C
all array indexes start from zero. There is no “option base” available. When we declare an array we
always put the number of elements in square brackets.
#include <stdio.h>
int main ()
{
printf (“Hello World”);
}
The printf statement causes the text Hello World to be put on the screen. The printf
command is defined in a library called “stdio”, and for that reason we need the line
#include <stdio.h> to tell the C compiler where to find this command.
If we want to print a variable we will need to be slightly more complicated. We need to tell the C
compiler what formatting to use for our type of variable.
#include <stdio.h>
int main()
{
int temp1;
float temp2;
char temp3;
temp1 = 2;
temp2 = 34567890;
temp3 = 'X';
If we want to put each new number on a new line we can add formatting characters like this:
printf ("%i\n",temp1); //print an integer
printf ("%f\n",temp2); //print a floating point number
printf ("%c\n",temp3); //print a character
'\n' is a newline character. We can also put text and variables on a line like this:
In this case the variable will be inserted into the text at the location of the %i marker. The newline
is after the formatting string, and thus the text and the variable will appear on the same line. Using
the wrong formatting string for your variable type can result in strange behaviour.
temp1 = 2;
temp2 = 34567890;
temp3 = temp1*temp2;
int temp1;
temp1 = 2;
temp1++;
//at this point temp1 = 3.
Example:
int temp1, temp2;
temp1 = 3;
temp2 = temp1++;
//at this point temp1= 4 and temp2=3.
This is because temp1 was only incremented after the assignment had taken place.
If you want to increment temp1 before the assignment takes place you do it like this:
is equivalent to:
if (condition) statement;
The condition must evaluate to either true or false. C does not provide Boolean data types. Rather
the “false” is represented by zero (e.g. an int or char with all the bits cleared) and true is
represented by non-zero.
Example:
#include <stdio.h>
int main()
{
int temp1;
temp1 = 2;
if (temp1)printf ("True"); // Will print the word True.
}
Example:
#include <stdio.h>
int main()
{
int temp1;
temp1 = 0;
if (temp1)printf ("True"); // Will NOT print the word True.
}
#include <stdio.h>
int main()
{
int temp1;
temp1 = 1;
if (temp1)printf ("True");
else printf ("False");
}
How does the compiler know where the if statement ends? C has a simple rule for if/else
statements: An if statement will ALWAYS only execute one statement or compound statement.
Compound statements are formed by enclosing a set of statements in curly brackets. Look carefully
at the difference between these two code examples:
Example 1:
if (temp1)
printf ("True"); // prints out if temp1 is true
printf ("another statement"); //prints out irrespective of temp1
Example 2:
if (temp1)
{
printf ("True"); // prints out if temp1 is true
printf ("another statement"); // prints out if temp1 is true
}
Forgetting the curly braces is a common source of programming errors. Some programmers always
put the opening curly bracket on the same line as the if statement.
In order to AND two logical conditions together we use the && operator. An example:
#include <stdio.h>
int main()
{
int temp1;
int temp2;
temp1 = 0;
temp2 = 1;
if ((temp1)&&(temp2))
{
printf ("True"); /* only prints out if temp1 is true and temp2 is
true.*/
}
}
Notice in the above example how the individual conditions have been bracketed. This removes all
doubt about the interpretation of this condition.
#include <stdio.h>
int main()
{
int temp1;
temp1 = 0;
if (!(temp1))
{
printf ("False"); //this prints out if temp1 is false
}
}
Example:
#include <stdio.h>
int main()
{
int temp1;
int temp2;
temp1 = 10;
if (temp1==10)
{
printf ("True"); //this prints out if temp1 is 10
}
}
Be careful over here. The single = is used for assignment. This is shown in the line temp1 = 10.
The double == is used as an equality operator. If you use a single = in an if statement it will
perform assignment instead of comparison!
Example:
#include <stdio.h>
int main()
{
int temp1;
int temp2;
temp1 = 0;
if (temp1=10) //BIG BUG HERE...
{
printf ("True"); //this ALWAYS prints out
}
printf ("%i",temp1); //This prints new value of temp1, i.e 10
}
If you want to see if two things are not equal you use the != operator:
if (temp1!=10)
{
printf ("True"); //this prints out if temp1 is not 10
}
You can also check relative magnitudes of numbers using the >, < <= and >= operators. These
are all shown in this example:
if (temp1<=10)
{
printf ("Less or equal"); /* prints if temp1 is less
than or equal to 10 */
}
if (temp1>=10)
{
printf ("Greater or Equal"); /* prints if temp1 is
greater than or equal to 10 */
}
if (temp1<10)
{
printf ("Smaller"); /* prints if temp1 is less
than 10 */
}
if (temp1>10)
{
printf ("Greater"); /* prints if temp1 is
greater than 10 */
}
}
The above operators all work on a whole variable at a time. There are also bitwise operators which
work on individual bits of a variable. These operators are AND, OR, NOT and XOR.
The OR operator is shown in action here. The symbol for bitwise OR is a single pipe | symbol.
Don't confuse it with a double pipe symbol.
#include <stdio.h>
int main()
{
int temp1;
int temp2;
int temp3;
temp1 = 0x24; //Hexadecimal 24 written like this
temp2 = 0x42;
temp3 = temp1|temp2; //Bitwise OR operator
printf ("%x",temp3); //prints temp3 in hexadecimal. Output is 66, hex.
}
The bitwise AND operator is a single ampersand (&), as shown in this example:
In this example there are a few important features. Firstly notice the difference between the '!'
operator and the '~' operator. The '~' has taken each bit in temp1 and inverted it. Secondly note
University of Cape Town
96
that the '!' has taken the number 0x78 as a binary condition (it is non-zero, so it is true) and
inverted that to arrive at an answer of zero, which is equivalent to a false condition. The “int” data
type is 32 bits long, so when we assign the number 0x78 to it, we are actually assigning it the
number 0x00000078. When this is complimented we therefore get 0xffffff87.
There are also left and right shift operators. These are the “>>” and “<<” operators. The direction in
which they shift is fairly intuitive.
Notice here that 0x78 = 120 decimal. 120 decimal divided by four (shifted 2 bits to the right) is 30
decimal, which is 0x1E, as expected. Similarly 120 decimal multiplied by 8 (shift 3 bits to the left)
is 960 decimal, which is 0x3C0.
The condition must evaluate to either true or false. There must either be only one statement, or there
can be multiple statements grouped within brace brackets. The condition is formulated in the same
way as it was for the if statement.
One common mistake is to put a semicolon after the while statement. A semicolon is effectively a
statement, so any while loop with a semicolon immediately following it will be an infinite do-
nothing loop.
#include <stdio.h>
int main()
{
int temp1;
temp1 = 0;
while (temp1<10)
{
//compound statement...
temp1++;
printf ("%i",temp1); //this prints 12345678910,number by number
}
temp1 = 0;
while (temp1<10); //infinite loop
{
//compound statement...
temp1++;
printf ("%i",temp1); //this never executes.
}
}
The condition for a while loop is examined before any code inside the loop body is executed. This
means that if the loop condition is false the code in the loop will never be executed.
This loop differs from the while loop in that the loop condition is examined at the end of the
statement. The basic structure of the loop is:
#include <stdio.h>
int main()
{
int temp1;
temp1 = 0;
do
{
temp1++;
printf ("%i",temp1); // this prints 12345678910, number by number
} while (temp1<10);
temp1 = 20;
do
{
temp1++;
printf ("%i",temp1); //This prints 21
}
while (temp1<10); // this is only examined after the first printf
temp1 = 20;
while (temp1<10); // this is examined before any printf is executed
{
temp1++;
printf ("%i",temp1); //This never executes
}
}
The “break” statement tells the computer to break out of the loop immediately. The statement
following the loop will be executed after the program performs a break operation. The break
statement works identically in all types of loops.
temp1 = 0;
while (temp1<10)
{
temp1++;
printf ("%i",temp1);//this only executes once, printing the number 1
break;
}
}
The “continue” statement causes the loop to re-examine the loop condition and start the next
loop through the code if that condition is still true.
#include <stdio.h>
int main()
{
int temp1;
temp1=0;
do
{
temp1++;
printf ("%i",temp1); /* This prints 12345678910, we only
increment once per loop.*/
continue;
temp1++; // this line never executes
}
while (temp1<10);
temp1 = 0;
while (temp1<10)
{
temp1++;
printf ("%i",temp1); /* This prints 12345678910, we
only increment once per loop*/
continue;
temp1++; //this line never executes
}
temp1 = 0;
while (temp1<10)
{
temp1++;
printf ("%i",temp1); /*This prints the number
1234579*/
if (temp1<5) continue;
temp1++; /*This line only executes when
temp1 is greater than 5.*/
}
}
Every time the for loop executes (every iteration) it modifies a variable called the “loop
variable”. It modifies this variable by performing the “loop modifier” operation. Examples
of loop modifiers would be increment, decrement etc. Every iteration the test condition is checked
and if it is found to be true then the next iteration will commence.
#include <stdio.h>
int main()
{
int temp1;
for (temp1 = 0;temp1 < 10;temp1++)
{
printf ("%i ",temp1); //prints out 0 1 2 3 4 5 6 7 8 9
}
}
In the above example the loop variable is called temp1. Every iteration it is incremented and
compared with the number 10. If it is smaller than 10 the next iteration will occur. Note carefully
that in the above program the output started at zero. This is because the increment (loop modifier)
occurs at the end of the iteration.
The for loop assumes only one statement or compound statement. Watch out for misplaced
semicolons and missing brace brackets. The loop modifier need not be a simple increment. It can be
more complicated as shown here:
#include <stdio.h>
int main()
{
int temp1;
for (temp1 = 1;temp1 < 65;temp1*=2)
printf ("%i ",temp1); //prints out 1 2 4 8 16 32 64
}
or you can even list multiple loop modifiers by separating them into a list using a comma separator.
#include <stdio.h>
int main()
{
int temp1;
int temp2;
temp2 = 1;
for (temp1=1;temp1<65;temp1*=2,temp2*=3)
{
printf ("%i ",temp1); //prints out 1 2 4 8 16 32 64
printf ("%i ",temp2); //prints out 1 3 9 27 81 243 729
}
}
In this example the outputs from the two printf statements will be interleaved, thus we will get:
1 1 2 3 4 9 8 2716 81 32 243 64 729
The variable which is being examined here is temp1. It is being checked to see if it has the value 1,
in which case the program output will be “1”, or 2, which causes “2” to print or 3, in which case the
program outputs “3”. If temp1 is not one of the above then the “default” clause will take effect
and the program will print “none of the above”. You will notice that after every case there is a break
statement. This causes the program flow to skip all of the following cases and resume execution
after the closing brace of the case statement. If this is omitted C does a strange thing: it will cause
ALL of the cases following the valid case to be executed. We do not need a break statement after
the default statement because there are no more cases after that. The order of the cases does not
matter.
The variable which is being examined in the case statement must be an integer-style data type. Ints,
chars and variations on these are acceptable, but doubles and floats are not. This is because a
floating point data type will not have a reliably precise value, it will only be an approximation
(albeit very close) to the desired value.
For every C function we will pass data to that function, in the form of parameters (also called
arguments) and the function will return exactly one value, called the “return value”.
We now need to introduce our final basic data type. This is called void. This data type holds no
value and takes no space. It is a place holder used where no data is to be exchanged.
We also need to declare each function before we invoke it. This is done as follows:
When we wish to invoke the function (call it, execute it) we use the function name followed by the
parameters which we wish to pass into the function.
function name (parameters);
int main()
{
func1(); //invoke the function
}
In this example we have a function which prints output to the screen. Because the function always
does the same thing and there is no data being passed to it, there is a single void parameter. The
function produces no output and so the return type is also void. Notice that we declared and defined
the function outside of any other function.
#include <stdio.h>
int func2 (int var1, int var2); //declare the function
int main()
{
int temp1,temp2,temp3;
temp1 = 2;
temp2 = 3;
Now the function takes in two parameters, adds them together and returns the result. Note the use of
the “return” keyword. This tells the function to send the answer back to the caller, in this case
main. The return keyword causes the function to exit.
int main()
{
int temp1,temp2,temp3;
temp1 = 50;
temp2 = 30;
temp3 = func2(temp1,temp2); //invoke the function
printf ("%i",temp3);
}
When the function is invoked it will execute and return an integer. The caller sees the function as if
it were an integer itself. We say that the function evaluates to an integer. Because of this the
following is also legal:
#include <stdio.h>
int func2 (int var1, int var2); //declare the function
int main()
{
int temp1, temp2;
temp1 = 50;
temp2 = 30;
printf ("%i",func2 (temp1,temp2));
}
We can give the parameters default values. This means that we do not always need to specify the
value of a parameter. An example is shown here:
#include <stdio.h>
int func2 (int var1, int var2=10); //declare the function
int main()
{
int temp1,temp2;
temp1 = 50;
temp2 = 30;
int main()
{
int temp1;
temp1 = 1;
myfunc (temp1);
printf ("%i",temp1); //prints 1, NOT 222
system("PAUSE");
}
One exception to this rule is when we pass an array into the function. Because copying an entire
array might be a lengthy process it is avoided and only the starting address of the array is copied
onto the stack instead of the entire array. This means that if a function modifies an array element
then the original array will be modified.
#include <stdio.h>
int main()
{
int temp1 [5]; //array of integers
temp1[3]=1;
This example shows two things. Firstly it shows how the original array element has been
overwritten. Secondly it shows the syntax of passing an array into a function as a parameter.
We also have a concept called “scope”. The scope of a variable is the region of code in which that
variable is valid. This leads to the concept of “local variables” and “Global variables”. Local
variables are only accessible within the function that they are declared and global variables are
accessible anywhere within the program. Local variables are normally created on the stack and
therefore they are destroyed when you return from their function. An example of different scopes is
shown:
int main()
{
int temp1; //A variable which is local to main
//Your code here...
}
Because a local variable exists on the stack it will lose its value between function calls. Remember
though that C does not perform automatic initialization of variables unless it is specifically told to.
This program will probably do what is expected even though it is wrong:
#include <stdio.h>
int main()
{
int temp1; //A variable which is local to main
for (temp1=0;temp1<10;temp1++)
{
myfunc1();
}
}
void myfunc1 ()
{
int local_var; //A variable which is local to myfunc1
local_var++;
printf ("%i ",local_var);
}
The reason that this will work is that the stack on the computer is being used in the same way for
each function call. This means that each time the function runs it uses the same piece of memory for
the local variable. This means that the local variable will appear to hold its value. As soon as you
start using the stack in a more complex way this convenient behaviour will vanish. Here is a
program that looks identical except that there are now two functions being called alternately.
int main()
{
int temp1; //A variable which is local to main
for (temp1 = 0;temp1<10;temp1++)
{
myfunc1();
myfunc2();
}
}
void myfunc1 ()
{
int local_varx; //A variable which is local to myfunc1
local_varx++;
printf ("%i ",local_varx);
}
void myfunc2 ()
{
int local_vary; //A variable which is local to myfunc2
local_vary--;
printf ("%i ",local_vary);
}
When you run this program you will see that the first function increments its local variable. The
second function uses the same space on the stack and decrements the variable. Overall the variable
goes nowhere.
#include <stdio.h>
int main()
{
int temp1,temp2=0; //A variable which is local to main
for (temp1=0;temp1<10;temp1++)
{
myfunc1();
temp2=myfunc2(temp2);
}
}
void myfunc1 ()
{
static int local_var; //A variable which is local to myfunc1 but static
local_var++;
printf ("%i ",local_var);
}
The method which passes the variable back and forwards also gives us an easy way of initializing
that variable. When a local variable and a global variable have the same name the variable with
more local scope is always used. Functions in C can only ever return one item. You cannot return
many variables or even arrays. We will see a solution to this later.
#include <stdio.h>
int main()
{
char mystring [4]; //a string is an array of characters
mystring[0]='C';
mystring[1]='a';
mystring[2]='t';
mystring[3]='\0';
printf (mystring); //This prints the word "Cat".
}
You will need to leave one character open at the end of the string of the zero element, which is
called a “null terminator”. If you want a three character string you will need a four element array. If
you forget the null terminator then the program will print whatever it finds in memory until it
reaches the first null character that happened to be there.
There is a set of string handling functions in the standard C libraries. We will look at that in a few
section's time. To whet you appetite here is an example function which determines the length of a
string:
int len (const char instr [])
{
int count=0;
while (instr[count]) count++;
return count;
}
We have used the 'const' keyword to specify that the function can not change the string array.
Remember that in the case of arrays changing the contents of the array will change the original
array, not just a copy.
We can initialize strings when we declare them by using the following syntax:
#include <filename>
This directive effectively tells the compiler to take the code from the file with name “filename”
and paste it into the current file at the position of the directive. This is used to include header files
(files of function declarations) into your code.
#include “filename”
This acts in the same way as the above directive except that it is used with non standard files, such
as your own files. If you project gets too big for one file then you can use #include and multiple
files.
#define myconst 32
#define directives are used to implement replacements. In the above example the compiler will
look for the word “myconst” and replace it with the number 32. If at some stage myconst needed
to be changed then all we'd have to do would be to change the number 32. #define is also used to
make code more intelligible, although this is often lost on the reader.
#include <stdio.h>
#define integer "%i"
int main()
{
printf (integer,42);
}
The word “integer” will be replaced with a %i formatting string. There is a set of directives
which include #if, #ifdef etc, which are used for conditional compilation, but they will not be
discussed here.
We are only going to discuss some libraries and some functions here, as the complete list is
extensive.
i. <stdio.h>
This is a library of input and output functions. It is humongous. I have only shown a few percent of
it here. We will discuss more functions from this library in later sections.
• int printf(const char* format, ...);
printf(f, ...) is equivalent to fprintf(stdout, f, ...)
• int sprintf(char* s, const char* format, ...);
Like fprintf, but output written into string s, which must be large enough to hold
the output, rather than to a stream. Output is NUL-terminated. Returns length (excluding
the terminating NULL).
• int scanf(const char* format, ...);
scanf(f, ...) is equivalent to fscanf(stdin, f, ...)
• int sscanf(char* s, const char* format, ...);
Like fscanf, but input read from string s.
• int fgetc(FILE* stream);
Returns next character from (input) stream stream, or EOF on end-of-file or error.
• int putchar(int c);
putchar(c) is equivalent to putc(c, stdout).
• int puts(const char* s);
Writes s (excluding terminating NUL) and a newline to stdout. Returns non-negative
on success, EOF on error.
ii. <stdlib.h>
This is a library of general purpose useful functions. You'll note that some of the more complex
functions are quite tricky to call because of their complicated parameter lists.
iii. <string.h>
String handling functions. For now you'll have to accept that the expression “char*” means a
string, as represented by an array characters. The * operator is used to indicate that the whole string
is not returned, but rather that the function's return value tells the caller where to find the returned
value. Some of the libraries functions look redundant, but generally there will be differences in
implementation which make it possible for a knowledgeable programmer to optimize his/her code.
iv. <math.h>
Mathematical library. Angles are generally in radians.
The simplest of these functions is the “getch()” function. This function gets one character from
the keyboard and returns it. There is also a “getche()” function which gets one character from
the keyboard and echoes (prints) it to the screen. If no key has been pressed then these functions
will cause your program to wait for a key to be pressed.
Here's an example:
#include <stdlib.h>
#include <stdio.h>
#include <conio.h>
int main()
{
char mystring [3]; //a string is an array of characters
mystring [0]= getch(); //get a character and store it
mystring [1]= getche(); //get a character, store it and print it
mystring [2]=0; //add a null terminator
printf (mystring); //Print out the two keys that were pressed
}
There is a function that simply checks if a key has been pressed. This is the kbhit() function. It
returns 0 (zero) if no key has been pressed and non-zero if a key has been pressed but not yet read
in by the program.
Here is a piece of code that loops around counting until a key is pressed:
#include <stdlib.h>
#include <stdio.h>
#include <conio.h>
int main()
{
int counter=0; //a string is an array of characters
while (!(kbhit()))
{
counter++;
printf ("%i\n",counter);
}
}
If you want to read in a whole word or a number or anything bigger than a single character at a time
you can use the “scanf” function. Scanf reads something in and needs to return it. Because of
the limited flexibility of C's functions (they can only have one return value) scanf uses its
parameters to pass values back to the calling function. Remember from before that if we pass an
array to a function then the original array is modified when the function modifies its variables. As a
result of this when we call scanf to read in a string we pass the string in as a parameter. We also
need to tell scanf what kind of input to expect. We do this with formatting codes, similar to those
that we used previously with printf. Here is an example:
int main()
{
char mystring [30];
scanf ("%s",mystring); /*read the string into mystring. This echoes to the
screen as well. */
printf (mystring); /*display the input that we just read from the
keyboard.*/
}
We can use other formatting strings to read in different types of input. To read in floating point
numbers we can use %d and to read in integers we can use “%i”.
#include <stdlib.h>
#include <stdio.h>
int main()
{
int myint;
float myfloat;
scanf ("%i",&myint); //read in an integer
printf ("%i",myint);
scanf ("%f",&myfloat); //read in a floating point number
printf ("%f",myfloat);
}
Notice the ampersand before the parameters that we send into the scanf function in the program
above. They are there to specify that when the function is called a copy must NOT be made of those
parameters, rather the original parameter must be changed. This is the mechanism used by scanf
to send data back to the caller.
Notice that if you expect the user to type in a number and they input letters then your program will
do strange things. A more secure way of inputting numbers is to input them as strings and then
convert them to numbers:
#include <stdlib.h>
#include <stdio.h>
int main()
{
char mystring [30];
int myint;
float myfloat;
#include <stdlib.h>
#include <stdio.h>
int main()
{
char mystring [30];
gets (mystring); //read in a string
printf (mystring);
}
We have already seen the printf function that is used to print things on the screen. Printf is
fairly flexible in that it allows us to print many different types of things. The type of thing that is to
be printed depends on the format code. Some allowable format codes are:
%c character
%d signed integers
%i signed integers
%e scientific notation, with a lowercase "e"
%E scientific notation, with a uppercase "E"
%f floating point
%g use %e or %f, whichever is shorter
%G use %E or %f, whichever is shorter
%o octal
%s a string of characters
%u unsigned integer
%x unsigned hexadecimal, with lowercase letters
%X unsigned hexadecimal, with uppercase letters
%% a '%' sign
There are also some modifiers which allow the above meanings to be altered slightly, for example
to restrict the length of floating point numbers.
There is a simpler function which can be used to print strings only. This is the puts() function. The
parameter is the string to be printed. An example program:
#include <stdlib.h>
#include <stdio.h>
int main()
{
char mystring [30];
gets (mystring); //read in a string
puts (mystring); //print the string
}
There are several special purpose characters with reserved meanings. These are listed over here.
These characters all represent characters in the ASCII table and therefore can be stored in a char
type variable.
#include <stdlib.h>
#include <stdio.h>
int main()
{
char mystring [30];
puts ("\a");
}
Notice that although the \a is a single character we have still used double quotation marks around it
because the puts function expects a string input.
4.1.17 Pointers
This is the section which ties many concepts and as yet unexplained things together.
We are used to the concept of variables. These are locations in which we store data. We understand
that each of the variables in our program occupy some space in the memory of our computer.
Finally we understand that each location in our computer's memory has a unique number, called an
address, associated with it.
We have reserved a space which is large enough to store an integer and given that space the name
“myint”.
Sometimes it is useful to be able to refer to a variable indirectly. We do this by creating a new type
which holds the address of a variable. This is called a pointer. The C to do this is as follows:
int *mypointer;
The mypointer variable has been declared as holding the address of an integer.
The * operator is a unary operator (it only operates on one item) which associates itself with the
thing to the right of it. Thus in the following example pointer is a pointer and integer is a normal
variable:
On a Pentium based PC all pointers are 32bits long regardless of what they are pointing to. This
means that a pointer to an integer occupies the same amount of space as a pointer to a char which
occupies the same amount of space as a pointer to a floating point number. Despite this C treats
pointers to different variable types as different types of pointer. This is because when we write into
the location “pointed to” by the pointer the number of storage locations affected will depend on the
type of the pointer.
If we have a variable and we want to know where it is stored we can use the unary & operator. This
operator is used as follows:
int myint;
int *intpointer;
intpointer=&myint;
In this example we have declared an integer and something that points to an integer. We have then
told the pointer to point to that integer by assigning the pointer (which holds an address) to the
address of the integer (provided by the & operator).
If we wish to refer to the “thing pointed to by a pointer” then we can access that by prefacing the
name of the pointer with an asterisk. A full example is given below. In this example we set up an
integer and a pointer to an integer. After that we make the pointer point to the integer. We assign a
number to the integer and we print out the contents of the item being pointed to by the pointer, i.e.
the original integer.
#include <stdio.h>
int main()
{
int myint;
int *intpointer;
intpointer =& myint;
myint = 3456;
printf ("%i",*intpointer); //prints 3456;
}
When we first create the pointer it does not point to anything. If you store something in the
referenced location you have no control over where it is stored, therefore the operation is
dangerous. It may result in unpredictable behaviour.
We can create a pointer which does not point to anything. These are called NULL pointers.
#include <stdio.h>
void swap (int arg1, int arg2);
int main()
{
int num1,num2;
num1 = 3;
num2 = 5;
swap (num1,num2);
printf ("%i\n",num1);
printf ("%i\n",num2);
}
The swap routine takes in two parameters and swaps them around. Unfortunately the parameters
that are swapped are only copies of the original parameters and so the function is useless. The trick
to fixing the problem lies in ensuring that the original parameters, not copies, are changed. If we
send the address of the data to be swapped then we can have the function swapping the originals,
not the copies. We can do this by passing a “reference” into the function. This reference is basically
the address of the data which the function must work on. Because we are passing the address of the
original data we achieve our aim.
There is some trickery involved. The compiler knows that we are passing references into the
function and automatically de-references them, so that when we refer to arg1 and arg2 in the
above example the compiler understands that we are working with “the thing referenced by the
address that was passed in”.
This leads to two ways of passing parameters. Before this all parameters were passed “by value”, in
other words we sent in the actual value for the function to work on. In the latest example we passed
our parameters “by reference”, we told the function where to find its parameters.
We can do the above example in a slightly more complicated way as shown here:
int main()
{
int num1,num2;
num1 = 3;
num2 = 5;
swap (&num1,&num2);
printf ("%i\n",num1);
printf ("%i\n",num2);
}
In this example we pass pointers into the function. Inside the function we dereference the
parameters manually by referring to their contents with the * operator. When we pass the
parameters into the function, inside main, we need to pass pointers to the integers into the function
so that the type of our parameters matches with the declaration. We do this conversion by using the
& operator which gets the address (a pointer) of the variables.
We can now revisit a line which you saw a few pages back:
You should now understand that the function must modify its parameter (that is the whole point of
calling this function) and in order to do this it needs to have its parameter passed by reference.
Pointers and arrays have a very close relationship. The name of an array is actually implemented as
a pointer to the first element of that array. Because of this I can do the following:
#include <stdio.h>
#include <string.h>
int main()
{
char myarray [26];
char *mypointer;
mypointer=myarray; /*make mypointer point to the first
element of myarray*/
strcpy (myarray,"Hello World"); //put something into myarray
printf (mypointer); /*show that printing mypointer
accesses the contents of myarray*/
}
There are two things to consider over here. Firstly there is a pointer, which is the starting address of
the array, and secondly it is crucial to note that when we declared the array a storage space was set
aside to actually place the contents. We must have both of these things.
myarray=”Hello World”
does not make sense. We cannot assign a string to a pointer because they are different types.
In addition we cannot store data into a pointer unless we have assigned space for it to reside.
If we wish to pass an array into a function by reference then we can do this by passing in a pointer,
as seen above, which explains the declaration of strcpy:
When strcpy returns an answer it passes a pointer back which tells the caller where to find the
answer.
Pointers are actually variables, and so we can do arithmetic on them. This leads to the following
implementation of strlen:
Over here we increment the pointer BEFORE we apply the * operator to it. Incrementing the
pointer has the effect of moving to the next element in the array. The C compiler knows how big
each array element is and moves that many bytes every time the pointer is incremented.
We can create a pointer to a C function. A pointer to a function points to the address of the first byte
of code in that function. When we refer to the contents of that function we are referring to the code
that runs when the function is called. Function pointers are complicated slightly by the fact that
functions have parameters and return types. The syntax needed is illustrated here. Line numbers
have been added:
1 #include <stdio.h>
2 int quadruple (int input);
3 int main()
4 {
5 int (*myfuncptr)(int);
6 int temp;
7 myfuncptr=quadruple;
8 temp=(*myfuncptr)(20);
9 printf ("%i\n",temp);
10 }
Line 2 declares a function. As you can see the function takes in an integer parameter and returns an
integer. Line 5 creates a pointer to a function. The pointer is given the integer type because the
function that it will point to will return an integer. This constraint is important. We distinguish the
function pointer from a normal integer pointer by putting the parameter type list in brackets after the
name of the function. The bracketing on this line is important because of the priority of the
operators. If we omit the brackets then the asterisk associates incorrectly and we end up with the
wrong type for our pointer. Once we have a pointer to a function we can point it to a function of the
It is possible to do some extremely fancy things with function pointers. We will use them later to
construct interrupt vector tables.
4.1.18 C Structures
We have come across the concept of an array. An array is a group of things of the same type
bundled together. A structure also bundles things together, but they need not be of the same type.
This is useful for dealing with grouped information.
As an example of grouped information suppose that we have an employee about whom we wish to
store basic characteristics. We can create an employee structure, effectively grouping a number of
different types of information about each employee. We can then refer to an employee's details by
using the dot operator. This is illustrated here:
#include <stdio.h>
#include <string.h>
int main()
{
strcpy (emp1.name,"Fred Cat"); /*give the employee a name. Note the dot
operator*/
emp1.age=21; //give the employee an age
emp1.salary=2122.34; //give the employee a salary
emp1.year_of_service=0; //number of years of employment
//now we print out the employee's information
printf
("%s\t%i\t%f\t%i\n",emp1.name,emp1.age,emp1.salary,emp1.year_of_service);
}
Notice how we defined what an employee's record consists of and then we defined a single
employee.
struct employee
{
char name [50];
int age;
float salary;
int year_of_service;
};
int main()
{
strcpy (smme[0].name,"Fred Cat"); //give the employee a name
smme[0].age=21; //give the employee an age
smme[0].salary=2122.34; //give the employee a salary
smme[0].year_of_service=0; //number of years of employment
//now we print out the employee's information
printf
("%s\t%i\t%f\t%i\n",smme[0].name,smme[0].age,smme[0].salary,smme[0].year_o
f_service);
system ("Pause");
}
We access each employee using standard array indexing. Passing a struct into a function will
cause the entire struct to be copied over onto the stack. This can be inefficient. If we want a
function to modify one of the struct's members then we have a problem. We cannot return a
struct as it consists of more than one item. We are forced to pass a pointer into the function so
that we can modify the original item, not a copy. The solution, as before, is to pass a pointer to a
struct into the function. This is done as follows:
#include <stdio.h>
#include <string.h>
struct employee
{
char name [50];
int age;
float salary;
int year_of_service;
};
int main()
{
strcpy (smme[0].name,"Fred Cat"); //give the employee a name
smme[0].age=21; //give the employee an age
smme[0].salary=2122.34; //give the employee a salary
smme[0].year_of_service=0; //number of years of employment
Because of the common and unwieldy de-reference operation in the above function there is a
shortened form available, as shown here:
4.1.19 Unions
Unions are similar to structs but the members of a union occupy the same memory space. This can
be useful for splitting bigger variables into smaller variables. As an example suppose we have an
integer, 32 bits, and we want to split it into 4 chars. We could convert it using bit shift and mask
operations, but this is quite wasteful. We could declare a union of an integer and four chars (maybe
an array) and then writing into the integer will automatically result in the chars taking on the desired
values:
#include <stdio.h>
#include <string.h>
union convert
{
int integer;
char chars[4];
};
union convert my_int;
int main()
{
my_int.integer=0x12345678;
printf ("%x\n",my_int.chars[3]); //display 12
printf ("%x\n",my_int.chars[2]); //display 34
printf ("%x\n",my_int.chars[1]); //display 56
printf ("%x\n",my_int.chars[0]); //display 78
}
The array indexing for the character array appears to be in reverse order. This is because of the
order in which the computer stores multi-byte data items. On some processor platforms, such as
some microcontrollers, this will not happen.
The fopen function returns a file pointer. The filename is the name of the file to access
#include <stdio.h>
int main()
{
fp=fopen ("myfile.txt","w"); /*open the file called myfile.txt
for writing, destroy previous contents*/
fprintf (fp,"This is a text file"); //put something into the file
fclose (fp); //close the file
}
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
FILE* fp;
char mystring [100];
int main()
{fp=fopen ("myfile.txt","r"); //open the file and assign file pointer. File is
read only
fscanf (fp,"%s",mystring); //get a string from the file
printf ("%s\n",mystring); //put the string on the screen – CONSOLE IO
fclose (fp); //close the file
}
There are many other ways of accessing files, which are not dealt with here.
num1=22;
In this case the program is forced to convert numbers from one type to another. This involves
changing their type and representation. In this case num1 will be converted to variable of the same
type as num2, the multiplication will be performed and the result will be converted to the same type
as num3.
There are a few nasty catches. Suppose I want to take two unsigned chars and concatenate them to
form an integer:
int main()
{
unsigned char num1,num2;
unsigned int num3;
num1 = 128;
num2 = 128;
num3 = (num1<<8)+num2;
printf ("%i",num2);
}
This will sometimes give an incorrect answer. The char num1 is shifted 8 bits to the left, while
still remaining an 8 bit variable, resulting in a nonsense value. This value is added to num2, and
only after that is the result converted to an integer.
int main()
{
unsigned char num1,num2;
unsigned int num3;
num1 = 128;
num2 = 128;
num3 = num1;
num3 = (num3<<8)+num2;
printf ("%i",num3);
}
Generally the compiler will cast values from a smaller representation to a bigger one.