Lecture Notes On C Programming: DR Virginie F. Ruiz
Lecture Notes On C Programming: DR Virginie F. Ruiz
Lecture Notes On C Programming: DR Virginie F. Ruiz
Dr Virginie F. Ruiz
Department of Cybernetics room 184, E-mail: [email protected]
VFR October 03
SE2B2 Further Computer Systems CALL BY REFERENCE .................................................................................................................................. 16 ARRAYS AS ARGUMENTS ............................................................................................................................ 16 FURTHER TYPES................................................................................................................................... 17 CASTING ...................................................................................................................................................... 17 ENUMERATED TYPES .................................................................................................................................. 17 DEFINITION ................................................................................................................................................. 18 STRUCTURES ............................................................................................................................................... 18 POINTERS TO STRUCTURES ......................................................................................................................... 18 A FUNCTION RETURNING A STRUCTURE ..................................................................................................... 19 UNION ......................................................................................................................................................... 19 LINKED LISTS .............................................................................................................................................. 20 BITFIELDS.................................................................................................................................................... 20 THE PRE-PROCESSOR......................................................................................................................... 21 PREDEFINED SYMBOLS ............................................................................................................................... 21 MACRO SUBSTITUTION ............................................................................................................................... 21 MACROS ...................................................................................................................................................... 22 CONDITIONAL OPERATOR ........................................................................................................................... 23 COMMA OPERATOR..................................................................................................................................... 23 MACROS VS FUNCTIONS ............................................................................................................................. 23 CONDITIONAL COMPILATION ..................................................................................................................... 24 CONDITIONAL DEFINITIONS ........................................................................................................................ 24 PRAGMA ...................................................................................................................................................... 24
AN INTRODUCTION TO C FOR DELPHI PROGRAMMERS .........................................................2 INTRODUCTION ..............................................................................................................................................2 ASSESSED WORK ...........................................................................................................................................2 TOPICS ...........................................................................................................................................................2 ACKNOWLEDGEMENTS ..................................................................................................................................2 C AND C++ FOR DELPHI PROGRAMMERS - AN INTRODUCTION ...........................................2 A SIMPLE PROGRAM .............................................................................................................................3 COMMENTS ....................................................................................................................................................3 DELIMITERS ...................................................................................................................................................3 MAIN ..............................................................................................................................................................3 LIBRARIES ......................................................................................................................................................3 DEFINITIONS ..................................................................................................................................................4 ASSIGNMENT .................................................................................................................................................4 SEMICOLONS ..................................................................................................................................................4 CASE SENSITIVE ............................................................................................................................................4 TYPES AND OPERATORS ......................................................................................................................5 TYPES.............................................................................................................................................................5 ASSIGNMENT OPERATORS .............................................................................................................................5 ARITHMETIC OPERATORS ..............................................................................................................................6 LOGICAL AND EQUALITY OPERATORS ..........................................................................................................6 UNARY OPERATORS ......................................................................................................................................6 Unary Minus.............................................................................................................................................6 Increment and Decrement........................................................................................................................6 Unary Pointer Operators .........................................................................................................................6 STRUCTURE OPERATORS ...............................................................................................................................7 PRECEDENCE OF OPERATORS ........................................................................................................................7 WHILE STATEMENT .......................................................................................................................................7 DOWHILE STATEMENT ...............................................................................................................................8 FOR STATEMENT ............................................................................................................................................8 IF STATEMENT ................................................................................................................................................8 GOTO ..............................................................................................................................................................9 CONTINUE AND BREAK ..................................................................................................................................9 continue ....................................................................................................................................................9 break.........................................................................................................................................................9 SWITCH STATEMENT ......................................................................................................................................9 ARRAYS, POINTERS AND STRINGS .................................................................................................10 ONE DIMENSIONAL ARRAYS .......................................................................................................................10 READING INTO AN ARRAY ...........................................................................................................................10 GENERATING A POINTER TO AN ARRAY .....................................................................................................11 STRINGS .......................................................................................................................................................11 MULTIDIMENSIONAL ARRAYS.....................................................................................................................11 POINTERS .....................................................................................................................................................12 Pointers and one dimensional arrays ....................................................................................................12 Pointers and multidimensional arrays...................................................................................................12 DYNAMIC MEMORY ALLOCATION ...............................................................................................................13 FUNCTIONS .............................................................................................................................................13 SYNTAX OF A FUNCTION .............................................................................................................................13 EARLY VERSIONS OF C................................................................................................................................14 VOID ............................................................................................................................................................14 SCOPE ..........................................................................................................................................................15 HEADER FILES .............................................................................................................................................15 Notes on C programming
VFR October 03
Assessed Work
There will be an assignment. The source listing, testing and written report will need to be handed in, early in term.
Topics
The following topics will be covered during the course of lectures: Overview of a program Input and output Types and operators Selection Iteration Functions Arrays Strings Pointers Files Structures
Programming is best learnt by writing programs, so it is suggested that you try to create simple programs that exercise the constructs that are discussed.
Acknowledgements
Thanks to Shirley Williams who kindly gave me some her material to help me with this course.
Notes on C programming
VFR October 03
A SIMPLE PROGRAM
Below is a simple Delphi 1 console program that handles numbers:
program PDelf1; { A Delphi program illustrating comments, simple IO, constants, libraries } uses WinCrt; const MyChoice=7; var MyNumber, YourNumber: Integer; begin WriteLn('Hello'); MyNumber:=MyChoice; WriteLn(MyNumber,' is my lucky number'); Write ('What is yours? '); Read(YourNumber); Write(YourNumber,' is your lucky number'); end.
In Delphi curly brackets { and } are used to delimit comments. While in C /* is used to mark the start of a comment and it terminates with */. Neither language supports nesting of comments. Later versions of Delphi allowed single line comments that begin with // and end with the end of line. The same single line comments are allowed in C++ but are not a feature of C.
Delimiters
Pascal programs must begin with the key word program and end with a full stop. There is no direct equivalent in C, however, all programs must have a function main. In Pascal compound statements, as well as, bodies of procedures are delimited using begin and end; the equivalent in C is that an open curly brace { marks the start and is matched at the end by a }. Strings in Pascal are delimited by single quotes. While in C they are marked by double quotes.
Main
All C programs consist of one or more functions. There must always be a function called main. The name main is not a reserved word, but it can cause confusion for the compiler if it is declared. If compiling with full type checking with a compiler such as gcc, there will be errors unless the programmer specifically indicates that main takes no parameters and does not return anything. This is done using void as in the example. Programmers and books dont always do this and the line:
void main(void)
could be replaced by
main( )
Libraries
In Delphi programs, libraries are included by listing them in a uses statement. A similar effect is obtained in C by the use of #include. The Delphi example includes the line:
uses WinCrt;
These two programs will be used to highlight the similarities and differences between C and Pascal.
Comments
Notes on C programming
VFR October 03
which tells the environment to generate a console window. Later versions of Delphi do not use the library, instead they have a compiler directive:
{$AppType Console}
that brings up the console window. The C version does not need to specify the use of a console, as the default with C programs is that they run in a console-like window. The WriteLn procedure is in the Delphi library unit System. Delphi programmers do not need to include System in a uses statement as it is always available. Other libraries such as SysUtils and Forms do need to be specifically included and are often seen in Delphi units:
uses SysUtils, Forms;
Pascal dictates that declarations occur outside the block. So the definitions of both variables and constants occur before the begin of a program or procedure block. With C, the declaration of variables and constants take place inside the block. The whole program can be considered to be a block, as well as individual conditionals and loops. The format is also different. The line:
#define MYCHOICE 7
is not a definition of a constant, it is in fact a macro definition. At compile time, all occurrences of MYCHOICE will be replaced by the number 7. A constant definition equivalent in C would be:
const int MyChoice = 7;
The C routines used in the example for reading and writing are scanf and printf, these are from the stdio library which is included in the program by the statement:
#include <stdio.h>
Assignment
In Pascal, the assignment statement uses the := operator. In C assignment is an expression that returns a value. So the line:
MyNumber = MYCHOICE;
The call to printf is more complicated than the Pascal Write. Consider the line:
printf("%5d is my lucky number\n",MyNumber);
The %5d is a format specifier: it dictates that the value of the first parameter after this string should be printed using up to 5 digits as an integer number. Leading blanks will be printed if the number is smaller. The d indicates decimal. The number is printed in the string at the position where the format specifier is. The back slash is used with another character to represent a control character. The combination \n represents the newline character. So the string will be displayed followed by a newline. The line:
scanf("%d",&YourNumber);
copies the value of MYCHOICE into MyNumber and returns true to indicate that the assignment has successfully occurred. A common mistake made by Pascal programmers when first using C is to use the assignment operator = when they mean to use the equivalence operator ==, this is syntactically correct but rarely does what is required - so take care.
Semicolons
The use of semicolons looks the same in both languages. However, Pascal uses a semicolon to terminate declarations and to separate statements. Whereas C uses semicolons to mark the end of an expression, so that, the expression becomes a statement.
cause a value to be read. The format specifier %d indicates an integer is to be read. The & indicates that this is a reference to the variable YourNumber, indicating the location where the value read must be stored. The & operator is necessary because C does not have variable parameters, and this can be overcome by passing a reference to a variable rather than the variable itself, this issue will be addressed in more detail later on.
Case Sensitive
Pascal is not case sensitive. A variable such as MYNUMBER can be written as MyNumber, mynumber or in any mixture of upper and lower case. C is case sensitive. The variable MyNumber is not the same as MYNUMBER. When the compiler throws up a warning that a variable is not declared it is always
4
Definitions
Notes on C programming
VFR October 03
wise to check that there is not a mismatch between the case used in the declaration and that used in an expression.
A declaration associates a group of identifiers with a specific data type. All variables must be declared before they can be used. Unlike Pascal, there is no special place in which variables need to be declared. Consider this declaration:
int MyNumber=7,YourNumber;
Two variables are declared, both of type int. MyNumber is initialised to 7. YourNumber is not initialised and so its value is undefined and could contain anything. It should not be used before a value is assigned to it. Other examples of declarations are:
int Number; char Letter1, Letter2='b'; float e,E,f,F; /*permitted but difficult to understand*/ double density_1; double density_2;
Types
The memory requirements of each type will vary on different computers, and indeed can vary with different compilers on the same processor. Table 1 indicates the names of some of the most commonly used data types and their possible range of values and size in bytes on a 32-bit machine. The use of the standard headers <limits.h> and <float.h> can help in fixing sizes when trying to write portable programs. Data Type char unsigned char int unsigned int short int long float double Table 1 The meaning of these types will vary between compilers, for example on some machines int and short int will represent the same range. Programmers should be aware that porting a program between compilers may result in the introduction of overflow errors. Size in Bytes 1 1 4 4 2 4 4 8 possible range of values -128 to 127 0 to 255 -2147483648 to 2147483647 0 to 4294967295 -32768 to 32767 same as int 3.4 *10-38 to 1.7* 1038 1.7*10-308 to 3.4 ^ 10308
The maximum allowable length of variable names varies from compiler to compiler. The general rule is to use names that are meaningful within the context of the program. It is wise to avoid using the same name in different case of letters, unless the meaning is very clear.
Assignment Operators
Pascal has a single assignment operator := which is equivalent to the = operator in C. The C language offers a number of compound operators, including assignment operators that combine other operations, as summarised in Table 2. Operator = += -= *= /= &= |= Table 2 More details of the other operations will be given in the following sections.
5
Description Assignment Sum and assign Subtract and assign Multiply and assign Divide and assign bitwise AND and assign bitwise OR and assign
Example Number = 6; Number += 10; Number -= 5; density_1 *= Number; density_1/=4; Letter1&=Letter2 Letter1|=Letter2
adds 10 to Number subtract 5 from Number same as density_1= density_1*Number; divides density_1 by 4 ands the bits of Letter1 with Letter2 ors the bits of Letter1 with Letter2
Notes on C programming
VFR October 03
When a compound operation is used, there must not be any space between the operators.
Arithmetic Operators
There are five arithmetic operators in C. Operator + * / % Description add subtract multiply division remainder after integer division Example 2 + 3 Number - 1 density_1*2.5 density_1/density_2 Number % 2 same as Pascal same as Pascal same as Pascal same as Pascal same as mod in Pascal
Less than Greater than Less than or equal to Greater than or equal to
Unary Operators
A unary operator acts upon a single operand to produce a new value.
Unary Minus
The most well known unary operator is minus, where a minus sign precedes a constant, variable or expression. In Pascal, constants can be negative. In C, all numeric constants are positive. Therefore, in C, a negative number is actually a positive constant preceded by a unary minus, for example:
-3
The weak typing of C supports the mixing of operands of differing types. Operands may undergo type conversion before an expression takes on its final value. The general rule is that the final result will be in the highest precision possible given the data types of the operands. When an arithmetic expression is assigned to a variable, there are two expressions an arithmetic expression and an assignment. Consider:
X = Y + Z;
The logical not operator is also frequently used. It is written as an explanation mark ! and is the same as NOT in Pascal. C also offers a bitwise not operator, exchanging 0s and 1s in the binary representation. This operator is represented by the tilde symbol ~.
First Y and Z are added, the result will be in the same precision as the highest precision of Y and Z. This result will be then cast (converted) into the precision of X and the assignment will occur. The assignment will return a result of 1 to indicate success, but this is rarely used. The expression becomes a statement because it is terminated with a semicolon.
These operators can also be used within expressions, most commonly they are seen in the control of loops. If the increment operator appears first it means increment the value and then use it in the expression. If the operand appears first it means, use the current value in the expression and then increment the value.
There are six equality operators, all working in the same way as the Pascal equivalent. C Operator == != Description Equal to Not equal to Pascal equivalent = <>
Notes on C programming
VFR October 03
C Operator & *
The C equivalent needs brackets to control what the indirection operator is applied to:
(*CThisItem).Next
The way in which they are written is subtly different. Consider declaring a pointer: in C a pointer to an integer is declared like this:
int *CIndex;
There must not be a space between the minus and the greater than symbols.
Precedence of Operators
The precedence of C operators dictates the order of calculation within an expression. The precedence of the operators introduced here is summarised in the table below. The highest precedence operators are given first.
Assigning a previously declared integer to the address these points and then setting both to the value 5 is done in similar manners. in C a pointer to an integer is used like this:
CIndex=&CInteger; *CIndex=5;
in Pascal:
PIndex:=@PInteger; PIndex^:=5;
Note in statements in C the indirection operator goes in front, whereas in Pascal it goes behind.
Structure Operators
C has a heterogeneous data structure like Pascal records. In C, these are called structures or struct as will be explained later. The dot operator is used in both C and Pascal to indicate a member. For example given a structure variable Employee with a field Salary both languages refer to this field as:
Employee.Salary
*=
/=
%=
+=
-=
Associativity left to right right to left left to right left to right left to right left to right left to right left to right left to right right to left right to left
Where the same operator appears twice (for example *) the first one is the unary version. At the heart of most programs are pieces of code that are executed many times, with some choices between the paths through the code. C offers counted and conditional loops. Selection is offered by an if statement on a multi-way switch.
Data structures such as linked lists mix records and pointers. In Pascal a list is traversed using expressions such as:
PThisItem^.Next
While Statement
The syntax of a while statement is:
while (expression) statement;
7
Notes on C programming
VFR October 03
The general format is: The expression is usually a relational expression. The statement is either a compound statement enclosed in curly brackets or single statement. For example:
n = 1; while (n <= 10) { printf("%3d", n); n++; } for (initial; test; adjust ) statement;
Where initial is a statement that initialises values prior to the execution of the loop. The expression test must hold true for an iteration of the loop to start. The adjust statement is executed for each iteration, after the iteration has completed.
for (n=1; n<=10; n++) { printf("%3d", n); }
this will print out the numbers 1 to 10. The positioning of the curly brackets and the indentation are according to the programmers preference. It is wise to keep with the same sort of lay out that you would have used in Pascal.
This will print out the numbers 1 to 10. The curly brackets are not necessary as there is only a single statement. It is not always necessary to have all parts of the for statement, parts can be left empty. The following code has the same functionality as the above.
n=0; for (; ++n<=10; ) { printf("%3d", n); }
dowhile statement
The syntax of a do...while statement is:
do statement while (expression);
it is similar to the Pascal repeat, in that the statement or compound statement is always executed at least once.
do { printf("Enter a number that is not 7"); scanf("%d",&n); } while (n==7);
In this case, the initialisation is outside the loop. The incrementing of n occurs as part of the final test. As the operator is first the incrementing happens before the comparison. If the test were replaced by:
n++<=10
This will loop until the user types something other than a 7. If the final expression is replaced by:
(n=7)
if statement
The general format of an if statement in C is:
if (test) statement;
Regardless what the user types n will be set to 7. The expression returns true each time it executes and so the program will loop infinitely.
for statement
The C for statement offers much more than the equivalent counted loop in Pascal.
Notes on C programming
VFR October 03
else statement;
Note that the semicolon is needed before the else. Below is an example of an if statement. It executes in the same manner as the Pascal equivalent.
if (number > 0) printf("Number positive\n"); else { printf("Number negative \n"); printf("or zero\n"); }
continue
The continue is used inside a loop, it causes the remainder of the current iteration to be skipped and the next iteration to be started.
break
The break is used inside a loop or conditional statement. It causes the remainder of the statement to be skipped and execution to proceed to the next statement. With a loop, this means that no further iterations are executed.
switch statement
The C switch statement is similar to the Pascal case. The general format is:
switch expression { case value1: statements1; case value2: statements2; /* more cases */ default: defaultstatements; }
Care is needed to match the else clause with the correct if.
goto
Pascal programmers rarely use the goto statement and its use in object oriented languages, including C++, is considered unnecessary. Some C programs do use the instruction and so it is described here. The syntax of a goto statement is:
goto label ;
Semantically this is different to the Pascal case. If the expression is equal to value1, then statements1 is executed, followed by statements2, then all other statements including defaultstatements. If the expression is equal to value2, then statements2 is executed, followed by all other statements including defaultstatements. To get the same effect as Pascal it is necessary to put a break after every set of statements, to prevent execution of all subsequent statements in the switch. The following fragment of code illustrates the use of break.
/* assume char op; */ scanf("%c", &op); switch(op) { case '+' : printf("%d", case '-' : printf("%d", case '*' : printf("%d", case '/' : printf("%d", default: break; }
x x x x
+ * /
Notes on C programming
VFR October 03
The following program fragment reads from the standard input (that is the keyboard) into an array - it stops at a sentinel. There is plenty of opportunity for this program to give rise to errors - so it should incorporate error checking on the size of the array before it is used.
/* reads numbers from standard input that is the keyboard - into array until sentinel is encountered NB overflow check needed*/ int i,Temp; int square[5]; fscanf(stdin,"%d", &Temp); for (i=0; Temp != SENT; i++) { square[i] = Temp; fscanf(stdin,"%d", &Temp); }
Alternatively the numbers can be read from a file and written to another:
FILE *fin, *fout; if (((fin =fopen("Numbers.txt","r")) == NULL) || ((fout=fopen("Copy.txt","w")) == NULL)) { fprintf(stdout,"Can't open file\n"); } else { fscanf(fin,"%d", &Temp); for (i=0; Temp != SENT; i++) { fprintf(fout,"%d ",Temp); fscanf(fin,"%d", &Temp); } } fclose(fin); fclose(fout);
Line 1 defines an array with 5 elements, each element is an integer. The first element of an array has the index 0. So this declaration gives 5 elements:
square[0] square[1] square[2] square[3] square[4]
The last element has an index that is one less than the size of the array. Line 2 defines a control variable for the loop that starts at line 3 and iterates round the statement at line 4. Line 4 in turn assigns to each element the square of its position (that is one more than its index). An array can be initialised as it is declared, as in this example:
int squared[5] = {0,1,4,9,16};
The variable fin is a pointer to a file, hence the declaration. All files can be opened using fopen. The second parameter indicates how it is to be used, "r" indicates reading, "w" indicates writing and if the file already exists, it will be overwritten. It is also possible to use "a" to indicate append. On successful
10
Notes on C programming
VFR October 03
completion, fopen returns a pointer to the newly opened stream. In the event of error, it returns NULL. It is wise always to check this before attempting to use the file. The programmer should always close files that could be open, when they are finished with.
In C strings are delimited by double quotes, while individual characters use single quotes. There is a standard C library <string.h> that contains a number of useful string operations. For example the string compare function, can be considered to have the prototype:
int strcmp(s1, s2);
This returns a negative number if s1<s2, 0 if they are equal and a positive number if s1>s2. The programmer should be careful in using this function in conditional statements such as:
Following this assignment, the following statements all read values into the first element of square:
scanf("%d",PtrToSquare); scanf("%d",&square[0]); scanf("%d",square);
Strings
Arrays of characters are used to hold strings. Consider the declaration:
char AStr[11];
If s1 and s2 are equal, strcmp returns 0, which will be interpreted as false and somethingelse will be executed. Other frequently used string functions are described in the table below:
strcat(s1,s2 ) int strcmpi(s1,s2) strcpy(s1,s2) strstr(s1,s2) int strlen(s1)
this can hold a 10-character string, the eleventh character (AStr[10]) holds null (the character written as: \0). Strings can be initialised when they are declared:
char BStr[11] = "Hello";
concatenates the second string to the first compares two strings, without case sensitivity copy a string to the target string scan a string for the first occurrence of a substring returns the length of a string
It is unnecessary for the programmer to enter the null character, as C adds it automatically, in this example as character BStr[5]. The string can be subsequently changed, it can hold a maximum of 10 characters. When a string is initialised and its value is unlikely to change, the programmer need not explicitly calculate the number of characters, as in the following example:
char CStr[ ] = "Hello this is an example of a string.";
Notes on C programming
The prototypes are not strictly correct, but give an indication of the parameters.
Multidimensional Arrays
A two dimensional array for storing the hours of sunshine can be declared as:
int suns [12][31];
11
VFR October 03
To record 6 hours of sunshine for the 1st April, the following assignment could be done:
suns [3][0] = 6;
is a pointer to a location that can contain a real number. NULL is equivalent to nil in Pascal. In C, it is possible to have a pointer to a general type, for example:
void * genptr;
The right most subscript varies most rapidly. This is important to remember when initialising arrays. It will also be significant if pointers are used to access elements of an array. Consider:
int values[3][4]={1,2,3,4,5,6,7,8, 9,10,11,12};
Then values[0][0] contains 1, values[0][1] contains 2 and values[1][0] contains 5. It is possible to initialise parts of each row:
int values[3][4] ={ {1,2}, {5,6}, {9,10} };
Which will initialise columns 0 and 1. The other elements will not be initialised, and may contain anything - so this is not often advisable. When a number of arrays are to be declared using the same dimensions, it is good practice to use #define to set the bound. For example:
#define XDIM 10 #define YDIM 20 #define ZDIM 30
Line 1 declares an array and line 2 declares a pointer to the first element of the array. With a single dimension array, it is possible to put just:
int *ptr = square;
Then if the program needs to cope with larger arrays it is only necessary to change the definitions (and probably allow a longer run time and plenty of memory).
However, this doesn't work with multidimensional arrays. Line 4 loops round setting each element of the array. Line 6 uses the pointer as a means of accessing the array. Note the indirection operator * is not used. The loop on lines, 8 and 9, is more subtle. Note at line 8 there are two increments (separated by commas). On the first iteration, ptr is pointing to the first element in the array, when it is incremented for the next iteration it will point to the second element of the array. When the loop is completed ptr will be pointing beyond the end of the array and if used would access garbage. The value ptr was pointing to could be seen by using a printf statement:
printf("%d",*ptr);
Pointers
Notes on C programming
VFR October 03
respects: ensuring that the array is accessed matching the appropriate subscripts de-referencing correctly to get to the initial point. Consider the following code: 1: 2: 3: 4: 5:
int values[3][4]; int *ptr; ptr=&values[0][0]; for (i=0;i<3*4;i++,ptr++) *ptr=i;
*numbers=i; /* display numbers */ for (i=n;i>0;i--) printf("Numbers are %3d\n", *(numbers-i)); /* free memory */ free(numbers-n); }
This uses ptr to set the elements of values. When the loop is complete, ptr will point beyond the end of the loop. Printing out the contents of values will verify the elements are correctly set:
for (i=0;i<3;i++) { for(j=0;j<4;j++) printf("%3d",values[i][j]); printf("\n"); }
In this code memory is allocated at line 5, within a conditional, if the allocation fails a suitable error message is displayed. The casting using (int *) in front of malloc ensures that result is treated as though it is a pointer to int, it is not strictly necessary. The for at line 13 increments both i and numbers, so the body of the loop (at line 14) puts successive values of i into successive locations of numbers. The loop at line 16 counts backward. Since numbers will now be pointing just beyond the space allocated, care has to be taken to print out the contents of locations that have been set!. Likewise at line 19 the space allocated originally to numbers is released, the decrement is because numbers is pointing beyond the allocated locations. This needs to be carefully handled as attempts to:
free(numbers)
when numbers is pointing beyond the space that was requested will result in an access violation.
FUNCTIONS
In C, functions are sections of code, separate from the main program that performs a well-defined action. C uses functions where Pascal uses procedures and functions.
Syntax of a Function
The declaration of a C function is in two parts. Firstly, there is a prototype declaration of the function, giving: the function name, return type and parameter types. For example:
int Calc(int Mine,int Yours);
is a function called Calc that returns an integer. The function takes two parameters,
13
Notes on C programming
VFR October 03
both of which are integers, the programmer can include the names of the parameters and it is often helpful to do so. The prototypes are usually placed just before the main function of the program. The implementation of a function comes after the main function. The name and types must match the prototype. The body of a function is enclosed in curly brackets. This may include declarations as well as statements. A return statement in a function can be followed by an optional value, which causes the function to return to where it was called from. Subsequent statements in the function are not executed - this is different to setting Result in Pascal. If there isn't a return, the function returns when it reaches it end.
int Calc(int Mine,int Yours) /* function to calculate Mine plus twice Yours */ { int Temp; Temp = Yours * 2; return Mine+Temp; }
return Mine+Temp; }
If this is run in C++ Builder, it will be necessary to add code to the main to stop the console window closing before the output is seen. The parameters Mine and Yours work in the same way as value parameters in Pascal. However, the return is different. In Pascal setting the result of a function does not cause the function to terminate, the function is executed until its logical last statement, whereas in C executing the return causes the function to terminate.
Early Versions of C
Earlier versions of C declared functions in a slightly different manner that does not work with current compilers. The function header did not include types. This information was provided by a declaration before the curly brackets.
Void
Recall that in Pascal there is no return value associated with a procedure. The type void is used in C to indicate a function that does not return a value, that is, it acts like a Pascal procedure. For example in the following C prototype:
void CommentOnValue(int number);
the function does not return a value. It can be called by the following code:
CommentOnValue(MyNumber);
14
VFR October 03
Many compilers allow programmers to omit voids, however with checking turned on missing a void will usually be flagged as an error. The variable MyNumber is an actual value parameter to the function CommentOnvalue, while number is a formal value parameter. In C, the word argument is sometimes used in preference to parameter. C uses pointers to provide the functionality of Pascal variable parameters and these will be discussed later.
code, this is stored as ASCII text in a file, the source file. The compiler takes the source code, parses it and produces machine code that the computer can execute, the object file. With C Builder, the source code is stored in a file ending in .cpp and object code in a file ending in .obj. In earlier examples the #include statement was used to tell the compiler to include the header for library functions. For example:
#include <stdio.h>
Scope
In C, each function has a discrete block of code, enclosed in curly brackets. It is not possible to use a goto statement from some other part of a program into a function nor is it possible to access variables defined locally within a function from anywhere other than within that function. C does not allow a function to be defined within another function: all functions are at the same level of scope. This is different to Pascal, which does allow nesting. The scope of a declaration is from the end of the declaration until the end of the block containing the declaration. For example, consider the function Calc. 1: 2: 3: 4: 5: 6: 7:
int Calc(int Mine,int Yours) /* function to calculate Mine plus twice Yours */ { int Temp; Temp = Yours * 2; return Mine+Temp; }
means that the header file for the standard input and output is included in with the source code. The file containing the header is in the C Builder directory called Include. The options of the environment ensure the compiler knows to look in this directory for files. Looking at the 500 lines of definitions shows that this contains a number of include statements, which will bring in other files. Programmers can put their own definitions into separate header files and use #include statements to bring the definitions into the source code. With short programs, this is not always necessary but with longer programs, it can be helpful. The same header can be included with different source code files allowing consistent definitions to be maintained in all places. A header file can be created to go with a source file, when the file is saved it should be given a .h extension. With other versions use a text editor - or investigate the help files. A simple example would be to create a file MyHeader.h containing the following two lines:
void CommentOnValue(int number); int dummy;
The variable Temp comes into scope when it is defined on line 4 of the code and goes out of scope with the closing brace. The formal parameters Mine and Yours come into scope on the first line of the function and remain in scope until the end of the function. The function is not enclosed in a block. It comes into scope when its prototype is declared and remains in scope for the whole of the current file, so the scope is file wide. In C, declarations must precede the code of a block. However declarations can appear at the start of any block, for example at the start of the code associated with the else condition of an if statement. This is not always a good idea, take care with such declarations.
Header Files
The statements that make up a program are sometimes referred to as the source
Notes on C programming
//---------------------------------#include <stdio.h> #include "MyHeader.h" void main() { printf ("Enter a number: "); scanf("%d",&dummy); CommentOnValue(dummy);
15
VFR October 03
13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26:
scanf("%d",&dummy); } void CommentOnValue(int number) { if (number > 100) printf("Large\n"); else if (number > 0) printf ("Not large but positive\n"); else if (number == 0) printf("or zero\n"); else printf("Negative \n"); } }
*y=temp;
The & in front of a and b indicates that the address of these variable is passed. Within the function x and y are pointers and the values used, are those they point to. It is possible to do this in Pascal, but programmers normally use variable parameters (which hide the use of pointers). If you find it confusing using pointers inside functions, you could on entry to the function, copy the pointers into local variable. However, you must then remember to copy them back at the end of the function, this may need to be done several times if you have multiple returns. It does make a lot more code
void swap2 (int *rptr, int *sptr) { int temp; int r,s; r=*rptr; s=*sptr;
The first 5 lines above the comment are automatically generated, by C++ Builder. These may not be necessary in other versions of C. The line:
#include <stdio.h>
includes the definitions for the input and output. The line:
#include "MyHeader.h"
includes the definition from the header file just created. The quotes indicates that this file should be found where the source file is, if it is not found there, searching follows the rules set within the environment.
Call by reference
All arguments in C are passed by value. To get the effect of Pascal variable parameters it is necessary to pass a pointer. Within the function the pointer can be de-referenced, that value can be changed, so what the pointer points to changes but the pointer remains unaltered. For example, a function, which swaps the values held in two integer variables, could be written like this:
void swap (int *x, int *y) { int temp; temp=*x; *x=*y;
Notes on C programming
Arrays as Arguments
When an array is passed as an argument, it is in fact the address of the first element that is passed. Therefore, it is possible to change the elements of the array because the argument is the address. It is not necessary to specify the size of an array when declaring it as an argument. Unfortunately, there is no way of
16
VFR October 03
determining the size of the array inside the function, so the information must be explicitly available. This code initialises all elements of an array to 1:
void init_array(int AnArray[],int n) { while (n>0) AnArray[--n]=1; }
Casting
A cast tells the compiler to temporarily treat one data type as though it was another. Given the weak typing of C this can be useful to ensure that variables are correctly treated. For example the following:
(float) 10
The array is iterated through from the back. The loop could be written so element 0 is initialised first. It can be called like this:
int MyArray[XDIM]; init_array(MyArray,XDIM);
casts the number 10 as a floating-point number. The analogy that is best to use when considering the term cast, is that of casting a metal (that is changing the shape). The general format of a cast is:
(type name) expression
where XDIM is previously defined. A useful trick for finding the size of an array is to calculate the size of an element in bytes by using the sizeof function. For example:
sizeof(MyArray[0])
The cast has a high precedence and will be only applied to the first operand of an expression, unless the whole expression is bracketed. Any type name can be used in the cast. If a cast is applied to a longer type then bits will be lost. A cast is equivalent to assigning the expression to a variable of the type and then using that variable in place of the whole construction. The maths library <math.h> contains a routine sqrt. This expects an argument of the type double. In some versions, if it is inadvertently passed a variable of a different type it will return rubbish. To avoid this problem casting can be used. For example to obtain the square root of an integer, the following can be used:
sqrt((double)AInt)
will return the number of bytes for the first element (say 4). While:
sizeof(MyArray)
returns the number of bytes for the whole array. Dividing one by the other gives the number of elements, so the initialising routine can be called as:
init_array(MyArray,sizeof(MyArray)/sizeof(MyArray[0]));
or
sqrt((double)4))
or
sqrt((double)(AInt+4))
Enumerated Types
FURTHER TYPES
C offers the programmer the ability to use standard types and to develop personalised types. Existing types can be cast to be used as though they were different types. Structured types can be constructed; these include the type arrays and heterogeneous record type. Programmers can also create enumerations and
Notes on C programming
An enumerated type is made up of an ordered list of names, which are symbolic constants. For example:
enum Seasons {Spring, Summer, Autumn, Winter};
17
VFR October 03
The names must be distinct even in different enumerations. The values are in fact stored as integers. The values used would be 0 for Spring, 1 for Summer, etc. Different values can be associated with particular enumerations, for example:
enum White_Space {TAB='\t',NEWLINE='\n',SPACE=' '};
will initialise an enumeration representing Roman numerals, as the first value is forced to 1, then all the other values will take successive values without the need to enter them.
Structures
A structure is a collection of related data items, not necessarily all of the same type. It is similar to a record in Pascal. A structure can be defined as follows:
typedef struct int x; int y; } gridtype; {
However most compilers will allow any value to be assigned to a variable of an enumerated type, and would allow a programmer to sensibly assign 1 to This_Year but also to assign the meaningless 67. Some debuggers retains the information on the enumeration constant and will display it. Although the enumeration constants have numeric values, compilers will give warning errors if the programmer attempts to use them in arithmetic expressions. For example:
This_Year++; /*will give a warning*/
The individual members of the structure can be accessed using the dot operator.
a.x=25;a.y=-50;
Where: 1. This_Year is cast as an integer 2. 1 is added 3. The result is then cast back as Seasons. If this operation was to be performed a number of times the programmer would design a function to do the casting.
Pointers to Structures
A pointer to a structure can be declared like this:
gridtype *ptrb;
Definition
As an alternative to enumerations, the programmer can use #define to associate constant values with names. For example:
#define TAB '\' #define NEWLINE '\n' #define SPACE ' '
could replace the enumeration White_Space. Both are acceptable approaches. If it is required to initialise several constants to consecutive values, this can be done more easily with an enumeration. For example:
Note *ptrb.x does not work because the precedence of dot is higher than *, and this is the reason for the existence of the -> operator. This is used a lot with the objects of C++.
18
Notes on C programming
VFR October 03
If we have:
typedef struct { int x; int y; } gridtype; gridtype a, *aptr=&a;
gridtype *makeplace (int xx, int yy) { gridtype *temp; temp =malloc(sizeof(gridtype)); if (temp == NULL) exit (1); /* error */ temp -> x = xx; temp -> y = yy; return temp; }
The programmer will need to free the space allocated to this gridtype when it is no longer needed.
increments the x member. If it was required to increment a ptr, brackets should be used.
Union
A union is somewhat like a variant record in Pascal. It can hold (at different times) things of different types and sizes. The compiler keeping track of size and alignment necessary, the space allocated will be sufficient for the largest member of the union. For example:
typedef union { int ival;/* or */ float fval;/* or */ char *sval; } utype; utype uvar;
Syntactically members are accessed in the same way as members of a record, but the programmer is responsible for keeping track of what is stored in the union. The normal approach is to put the union into a record and add a tag field (this is automatically available in Pascal). In C this is more difficult but can be achieved using code such as this: 1: 2: 3: 4: 5: 6: 7: 8: 9:
enum TSORT{TINTEGER,TFLOAT,TSTRING} ; typedef struct { enum TSORT sort; union { int ival;/* or */ float fval;/* or */ char *sval;
19
This will be more readable to some programmers. To return a pointer to a gridtype will require memory to be allocated, as in the following:
Notes on C programming
VFR October 03
into a single word. C++ uses bitfields to emulate the working of sets (C does not have sets and Delphi/Builder uses them extensively in the VCL). A bitfield is declared in the same way as a structure, except its members are fields of one or more bits. For example: 1: 2: 3: 4: 5: 6: 7: 8: 9:
typedef struct { signed i : unsigned j : signed : signed k : unsigned m : } TBIT; TBIT a,b;
Line 1 declares an enumerated type matching possible values of the union. Care must be exercised in choosing appropriate names that do not conflict with reserved words or the like. Lines 2 to 11 define the record type VARIABLE. Line 4 sets a tag field sort to the enumeration from line 1. Line 5 to 10 is the union (declared using an anonymous type). Line 12 declares a variable of the record type. In the code, the tag field can be set to indicate what member of the union is set, for example:
AVar.sort=TINTEGER; AVar.uvar.ival=2;
2; 5; 4; 1; 4;
When accessing the union the programmer should always check the tag before assuming it has a particular form.
Linked Lists
Linked lists can be set up and traversed in the same manner as Pascal. The following defines and declares a suitable structure:
typedef struct cell { double value; struct cell * next; }cellt; cellt * head, * ptr;
Lines 1 to 8 define a structure that contains 4 used fields (i, j, k and m). All bitfield members must be int, signed or unsigned. It is best practice to explicitly state signed or unsigned integers, as int maybe stored as signed or unsigned depending on the implementation. The declaration at line 3 reserves 2 bits for a field called i. This will stored as a signed integer. The declaration at line 4 reserves 5 bits for a field called j. This is stored as an unsigned integer. The declaration at line 5 is padding, these bits are not used. The most likely reason for this is so that a variable of this type can be used in a bitwise operation that does use these locations. Line 9 declares two variables of the type TBIT. The declaration creates a 16-bit structure, which will fit within a word on most computers. In most compiler, it will be stored like this: 15 X 14 X m 13 X 12 X 11 X k 10 X 9 8 X X unused 7 X 6 X 5 X 4 X j 3 X 2 X 1 X i 0 X
This could be traversed, and values printed out using the following code:
for (ptr = head;ptr != NULL;) { printf("%f\n",ptr -> value); ptr = ptr -> next; }
The integer fields are stored in two's-complement form, with the leftmost bit being the MSB (most significant bit). With signed integers the MSB is interpreted as a sign bit. A bit field of width 2 holding binary 11, therefore, would be interpreted as 3 if unsigned, but as -1 if int. Always check the format relative to the compiler. Fields can be set using the selector operators, for example:
a.i= -1;
Bitfields
When trying to economise on space a programmer may wish to pack several things
Notes on C programming
VFR October 03
THE PRE-PROCESSOR
There are many steps involved in turning a C program into an executable program. The first step is called pre-processing. The pre-processor performs textual manipulation on the source code before it is compiled. There are a number of major parts to this: 1. 2. 3. 4. deleting comments inserting the contents of files mentioned in #include directives defining and substituting symbols from #define directives deciding which code should be compiled depending on conditional compiler directives 5. to act on recognised #pragma statements, which are implementation dependent.
would leave binary 10 = -2 in a.i with no warning. The signed field k of width 1 can hold only the values -1 and 0, because the bit pattern 1 is interpreted as -1. In other machines the coding may be the other way round (right most bit most significant). Bitfields may be aligned to word boundaries or they may flow across them. Some machines limit structures containing bitfields to 16 bits. Thus, there are issues that may effect portability. Bitfields are not arrays and cannot be treated as such. The bitwise operators should be used: Operator & | ^ ~ >> << What it does bitwise AND; compares two bits and generates a 1 result if both bits are 1, otherwise it returns 0. bitwise inclusive OR; compares two bits and generates a 1 result if either or both bits are 1, otherwise it returns 0. bitwise exclusive OR; compares two bits and generates a 1 result if the bits are complementary, otherwise it returns 0. bitwise complement; inverts each bit. ~ is used to create destructors. bitwise shift right; moves the bits to the right, discards the far right bit and if unsigned assigns 0 to the left most bit, otherwise sign extends. bitwise shift left; moves the bits to the left, it discards the far left bit and assigns 0 to the right most bit.
Predefined Symbols
C provides a number of predefined symbols, the values of which are either string literals or decimal constants. These include: Symbol _ _FILE_ _ _ _DATE_ _ _ _STDC_ _ Sample Value " C:\ Unit1.cpp" "May 14 1999" 1 Meaning name of the source file being compiled date the current file was compiled 1 if the compiler conforms to ANSI C, otherwise undefined
The combined assignment and operators &= and |= can also be used. For example: a&=b; will do a bitwise and between a and b putting the result in a. The & operator is context sensitive and so the compiler differentiates between its use as a bitwise operator and the address operator (the same is true for the * operator). Care is needed with addresses as bitfields may not be on byte boundaries, so the expression &a.i is illegal as i is a bit field identifier, because there is no guarantee that a.i lies at a byte address.
A particular environment may supplement the available symbols. Most predefined symbols start with two underscores, there must not be a blank between them, and they often look as though they are a single long line ( __ ).
Macro Substitution
A definition has the form
#define name replacement text
This causes a simple macro substitution, each occurrence of name is replaced by replacement text. For example:
#define XDIM 10
Notes on C programming
21
VFR October 03
The substitution does not have to be limited to constants, for example if the programmer wished to use notuntil instead of the C while, the following would create a correct program
#define notuntil while do { /* this is now correct */
The programmer must realise that the replacement text is used exactly. So:
5* TWO_PLUS_J
i++; /* though not necessarily */ } notuntil (i<2); /* not a very good idea */
It is debatable whether this is good programming practice, as the meaning of the notuntil is the converse of usual until. Where the replacement text spreads over more than one line a forward slash is used to indicate continuation. For example:
#define WHERE_AM_I printf("In file %s at line %d", \ __FILE__,__LINE__)
Macros
A macro allows parameters to be used in substitution text. In C the syntax of a macro definition is:
#define name(parameters) code
This code will replace all occurrences of WHERE_AM_I. Several statements can be combined. For example:
#define WHERE_AM_I printf("In file %s at line %d", \ __FILE__,__LINE__) ; \ printf("\non the %s",__DATE__)
The bracket of the parameter list must be next to the name, otherwise it will be part of the code substituted. For example:
#define SQUARE(x) x*x
Note the \n still gives a newline in the output when used inside the quotes of a print. The single forward slash at the end of the line of a #define is a continuation. For both these examples to work it is necessary that earlier in the code the stdio library has been included. It is possible to use variables in replacement text, for example:
#define TWO_PLUS_J 2+j
Such substitutions should be undertaken with care as there may be more than one j in scope. If the following were to appear in the program:
int j=100; i= TWO_PLUS_J;
Notes on C programming
22
VFR October 03
will be replaced by
10+5*5
*ptr = (i+1)*(i+1);
which incremented both ptr and i. The syntax of the comma operator is that the sub-expressions are evaluated in order and the result is the value of the last one. The comma operator is often used with while loops to incorporate fetching of a value with the test, with the general syntax:
while(get some value, perform calculation on value, and test) { statements}
this is not a problem here, consider what happens with the following:
#define DOUBLE(IT) IT+IT m= 10*DOUBLE(5);
It is unlikely the programmer expected this to evaluate to 55. Use of brackets in the #define or the code will remove this problem. The use of an argument could make the earlier example of UNTIL closer to the Pascal equivalent.
#define UNTIL(x) WHILE(!(x))
The comma operator is different to the comma used to separate the list of arguments.
Macros Vs Functions
Macros are sometimes used to perform simple computations, often using the operators described above. For example, the following macro finds the maximum of two expressions:
#define MAX(a,b) a>b ? a : b
Note the brackets around the argument will ensure the not operator (that is the ! symbol) is applied to the whole of the x argument.
Conditional Operator
The conditional operator can be used to return a value. It has the following syntax:
expr1 ? expr2 : expr3
If expr1 is true then the result of expr2 is returned otherwise, the result of expr3 is returned. This means that the statement:
if (a>0) b=0; else b=1;
This is the code that will be compiled and then executed. If there is an error in the definition, it will be highlighted as an error at the point that MAX is used. This can be disconcerting for the programmer. A function could be written that performed in a similar manner. For example:
int FMax(int a, int b) { return (a>b ? a : b); }
The brackets round the condition are not strictly necessary, but they do aid readability.
Comma Operator
The comma operator was used earlier with:
for(i = 0;i < 5;ptr++, i++)
This is not exactly the same. The arguments have had to be given a type and so
23
Notes on C programming
VFR October 03
different functions would have to be used for different types. The function would be called at run time, this would normally involve more code than the in-line version generated from the macro. With a larger function, the use of a macro would be a distinct disadvantage. As each call would be replaced by the copy of the code, significantly increasing the size of the executable.
version, the economy version or is trying a cover disc sample. A single set of code can exist for all versions. Where there is functionality, that is available, differs between versions then the code can be delimited within a conditional inclusion: This can be achieved using this enumeration and definition:
enum VERSION {FULL, ECONOMY, SAMPLE}; #define VERSION FULL
Conditional Compilation
Conditional compilation provides a way where by code can be selectively included into the compilation - depending on values available at pre-processing. The syntax of conditional compilation statement is:
#if constant expression statements #endif
The pre-processor evaluates the constant expression if it is zero (false) the statements are deleted from the code passed to the compiler, if the constant expression is non-zero (true) the statements are passed to the compiler. The constant expression is made up of literals and variables that have been defined using a #define. It is illegal to use anything where the value will not be known until execution time, as the compiler is unable to predict the values. A simple example is to bracket the code used for debugging in the following manner:
#if DEBUG printf("At line %d: a=%d, b=%d\n",__LINE__,a,b); #endif
Conditional definitions
The #ifdef command conditionally includes code if a symbol is defined. If the symbol is not defined, the code is not included. The opposite command is #ifndef which includes code only if the symbol is not defined. For example, if the program includes a library file that, in some implementations, does not define a symbol MAXLINES, then the program may have a fragment like this:
Therefore, the listing may contain many instances of this conditional inclusion, protecting the printing of interesting variables. If the following is present:
#define DEBUG 1
1: 2: 3: 4:
Line 1 includes the library, which may vary between machines. Line 2 checks if MAXLINES is already defined. If it isn't defined then line 3 defines it. Line 4 is the end of the conditional definition. This is necessary as it is not possible to define the same symbol twice. An alternative would be to undefine the symbol and then redefine it, for example:
#undef MAXCHARS #define MAXCHARS 60
then at compile time, all the debugging print statements will be included in the object code produced. Whereas if the definition is:
#define DEBUG 0
none of the debug statements will be included in the object code generated. Conditional compilation is also useful if developing a software product that has different functionality depending on whether the user has purchased the full
Notes on C programming
Pragma
The #pragma command is a mechanism that supports implementation dependent
24
VFR October 03
directives. An environment may provide pragmas to allow special options. Where a pragma is not recognised, it is ignored. So a program with pragams will run on different machines, however the actions of the pragma may not be the same across machines, so the program is not truly portable. Pragmas are used in a number of ways by C++ Builder. For example when generating forms, the corresponding unit will contain code like this:
#pragma resource "*.dfm"
in a Delphi unit.
Notes on C programming
25