You are on page 1of 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: V.F.Ruiz@reading.ac.uk

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


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

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
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* 10
38

double 8 1.7*10
-308
to 3.4 ^ 10
308


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

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;
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;
}
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
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];
VFR October 03 SE2B2 Further Computer Systems
Notes on C programming 12

To record 6 hours of sunshine for the 1
st
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
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,
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.
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);
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
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:
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++.
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;
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.
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.
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)

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

You might also like