lecture notes on c

26
Department of Cybernetics The University of Reading SE2B2 FURTHER COMPUTER SYSTEMS LECTURE NOTES ON C PROGRAMMING by Dr Virginie F. Ruiz Department of Cybernetics room 184, E-mail: [email protected]

Upload: nawaraj-subedi

Post on 07-Mar-2015

15 views

Category:

Documents


3 download

TRANSCRIPT

Page 1: Lecture Notes on C

Department of Cybernetics The University of Reading

SE2B2

FURTHER COMPUTER SYSTEMS

LECTURE NOTES ON C PROGRAMMING

by

Dr Virginie F. Ruiz Department of Cybernetics

room 184,

E-mail: [email protected]

Page 2: Lecture Notes on C

VFR October 03 SE2B2 Further Computer Systems

Notes on C programming 1

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 DO…WHILE 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

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

Page 3: Lecture Notes on C

VFR October 03 SE2B2 Further Computer Systems

Notes on C programming 2

AN INTRODUCTION TO C FOR DELPHI PROGRAMMERS

Introduction This course will cover an introduction to programming in C. The programming environment used is MS Visual C++. If you develop your programs using another environment please make sure that you can run it under Visual C++ version that is available in the School.

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.

C AND C++ FOR DELPHI PROGRAMMERS - AN INTRODUCTION

The C programming language was developed in the early 1970s at the Bell Laboratories. It became the primary language for the UNIX operating system. It spread in popularity and is now available on most computers, not just those using UNIX. There is an ANSI (American National Standards Institute) standard for the C programming language, which is used to ensure that C code, is portable between computers.

The programming language C++ was originally developed in the 1980s to support the development of large software projects, including those using object oriented methods. The name signifies the evolution from C; the increment operator "++" signifies that the new language is the old one with additions; indeed C++ is a superset of C (there are a few exceptions). There is now an ANSI standard for C++.

There are many compilers and environments available for turning C and C++ programs into executable programs. These range from the simplistic to the supportive. The most simple systems require the programmer to use a text editor to enter all the code, the code is written to an appropriately name file, compiled, then linked to necessary libraries and execution of the code can then follow. The user corrects mistakes returning to the editor. More supportive environments tend to be integrated, with some automatic generation of code; the available libraries may be very extensive.

Comparing the above description with Delphi is a useful exercise. The base level of Delphi is Pascal. C and Pascal share common roots on the Algol family of languages, these will become obvious as C is studied in more detail. Object Pascal is Borland's extensions to Pascal that allow object-oriented programming. In much the same way C++ is an extension to C. The Delphi IDE (Interactive Development Environment) is the usual way in which Delphi programmers develop both Pascal and Object Pascal programs. There are many environments available to the C and C++ programmer to develop their programs. As C and C++ are used in a variety of environments the examples presented here will not rely on a particular environment, instead they will focus on the code.

The C programming language is very powerful. It provides ready access to program memory. This is both a strength and a weakness. Delphi programmers are used to the compiler catching silly errors, often made as a result of mistyping, the danger with C is that these errors will be syntactically correct but will cause a run time error. Therefore, the Delphi programmer must be careful to remember that C is a weakly typed language and does not provide the compile time checking that is a feature of the more strongly typed Pascal.

Page 4: Lecture Notes on C

VFR October 03 SE2B2 Further Computer Systems

Notes on C programming 3

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.

The equivalent in C is:

/* A C program illustrating comments, simple IO,

constants, libraries */

#include <stdio.h>

#define MYCHOICE 7

void main(void)

{

int MyNumber,YourNumber;

printf("Hello\n");

MyNumber = MYCHOICE;

printf("%5d is my lucky number\n",MyNumber);

printf("What's yours?\n");

scanf("%d",&YourNumber);

printf("%5d is your lucky number\n", YourNumber);

}

These two programs will be used to highlight the similarities and differences between C and Pascal.

Comments

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 don’t always do this and the line:

void main(void)

could be replaced by

main( )

Functions will be discussed in more detail later.

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;

Page 5: Lecture Notes on C

VFR October 03 SE2B2 Further Computer Systems

Notes on C programming 4

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;

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>

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);

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.

Definitions

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;

This could occur anywhere that a variable declaration could occur.

Assignment In Pascal, the assignment statement uses the := operator. In C assignment is an expression that returns a value. So the line:

MyNumber = MYCHOICE;

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.

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

Page 6: Lecture Notes on C

VFR October 03 SE2B2 Further Computer Systems

Notes on C programming 5

wise to check that there is not a mismatch between the case used in the declaration and that used in an expression.

TYPES AND OPERATORS

The C programming language, like Pascal, supports several different data types. Variables are declared to be of a type. Operations can be performed on variables and constants.

Variable names in C are made up of uppercase and lowercase letters, numbers and the underscore character. Variable names can begin with either a letter or an underscore, names beginning with underscore often have special meanings and so are best avoided. It should be remembered that C is case sensitive so it is important always to use the same mix of uppercase and lowercase letters in a particular name. As with Pascal it is wise not to use variable names that match reserved words.

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 Size in Bytes possible range of values char 1 -128 to 127 unsigned char 1 0 to 255 int 4 -2147483648 to 2147483647 unsigned int 4 0 to 4294967295 short int 2 -32768 to 32767 long 4 same as int float 4 3.4 *10-38 to 1.7* 1038 double 8 1.7*10-308 to 3.4 ^ 10308

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.

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;

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 Description Example = Assignment Number = 6; += Sum and assign Number += 10; adds 10 to Number -= Subtract and

assign Number -= 5; subtract 5 from Number

*= Multiply and assign

density_1 *= Number;

same as density_1= density_1*Number;

/= Divide and assign density_1/=4; divides density_1 by 4 &= bitwise AND and

assign Letter1&=Letter2 ands the bits of Letter1

with Letter2 |= bitwise OR and

assign Letter1|=Letter2 ors the bits of Letter1

with Letter2 Table 2

More details of the other operations will be given in the following sections.

Page 7: Lecture Notes on C

VFR October 03 SE2B2 Further Computer Systems

Notes on C programming 6

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 Example + add 2 + 3 same as Pascal - subtract Number - 1 same as Pascal * multiply density_1*2.5 same as Pascal / division density_1/density_2 same as Pascal % remainder after

integer division Number % 2 same as mod in

Pascal 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;

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.

Logical and Equality Operators There are two logical operators in C. These both in the same way as the equivalent in Pascal. They are:

&& AND || OR

There are six equality operators, all working in the same way as the Pascal equivalent.

C Operator Description Pascal equivalent == Equal to = != Not equal to <>

< 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 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 ~.

Increment and Decrement C has unary operators for both increment and decrement. They work in the same manner as the Inc and Dec procedures in Pascal.

Operator Description Example Pascal equivalent ++ increment x++;

++y; inc(x); inc(y)

-- decrement x--; --y;

dec(x); dec(y)

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.

Unary Pointer Operators There are two unary operators used with pointers, these are similar to their equivalents in Object Pascal, there is no equivalent to the address of operator in standard Pascal.

Page 8: Lecture Notes on C

VFR October 03 SE2B2 Further Computer Systems

Notes on C programming 7

C Operator Description Object Pascal equivalent & Address of @ * Indirection ^

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;

• in Pascal a pointer to an integer is declared like this:

var PIndex:^Integer;

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

Data structures such as linked lists mix records and pointers. In Pascal a list is traversed using expressions such as:

PThisItem^.Next

The C equivalent needs brackets to control what the indirection operator is applied to:

(*CThisItem).Next

Alternatively, the C indirection operator can be used:

CThisItem->Next

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.

Operators 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.

While Statement The syntax of a while statement is:

while (expression) statement;

Page 9: Lecture Notes on C

VFR October 03 SE2B2 Further Computer Systems

Notes on C programming 8

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++;

}

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.

do…while 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);

This will loop until the user types something other than a 7.

If the final expression is replaced by:

(n=7)

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.

The general format is:

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 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);

}

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

The loop would be executed 11 times.

if statement The general format of an if statement in C is:

if (test) statement;

The statement (or compound statement) can be followed by an else clause.

if (test) statement;

Page 10: Lecture Notes on C

VFR October 03 SE2B2 Further Computer Systems

Notes on C programming 9

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");

}

As with Pascal statements can be nested, as shown in the following example.

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");

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 ;

Where label is a local label, terminated by a colon.

For example:

again:

/* code*/;

goto again;

continue and break C offers two structured alternatives to goto. Both of these cause the remainder of a compound statement to be ignored.

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; }

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", x + y); break; case '-' : printf("%d", x - y); break; case '*' : printf("%d", x * y); break; case '/' : printf("%d", x / y); break; default: break; }

Page 11: Lecture Notes on C

VFR October 03 SE2B2 Further Computer Systems

Notes on C programming 10

ARRAYS, POINTERS AND STRINGS

An array is a bounded collection of elements of the same type. Each element of an array is accessed via an index. In C, all arrays are stored in contiguous memory locations. The lowest address corresponds to the first element and the highest address to the last element. As in Pascal arrays can be, single dimensional or multidimensional.

In C arrays and pointers are closely related, so programmers can use pointers to traverse arrays.

One of the most common uses of one-dimensional arrays is to represent strings. These are stored as an array of characters, terminated by a null. In Delphi, we refer to these as C-strings.

One Dimensional Arrays Consider this fragment of program:

1: int square[5];

2: int i;

3: for(i = 0;i < 5; i++)

4: square[i] = (i+1)*(i+1);

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};

Reading into an Array 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);

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

Page 12: Lecture Notes on C

VFR October 03 SE2B2 Further Computer Systems

Notes on C programming 11

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.

Generating a Pointer to an Array A pointer to the first element of an array is generated by specifying the array name, without any index.

So given the definition:

int *PtrToSquare;

it is possible to assign in this manner;

PtrToSquare = square;

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);

More on pointers and arrays later.

Strings Arrays of characters are used to hold strings. Consider the declaration:

char AStr[11];

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";

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.";

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:

if strcmp(s1,s2)

{

dosomething()

}

else

{

somethingelse()

}

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 ) concatenates the second string to the first int strcmpi(s1,s2) compares two strings, without case sensitivity strcpy(s1,s2) copy a string to the target string strstr(s1,s2) scan a string for the first occurrence of a substring int strlen(s1) returns the length of a string 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];

Page 13: Lecture Notes on C

VFR October 03 SE2B2 Further Computer Systems

Notes on C programming 12

To record 6 hours of sunshine for the 1st April, the following assignment could be done:

suns [3][0] = 6;

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

these can then be used in the program:

int testarray [XDIM][YDIM][ZDIM];

int i,j,k;

for (i=0;i<XDIM;i++)

for (j=0;j<YDIM;j++)

for (k=0;k<ZDIM;k++)

testarray[i][j][k]=i*YDIM*ZDIM+j*ZDIM+k;

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).

Pointers

A pointer is an address of a location (of a particular type), for example:

float * RealPtr;

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;

Pointers and one dimensional arrays In C pointers are often used to access existing structures, such as arrays. The following code shows three ways in which an array can be accessed:

1: int square[5];

2: int *ptr = &square[0];

3: /* assigns square of number to element */

4: for (i = 0; i < 5; i++) square[i] = (i+1)*(i+1);

5: /* and so does this */

6: for (i = 0; i < 5; i++) ptr[i] = (i+1)*(i+1);

7: /*and this - note comma*/

8: for(i = 0;i < 5;ptr++, i++)

9: *ptr = (i+1)*(i+1);

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;

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 and multidimensional arrays Pointers can also be used to access multidimensional arrays, care is needed in two

Page 14: Lecture Notes on C

VFR October 03 SE2B2 Further Computer Systems

Notes on C programming 13

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: int values[3][4];

2: int *ptr;

3: ptr=&values[0][0];

4: for (i=0;i<3*4;i++,ptr++)

5: *ptr=i;

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");

}

Dynamic memory allocation The above pointers have used static memory and pointer manipulation on previously declared arrays. Pointers are often used in association with dynamic memory allocation. The use is similar to that in Pascal. There are a number of functions associated with the allocation and freeing of dynamic memory. These are found in the library stdlib.h. Most commonly used are malloc and free, which can be closely matched to new and dispose in Pascal.

1: int *numbers;

2: printf("How many?\n");

3: scanf("%d",&n);

4: /* allocate memory for string */

5: if ((numbers=(int *) malloc(n*sizeof(int)))==NULL)

6: {

7: printf("Not enough memory to allocate buffer\n");

8: /* terminate program if out of memory */

9: }

10: else

11: {

12: /* copy values into numbers */

13: for (i=0;i<n;i++,numbers++)

14: *numbers=i;

15: /* display numbers */

16: for (i=n;i>0;i--)

17: printf("Numbers are %3d\n", *(numbers-i));

18: /* free memory */

19: free(numbers-n);

20: }

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,

Page 15: Lecture Notes on C

VFR October 03 SE2B2 Further Computer Systems

Notes on C programming 14

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; }

A whole program using this function is given below:

#include <stdio.h>

#define MYCHOICE 7

int Calc(int Mine,int Yours);

main()

{

int MyNumber,YourNumber;

MyNumber = MYCHOICE;

printf("%5d is my number\n",MyNumber);

printf("What's yours?\n");

scanf("%d",&YourNumber);

printf("Mine plus twice yours is: %5d",

Calc(MyNumber,YourNumber));

}

int Calc(int Mine,int Yours)

/* function to calculate Mine plus twice Yours */

{

int Temp;

Temp = Yours * 2;

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);

and in the corresponding function:

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");

}

the function does not return a value. It can be called by the following code:

CommentOnValue(MyNumber);

where MyNumber is a suitably declared variable.

Page 16: Lecture Notes on C

VFR October 03 SE2B2 Further Computer Systems

Notes on C programming 15

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.

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: int Calc(int Mine,int Yours)

2: /* function to calculate Mine plus twice Yours */

3: {

4: int Temp;

5: Temp = Yours * 2;

6: return Mine+Temp;

7: }

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

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>

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;

and a C source file containing:

1: #pragma hdrstop

2: #include <condefs.h>

3:

4:

5: //----------------------------------

6: #include <stdio.h>

7: #include "MyHeader.h"

8: void main()

9: {

10: printf ("Enter a number: ");

11: scanf("%d",&dummy);

12: CommentOnValue(dummy);

Page 17: Lecture Notes on C

VFR October 03 SE2B2 Further Computer Systems

Notes on C programming 16

13: scanf("%d",&dummy);

14: }

15:

16: void CommentOnValue(int number)

17: {

18: if (number > 100)

19: printf("Large\n");

20: else if (number > 0)

21: printf ("Not large but positive\n");

22: else if (number == 0)

23: printf("or zero\n");

24: else

25: printf("Negative \n");

26: }

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;

*y=temp;

}

and called like this:

int a=3,b=5;

swap(&a,&b);

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;

temp=r;

r=s;

s=temp;

*rptr=r;

*sptr=s;

}

The call is the same as before:

swap2(&a,&b);

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

Page 18: Lecture Notes on C

VFR October 03 SE2B2 Further Computer Systems

Notes on C programming 17

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;

}

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);

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])

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]));

This does not work inside the function init_array.

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

types based on bit patterns.

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

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

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) or

sqrt((double)4)) or

sqrt((double)(AInt+4))

A properly declared prototype will avoid this problem.

Enumerated Types An enumerated type is made up of an ordered list of names, which are symbolic constants. For example:

enum Seasons {Spring, Summer, Autumn, Winter};

Variables of the type are declared like this:

Page 19: Lecture Notes on C

VFR October 03 SE2B2 Further Computer Systems

Notes on C programming 18

enum Seasons This_Year, Next_Year;

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=' '};

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 correct form is:

This_Year=(Seasons)((int)This_Year+1);

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.

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:

enum Roman {I=1,II,III,IV,V,VI,VII,VIII,IX,X};

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;

Variables of this type can be declared:

gridtype a, b;

The individual members of the structure can be accessed using the dot operator.

a.x=25;a.y=-50;

Pointers to Structures A pointer to a structure can be declared like this:

gridtype *ptrb;

and this can be associated with an existing variable like this:

ptrb = &b;

To obtain the x member of *ptrb we do either:

(*ptrb).x

or use the specially provided operator ->

ptrb->x

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++.

Page 20: Lecture Notes on C

VFR October 03 SE2B2 Further Computer Systems

Notes on C programming 19

If we have:

typedef struct {

int x;

int y;

} gridtype;

gridtype a, *aptr=&a;

then the following are equivalent:

a.x

aptr->x

(*aptr).x

The priorities indicate that:

++ aptr->x increments the x member. If it was required to increment a ptr, brackets should be used.

A function returning a structure Function to return structure

gridtype makeplace (int xx, int yy) { gridtype temp; temp.x = xx; temp.y = yy; return temp; }

The scoping of C means it is acceptable to use x and y in place of xx and yy:

gridtype makeplace (int x, int y) { gridtype temp; temp.x = x; temp.y = y; return temp; }

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:

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.

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: enum TSORT{TINTEGER,TFLOAT,TSTRING} ;

2: typedef struct

3: {

4: enum TSORT sort;

5: union

6: {

7: int ival;/* or */

8: float fval;/* or */

9: char *sval;

Page 21: Lecture Notes on C

VFR October 03 SE2B2 Further Computer Systems

Notes on C programming 20

10: } uvar;

11: } VARIABLE;

12: VARIABLE AVar;

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;

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;

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;

}

Bitfields When trying to economise on space a programmer may wish to pack several things

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: typedef struct

2: {

3: signed i : 2;

4: unsigned j : 5;

5: signed : 4;

6: signed k : 1;

7: unsigned m : 4;

8: } TBIT;

9: TBIT a,b;

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 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 X X X X X X X X X X X X X X X X

m k unused j i 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;

Would put the bit pattern 1 1 in bits 1 and 0.

Page 22: Lecture Notes on C

VFR October 03 SE2B2 Further Computer Systems

Notes on C programming 21

However, there is no warning of overflows so the assignment:

a.i = 6;

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. 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.

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. deleting comments 2. inserting the contents of files mentioned in #include directives 3. defining and substituting symbols from #define directives 4. deciding which code should be compiled depending on conditional compiler

directives 5. to act on recognised #pragma statements, which are implementation dependent.

Predefined Symbols C provides a number of predefined symbols, the values of which are either string literals or decimal constants. These include:

Symbol Sample Value Meaning _ _FILE_ _ " C:\ Unit1.cpp" name of the source file being compiled _ _DATE_ _ "May 14 1999" date the current file was compiled _ _STDC_ _ 1 1 if the compiler conforms to ANSI C,

otherwise undefined 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

causes all occurrences of XDIM to be replaced by the literal 10.

Page 23: Lecture Notes on C

VFR October 03 SE2B2 Further Computer Systems

Notes on C programming 22

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 */

{

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__)

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__)

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;

After pre-processing, it would be:

int j=100;

i= 2+j;

The programmer must realise that the replacement text is used exactly. So:

5* TWO_PLUS_J

will after pre-processing be:

5* 2+j

which might not be what the programmer expected?

Macros A macro allows parameters to be used in substitution text. In C the syntax of a macro definition is:

#define name(parameters) code

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

In the text an occurrence of SQUARE(5) will be replaced by:

5*5

So a program containing the following:

x= SQUARE(5); y= SQUARE(x); z= SQUARE(2.5);

will after pre-processing be:

x= 5*5; y= x*x; z= 2.5*2.5;

However, an occurrence of

10+SQUARE(5)

Page 24: Lecture Notes on C

VFR October 03 SE2B2 Further Computer Systems

Notes on C programming 23

will be replaced by

10+5*5

this is not a problem here, consider what happens with the following:

#define DOUBLE(IT) IT+IT

m= 10*DOUBLE(5);

after pre-processing, the assignment will become:

m= 10* 5+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))

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;

can be written in a shorter form as:

b= (a>0) ? 0 : 1;

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++)

*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}

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

In the program code such as the following can be used:

scanf("%d",&dummy);

a=MAX(5,dummy);

After pre-processing, this will be:

scanf("%d",&dummy);

a= 5>dummy ? 5 : dummy;

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);

}

This is not exactly the same. The arguments have had to be given a type and so

Page 25: Lecture Notes on C

VFR October 03 SE2B2 Further Computer Systems

Notes on C programming 24

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.

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

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

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

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

in conjunction with conditional compilations of the following sort:

#if (VERSION == FULL)

statements for full implementation

#elif (VERSION == ECONOMY)

statements for economy implementation

#else

statements for sample implementation

#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:

1: #include <somelib.h>

2: #ifndef MAXLINES

3: #define MAXLINES 100

4: #endif

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

Pragma The #pragma command is a mechanism that supports implementation dependent

Page 26: Lecture Notes on C

VFR October 03 SE2B2 Further Computer Systems

Notes on C programming 25

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"

This is equivalent to:

{$R *.DFM}

in a Delphi unit.