c programming lecture notes 2008-09
TRANSCRIPT
-
8/3/2019 C Programming Lecture Notes 2008-09
1/173
1
1. A Gentle Introduction to C 2
2. Variables, Operators & Expressions 9
3. Statements & Flow Control 20
4. Functions 36
5. The Preprocessor 50
6. Arrays 56
7. Program Structure 62
8. Pointers 77
9. Strings 103
10. Structures 113
11. Dynamic Memory 122
12. Input & Output 135
Appendix A: Some Example Programs 151
Appendix B: Problem Sheets
Appendix C: Past Exam Papers
Appendix D: Data Structure Example
-
8/3/2019 C Programming Lecture Notes 2008-09
2/173
2
1. A Gentle Introduction to C
1.1 Basic Hardware concepts
A computer is made up of three main components:
1. The micro-processor (mp) which is only able of
performing very basic
instructions
2. The memory which stores the data manipulated by the
micro-processor.
3. The bus which links the mp to the memory and allow
communication.
1.1.1 Memory - the concept of address
The memory of a computer is made of a large number of electronic components
which are designed to store the value of a set of eight binary values, a byte.
Each of these components is identified by a integer (usually given in
hexadecimal notation) called the address.
In other words, an address is nothing more than a location in the memory where
a byte can be stored.
Example: the 6501, a very simple mp, can manage up to 64 Kb of memory.
There are therefore 65,536 addresses available to store bytes of information.
The addresses start at 0000 and finish at 0xFFFF (which is 65,535 in
hexadecimal notation).
When the mp requires data, it has to determine its address. This is the only way
it can access it.
Every object that the computer manipulates has to be represented as one or a
sequence of several continuous bytes.
The address of an object in memory is the address of the first byte used to store
it.
-
8/3/2019 C Programming Lecture Notes 2008-09
3/173
3
1.1.2The Micro-Processor - The Concept of Machine Instruction
The micro-processor is a electronic component which is able to perform only a
fairly limited number of basic operations:
1. store a bit pattern in memory
2. get a bit pattern from memory
3. perform basic arithmetic or boolean operations on a bit pattern
4. jump to "somewhere'' in a program. Actually, the mp jumps to the
address where the next instruction to be executed is stored in memory.
These basic operations are the machine instructions.
Basic tasks such as the multiplication of two real numbers have to be
decomposed in a large sequence of machine instructions.
A computer program at the lowest level is nothing more than a sequence of
machine instructions operating on the memory.
1.2 The Role of High-Level Languages
The first people to use computers had to write their program directly with the
machine instructions and addresses needed.
This is both very impractical and error-prone.
High level languages are designed to allow easier communication between the
user and the machine.
While executable code is made of machine instructions and addresses, a high
level language is made of statements describing the operations to be done and
variables which store the data being processed.
Once a program is written in the language, it is transformed into an executable
code in two main stages:
1. the compiler translates the high level instructions into machine
instructions
2. the linker associates an address with every variable used. A variable is
nothing more than a symbolic name for an address in the computer
memory.
-
8/3/2019 C Programming Lecture Notes 2008-09
4/173
4
1.3 A First C program
A C program is collection of functions which operate on variables.
A function in C is a sequence of computing operations which are executed in
sequence.
A function has:
1. a name
2. a number of arguments which provide data for the function (the
function may need no argument).
3. a body i.e. a sequence of statements that describe the job to be carried
out by the function.
4. a result which is returned (the function may also return no result).
Example of a complete C program:
#include
int main()
{
printf("Hello world\n");
return 0;
}
After compilation and linkage, this program will display the message Hello
worldon the screen.
-
8/3/2019 C Programming Lecture Notes 2008-09
5/173
5
Analysis of the program
It consists of a function called main which takes no arguments and returns no
value. main is the C equivalent of the main program in Pascal or FORTRAN.
A C program starts at the beginning ofmain. Consequently every C program
has to have a main function somewhere.
The work done by main is enclosed between the braces {}.
Usually, main will call other functions to perform its task.
In this case, main only uses one other function, called printf with a single
argument: "hello World". It is printf which is responsible for outputting the
message.
In C a function is called by stating its name followed by a possibly empty list of
arguments enclosed in brackets:
Function_Name(list_of_arguments);
printf is very commonly used in C programs. It is the basic output routine.
After execution ofprintf, the end of main is reached and control is returned
to the calling program. In the case of main, the calling program is always theoperating system.
Note that printf is not defined in the program. The reason is that printf is
a function which is part of the C standard itself.
A C program can therefore make use of function that are either written by the
program or provided to him by the language standard.
C comes in with different collections of such standard functions called libraries.
Commonly used library of functions include:
1. input/output operations
2. mathematic functions
3. processing of strings of characters.
The line #include indicates to the compiler that the
input/output library is going to be used in the program.
-
8/3/2019 C Programming Lecture Notes 2008-09
6/173
6
1.4 Variables in C
Variables are defined by specifying:
a) a name
b) the type of data they are to contain
C can handle variables containing integer numbers, floating point numbers and
a single characters.
For example:
#include
int main()
{
int sum;
sum=10+15;
printf("sum of 10+15:%d\n",sum);
return 0;
}
This program makes use of a variable named sum of type integer (this is a
short-cut for the full length definition: sum is a variable which can hold the
value of an integer).
The line int sum; is a variable definition. As well as giving information to
the compiler about the name of the variable and its type, the compiler also
allocates the memory corresponding to the variable.
In C, a variable definition follows the pattern: type_of_data Variable_Name;
The line sum=10+15; is a assignment statement. According to intuition, the
value computed from the expression at the right of the sign = is stored in sum.
= is called the assignment operatorin C
The line printf("the sum of 10+15 is %d\n",sum); calls
printf for the purpose of outputting information. Here, there are two
arguments:
-
8/3/2019 C Programming Lecture Notes 2008-09
7/173
7
The first argument is the string of characters to be printed. The % indicates
where the second argument is to be substituted, and in what form it is to be
printed.
Here %d means that the second argument is to be printed as an integer (d
standing for digit).
The second argument, sum, gives the value to be printed. When printf is
called, the value stored in sum will be passed the function.
This is an example of variable reference. Whenever a variable is encountered in
an expression, the value it contains at that time is substituted in the expression.
1.5 Basic Input/Output Operations in C
Basic output is performed by printf. It is a general purpose function to print
characters
to the screen.
Unlike most other functions, it has a variable number of arguments; a call to
printf takes in general the following form:
printf(format,val1,...,valn);
format is a string of characters which is to be printed. It is composed of i)
ordinary characters which are output directly to the screen and ii) conversion
specifications.
Each conversion specification begins with % and controls the way in which one
of the subsequent arguments is converted into printable characters.
val1 to valn are the arguments to be converted. There must be as many
arguments as there are conversion specifications in the format string.
Examples:
printf("A string\n") output the message A string and starts a
new line.
printf("%d\n",sum) prints the value of the variable sum
interpreted as an integer.
printf("%d %f\n",a,b) prints the values of integer variable a
and floating point variable b, separated by a space and followed by a
new line. A more detailed description ofprintf is given later.
-
8/3/2019 C Programming Lecture Notes 2008-09
8/173
8
1.6 Example of a User Defined Function
The following programme illustrates how a user-defined function can be written
and employed in the C language.
This program computes a generalisation of the square root:
/* pre-processor directivenecessary when using the math library */
#include
double gen_sqrt(double); /* function prototype */
/* main function */
int main()
{
double val,sqroot; /* variables */
/* ask the user to enter a real number */printf("Enter a floating point value > 0");
/* get the value from the user */scanf("%lf",&val);
/* call the function to compute the generalised square root */sqroot=gen_sqrt(val);
/* print out the result */printf("The generalised square root of %lf is %lf\n",val,sqroot);
return 0;
}
/* -- user-defined function gen_sqrt --*/
double gen_sqrt(double x)
{
double result;
if(x
-
8/3/2019 C Programming Lecture Notes 2008-09
9/173
9
2. Variables, Operators and Expressions
2.1 Variable Types
A variable is a symbolic name which the compiler associates with an area in
memory where a value can be stored.
The fundamental characteristic of a variable is the type of data which they hold.
This is important because (i) different data types need different amounts of
storage space and (ii) different data types are represented differently in binary
form.
A variable declaration specifies to the compiler: (i) the name of the variable, (ii)
its type. In addition, the compiler allocates the necessary memory.
Strictly speaking, this is really a variable definition. A variable declaration does
not cause the compiler to allocate memory. However, in the common usage,
declaration is used in both cases.
A C declaration follows the pattern
data_type variable name;
C can handle variables of the following data types:
characters (which are stored as integer values according to a code - for
example the ASCII code):
char c; declares a variable c of type character. A variable of type
character only hold one character.
-
8/3/2019 C Programming Lecture Notes 2008-09
10/173
10
2.2 Variable Names
One can also speak of a variable identifier
Names are made of letters and digits. The first character must be a letter.
The underscore _ is a letter so that names such as the_sum, _value, flag_
are valid.
It is best to avoid names starting with an underscore because these patterns are
often used for library
functions.
Upper and lower cases are different and can be mixed. SUM, Sum and sum
are all different names.
Traditional C usage is that variables are in lower cases and constants in
uppercases.
Internal names are significant up to 31 characters.
External names are guaranteed significant up to 6 characters by the standard
and are single case.
(This is due to the fact that external names have to be manipulated by linkers,
assemblers and loaders over which the language has no control).
In practice, external names can often have more than 6 significant characters.
There is a list of reserved keywords which cannot be used as variable names:
auto double int struct
break else long switch
case enum register typedef
char extern return union
const float short unsigned
continue for signed void
default goto sizeof volatile
do if static while
-
8/3/2019 C Programming Lecture Notes 2008-09
11/173
11
2.3 More on Fundamental Data Types
Important properties of a data type are:
1. its size i.e. the number of bits that are used to
store a value of this type
2. its range i.e. the interval in which values can be
represented by a variable of this type.
C provides a range of fundamental data types (with different sizes and
range) in order to suit the needs of the programmers.
According to the basic type of data, types belong to one of three
categories:
1. Integral Types
2. Character Integral Types
3. Floating Point Types
A character integral type can either be unsigned or signed.
If a character variable is stored using 8 bits, as is usually the case, then
if we have unsigned char x; can hold values
between 0 and 255.
if we have signed char x; can hold values
between -128 and 127.
The declaration char x; is in practice equivalent to signed char
x;
A integral type can also be either unsigned or signed, the default being
signed.
For integral types, There are also three different sizes available (that is
the number of bits used to store the number):
1) short int
2) int
-
8/3/2019 C Programming Lecture Notes 2008-09
12/173
12
Examples :
unsigned short int i;
signed long int j;
int can be omitted when used with either short, long, signed or unsigned.
Thus:
long i;
is equivalent to
signed long int i;
Finally, there are two floating point types:
1. float
2. double for double precision numbers
The C standard does not specify that a double is twice as precise as a
float.
The only guarantee is that the latter types in the list are at least as
precise as those which precede.
The actual size of these types is not specified by the standard. The
following table shows the size for different platforms:
Type PCDec MIPS
(ULTRIX)
Dec Alpha
(OSF/1)
Dec Alpha (OPEN VMS)
char 1 1 1 1
short int 2 2 2 2
int 2 4 4 4
long int 4 4 8 4
float 4 4 8 4
double 8 8 8 8
-
8/3/2019 C Programming Lecture Notes 2008-09
13/173
13
2.4 Constants
There are four categories of constants in C:
1. Integer Constants (such as 63, 0 and 42L)
2. Floating-Point Constants (such as 1.2, 0.00,
and 77E2+)
3. Alpha-Numeric Constants (such as 'A' or
"Hello!")
4. Enumeration constants
2.4.1 Floating-point constants
The default type of a floating-point constant is double.
An F or f is appended to the value, which specifies the float type for the
floating point constant.
Examples:
Notation Value Type
.0 0.000000 double
0. 0.000000 double
2. 2.000000 double
2.5 2.500000 double
2e1 20.00000 double2E1 20.00000 double
2.E+1 20.00000 double
2e+1 20.00000 double
2e-1 0.200000 double
2.5e4 25000.00 double
2.5E+4 25000.00 double
2.5F 2.500000 float
-
8/3/2019 C Programming Lecture Notes 2008-09
14/173
14
2.4.2 Alpha-Numerical Constants
A character constant is any character from the character set enclosed in
apostrophes.
Note that characters are stored by the machine as (unsigned) integernumbers. The value associated with the character is machine dependent.
Very often the ASCII code is used.
The following piece of code is valid:
char c;
c='A';
On a machine using the ASCII code, the value 65 (which is the ASCII
code for A), is stored in c.
Computers use not only printable characters (letters, digits, punctuation,
etc.) but also non-printable characters such as the new-line character, the
horizontal tab and the bell.
These characters can be entered as escape sequences (which begin with
a backslash '\')
Non printable characters are:
Character Escape sequence
Alert (Bell) \a
Backspace \b
Form feed \f
Newline \n
Carriage Return \r
Horizontal tab \t
Vertical tab \v
In addition, some printable characters have to be entered as escape
sequences because they have a special meaning in the C syntax.
-
8/3/2019 C Programming Lecture Notes 2008-09
15/173
15
Example: char c='\n';
A string constant or string literal is a sequence of characters enclosed by
double quotes (").
Escape sequences can be included in the string.
Example of valid string constants are:
"Hello world"
"Hello world\n"
"Ready ? \n Steady \n\t Go!\a\n"
Unlike the FORTRAN read, C does not add a new-line character after a
call to printf. The new line has to be given explicitly. Hence:
printf("Hello ");
printf("World");
will produce the output
Hello World
But
printf("Hello\n");
printf("World");
will produce the output
Hello
World
and
printf("Ready ? \n Steady \n\t Go!\a\n");
-
8/3/2019 C Programming Lecture Notes 2008-09
16/173
16
2.4.3 The const type modifier
Some variables are meant to store constants. This is particularly true in
the case of functions parameters.
C provides a keyword, const, which informs the compiler that the
value stored in a variable is constant and that any attempt by theprogrammer to modify this value is illegal.
For example:
const double pi=3.14159;
2.5 Operators and Expressions
An expression is a sequence of computing operations, to which aresulting value can be associated.
C has a wider range of expressions than the usual mathematical and
boolean expressions. For instance, a variable assignation such as:
x=1.0;
is an expression.
The value associated with an assignation expression is the value storedin the variable, in this case, 1.0.
An operator is "something'' which given some arguments (the operands)
will produce a result.
C has a very wide set of operators.
C has unary operators which take only one argument, binary operators
which take two arguments and even a ternary operator which need three
arguments!
2.5.1 Arithmetic operators
The usual unary arithmetic operators are:
- which gives the negative of the operand
+ which gives the value of the operand itself (not
very useful)
-
8/3/2019 C Programming Lecture Notes 2008-09
17/173
17
2.5.2 Relational and Logical Operators
The relational operators are >, >=,
-
8/3/2019 C Programming Lecture Notes 2008-09
18/173
18
2.5.3 Increment and Decrement Operators
The increment operator ++ adds 1 to its operand.
The decrement operator -- subtracts 1 from its operand.
For example:
int main()
{
int i=0;
i++;
printf("i: %d\n",i);
return 0;
}
will produce the output: i : 1
i++; is therefore equivalent to i=i+1;
Similarly, i--; is equivalent to i=i-1;
Both ++ and -- can either be prefix or postfix operators.
In other words, i++, ++i, i-- and --i are all valid C expressions.
However ++i is not equivalent to i++ although they both increment the
value of i.
With ++i, the value of the expression is the value of i after theincrementation is carried out.
With i++, the value of the expression is the value of i before the
incrementation is carried.
-
8/3/2019 C Programming Lecture Notes 2008-09
19/173
19
Consider the code:
int main()
{
int i=0;
printf("i: %d\n",++i);
return 0;
}
It will produce the output i: 1 as in the last example because printf
outputs the value of the expression ++i, which is the value ofi after
incrementation.
By contrast,
int main()
{
int i=0;
printf("i: %d\n",i++);return 0;
}
will produce the output i: 0 because the value ofi++ is the
value ofi before incrementation.
Another example:
int main()
{
int i=0,j,k;
j=++i;
k=i++;
printf("i: %d j: %d k: %d\n",i,j,k);
return 0;
}
-
8/3/2019 C Programming Lecture Notes 2008-09
20/173
20
3. Statements and Flow Control in C
3.1 Concept of Statement in C
A statement is either
an expression followed by a semicolon;
a construct which controls the flow of the program
the so-called 'null' statement which consists of a single semicolon ;
This statement provides a null operation in situations where the grammar of C
requires a statement but the program requires no work to be done.
A typical use of the null statement is in loops, as the following example shows:
for(i=0;array[i]!=0;i++)
This piece of code finds the first non zero element of array
Most expressions are either assignments or function calls:
i++;
y=sqrt(x);
printf("Hello World\ n");
Note that i++, y=sqrt(x), printf("Hello World") are
expressions. They only become statements when they are followed by a semi-
colon.
The semicolon in C is a statement terminator rather that a statement separator as
in Pascal.
Example of statement which control the flow of the programme are selection
statements, e.g. the if then else construct, iteration statements, e.g. do
while construct, or jump statements such as return or goto.
-
8/3/2019 C Programming Lecture Notes 2008-09
21/173
21
3.2 Compound Statements or Blocks
A compound statement is a sequence of declarations and statements enclosed in
braces:
{ }.
This group of statements can then be treated syntaxically as a single statement.
For example, the definition of the function gen_sqrt is a compound
statement:
double gen_sqrt(double x)
{
double result;
if(x
-
8/3/2019 C Programming Lecture Notes 2008-09
22/173
22
The statement following the control expression is executed if the value of the
control expression is true (nonzero).
An if statement can be written with an optional else clause that is executed if
the control expression is false (0).
For example:
if (i < 1)
funct(i);
else
{
i = x++;
funct(i);
}
Here, if the value of i is less than 1, then the statement funct(i); is
executed and the (compound) statement following the keyword else is not
executed.
If the value of i is not less than 1, then only the (compound) statement
following the keyword else is executed.
The syntax of the C language requires that the if and then clause be single
statements. This is why a block has to be used when several statements are to be
executed.
The control expression in a selection statement is usually a logical expression,
but it can be any expression of scalar type.
Note: the statement
if (expression)
statement-1
else
statement-2
is equivalent to
-
8/3/2019 C Programming Lecture Notes 2008-09
23/173
23
if (expression!=0)
statement-1
else
statement-2
When if statements are nested, an else clause matches the most recent if
statement that does not have an else clause, and is in the same block.
For example,
if (i < 1)
{
if (j < 1)
funct(j);
if (k < 1) /*This if statement is associated*/funct(k);
else /* with this else clause */
funct(j + k);
}
Braces can be used to force the proper association:
if (b < 3)
{
if (a > 3) b += 1;
} else {
b - = 1 ;
}
A common use of the if then else statement deals with multi-way decisions. The
following construction is used:
if (expression)
statement
else if (expression)
statement
[ else if (expression) statement ]
[ else statement ]
-
8/3/2019 C Programming Lecture Notes 2008-09
24/173
24
The expressions are evaluated in order; if any expression is true, the statement
associated with the expression is executed and this terminates the whole chain.
The last else handles the "none of the above'' case. It can be used for error
handling. It can also be omitted.
3.3.2 The switch Statement
The switch statement executes one or more of a series of cases, based on the
value of a controlling expression.
The switch statement has the following syntax:
switch (expression)
{
case const-expression: statements
[ case const-expression: statements ]
[ default: statements ]
}
The switch statement is a multi-way decision statement where a singleexpression is evaluated.
It the result matches one of a number of constants, a branching is performed to
the statement associated with the constant.
The case labelled default is executed if none of the other cases match. It is
optional.
case and default clauses can occur in any order.
The usual arithmetic conversions are performed on the control expression, but
the result must have an integral type.
The constant expressions must have an integral type. No two case labels can
specify the same value. There is no limit on the number of case labels in a
switch statement.
The following code is a very simple example showing how to use the switch
statement for interfacing purposes.
Depending on the value of a, functions action1 and action2 can be called
or appropriate messages be displayed.
-
8/3/2019 C Programming Lecture Notes 2008-09
25/173
25
The break statement causes the programme to exit from the switch statement
Without the break statements, each case would drop through to the next.
It is possible to have several cases resulting in the same sequence of statements
being executed:
switch(a)
{
case 1: action1();
break;
case 2:
case 3: action2();
break;
case 4: printf("Exit...\n");
break;
default: printf("Incorrect choice\n");
break;
}
-
8/3/2019 C Programming Lecture Notes 2008-09
26/173
26
The following example uses switch to count blanks, tabs, and newlines entered
from the terminal:
#include
int main()
{
int number_tabs = 0;
int number_lines = 0;
int number_blanks = 0;
int ch;
while ((ch = getchar()) != EOF)
switch (ch)
{
case '\t': ++number_tabs;
break;
case '\n': ++number_lines;
break;
case ' ' : ++number_blanks;
break;
default:;
}
printf("Blanks %d\n",number_blanks);
printf("Tabs %d\n",number_tabs);
printf("Newlines %d\n",number_lines);
return 0;
}
Here a series of case statements is used to increment separate countersdepending on the character encountered.
-
8/3/2019 C Programming Lecture Notes 2008-09
27/173
27
3.4 Iteration Statements
An iteration statement, or loop, repeatedly executes a statement, known as the
loop body, until the controlling expression is false (0). The control expression
must have scalar type.
There are three different iteration statements:
The while statement evaluates the control expression before executing the loop
body.
The do statement evaluates the control expression afterexecuting the loop
body: at least one execution of the loop body is guaranteed.
The for statement executes the loop body on the evaluation of the second ofthree expressions.
3.4.1 The while Statement
The while statement evaluates a control expression before each execution of the
loop body. If the control expression is true (nonzero), the loop body is executed.
If the control expression is false (0), the while statement terminates.
The while statement has the following syntax:
while (expression)
statement
For example:
n = 0 ;while (n < 10)
{
a[n] = n;
n++;
}
This statement tests the value of n; ifn is less than 10, it assigns n to the nth
element of the array a and then increments n.
-
8/3/2019 C Programming Lecture Notes 2008-09
28/173
28
The control expression (in parentheses) is then evaluated; if true (nonzero), the
loop body is executed again; if false (0), the while statement terminates.
If the statement n++; were missing from the loop body, this while statement
would not terminate. If the statement n = 0 ; were replaced by the statement n
= 10;, the control expression is initially false (0), and the loop body is never
executed.
Another example is:
while ( n < 1 000)
{
n *= 2;
j += 1;
}
3.4.2 The do Statement
The do statement evaluates the control expression aftereach execution of the
loop body
The do statement has the following syntax:
do
statement
while (expression);
The loop body is executed at least once.
The control expression is evaluated after each execution of the loop body.
If the control expression is true (nonzero), the statement is executed again. If
the control expression is false (0), the do statement terminates.
-
8/3/2019 C Programming Lecture Notes 2008-09
29/173
29
For example:
do
{
n * = 2 ;
j + = 1 ;
}
While (n < 1000);
Example: a typical use of the do statement is checking input data:
do
{
printf("y/n?");
scanf("%c",&c);
}
While((c!='y')&&(c!='n'));
-
8/3/2019 C Programming Lecture Notes 2008-09
30/173
30
3.4.3 The for Statement
The for statement evaluates three expressions and executes the loop body until
the second controlling expression evaluates to false (0).
The for statement is useful for executing a loop body a specified number of
times.
The for statement has the following syntax:
for ([expression-1];[expression-2];[expression-3])statement
The for statement is equivalent to the following while loop:
expression-1;
while (expression-2){
statement
expression-3;
}
The for statement executes the loop body zero or more times.
Semicolons (;) are used to separate the control expressions.
-
8/3/2019 C Programming Lecture Notes 2008-09
31/173
31
A for statement executes the following steps:
1. Expression-1 is evaluated once before the first iteration of the
loop. This expression usually specifies the initial values for variables
used in the loop.
2. Expression-2 is any scalar expression that determines whether to
terminate the loop.
3. Expression-2 is evaluated before each loop iteration. If the
expression is true (nonzero), the loop body is executed. If the expression
is false (0), execution of the for statement terminates.
4. Expression-3 is evaluated after each iteration.
The for statement executes until expression-2 is false (0), or until a jump
statement, such as break or goto, terminates execution of the loop.
Any of the three expressions in a for loop can be omitted:
Ifexpression-2 is omitted, the test condition is always true; that is the
while loop equivalent becomes while(1). This is an infinite loop.
For example:
for (i = 0; ;i++)
statement;
Infinite loops can be terminated with a break, return, or goto statement within
the loop body.
If either expression-1 or expression-3 is omitted from the for
statement, the omitted expression is evaluated as a void expression and iseffectively dropped from the expansion.
-
8/3/2019 C Programming Lecture Notes 2008-09
32/173
32
For example:
n = 1 ;
for ( ; n < 10; n++)
func(n);
Here n is initialised before the for statement is executed.
Infinite loops are often used in practice.
For example:
#include
void action1();
void action2();
int main()
{
int a;
for(;;)
{printf("Enter a choice\n");
printf("\t 1. Action 1\n");
printf("\t 2. Action 2\n");
printf("\t 3. Exit\n");
scanf("%d",&a);
switch(a)
{
case 1: action1();break;
case 2: action2();
break;
case 3: printf("Exit...\n");
return 0;
default: printf("Incorect choice\n");
}
}
return 0;
}
-
8/3/2019 C Programming Lecture Notes 2008-09
33/173
33
/* ------ action routines ------ */
void action1()
{
printf("This is the action1 routine\n");
}
void action2()
{
printf("This is the action2 routine\n");
}
3.5 Jump Statements
Jump statements cause an unconditional jump to another statement elsewhere in
the code.
They tend to be used to interrupt switch statements and loops.
The jump statements are:
the goto statement,
the continue statement,
the break statement,
the return statement.
The break statement terminates execution of the immediately enclosing
while, do, for or switch statement. Control passes to the statementfollowing the loop or switch body.
The syntax for the break statement is
break;
-
8/3/2019 C Programming Lecture Notes 2008-09
34/173
34
3.5.1 The return Statement
The return statement terminates execution of a function and returns control
to the calling function, with or without a return value.
A function may contain a number of return statements.
The return statement has the syntax
return [expression];
If present, the expression is evaluated and its value returned to the calling
function.
Very often, the expression is enclosed in brackets:
return (0);
A return statement with an expression cannot appear in a function whose
return type is void.
If there is no expression and the function is not defined as void, the return value
is undefined.
Reaching the closing brace that terminates a function is equivalent to executing
a return statement without an expression.
3.5.2 The continue Statement
The continue statement passes control to the end of the immediately enclosing
while, do or for statements. It has the following syntax:
continue;
The continue statement can be used only in loops.
A continue inside a switch statement that is inside a loop causes continued
execution of the enclosing loop after exiting from the body of the switch
statement
-
8/3/2019 C Programming Lecture Notes 2008-09
35/173
35
3.5.3 The goto Statement
The goto statement causes unconditional transfer of program control to a
labelled statement.
The label identifier is in the scope of the function containing the goto
statement.
The labelled statement is the next statement executed.
The goto statement has the syntax
goto identifier;
The goto statement is redundant and dangerous.
It is always possible to write equivalent code which does not use any goto
statement.
The equivalent code is often easier to write and usually easier to understand and
easier to maintain because it is more structured.
The only circumstances where a goto statement may be acceptable is the case
of non-trivial error-handling, in a deeply nested code:
for (...)
{
...
for (...)
...
if (disaster)
goto error;
...
...
}
...
error:
/* error handling */
return;
In this example, the use of a goto statement enable a clean exit from both
loops. Here, a break statement would not be sufficient because it would onlyexit from the inner-most loop.
-
8/3/2019 C Programming Lecture Notes 2008-09
36/173
36
4. Functions in C
4.1 Basic Principles and Definitions
A C program is a collection of user-defined and system-defined functions.
Functions provide a useful way to break large computing tasks down into
smaller ones which promotes the design of modular programmes that are easier
to understand and maintain.
A function contains statements to be executed when it is called, can be passed
zero or more arguments, and can return a value.
4.2 Function Calls
A function call is an expression, usually consisting of a function identifier
followed by parentheses used to invoke a function.
The parentheses contain a (possibly empty) comma-separated list of
expressions that are arguments to the function.
For example, the following is a call to the function power (defined
appropriately elsewhere):
int main()
{
...
y = power(x,n); /* function call */
...
return 0;
}
In this example, the value returned by the function power is assigned to the
variable y.
The calling function is also free to ignore the return value of the function.
For example, printf is a function which returns an integer, (the number of
character displayed). This return value is usually ignored as in
int main()
{
printf("Ignored returned value\n");
return 0;
}
-
8/3/2019 C Programming Lecture Notes 2008-09
37/173
37
4.3 Function Types
A function has the derived type function returning type.
The type can be any data type except array types or function types, althoughpointers to arrays and functions can be returned.
If the function returns no value, its type is function returning void', sometimes
called a void function.
A void function in C is equivalent to a procedure in Pascal, or a subroutine in
FORTRAN.
A non-void function in C is equivalent to a function in these other languages.
It is very important for the compiler to know the type returned by any function
the program uses.
This information can be passed to the compiler through a function prototype.
The simplest form of function prototype deals with the case when there is no
argument.
For example: we want to write a C function my_pi which returns the value of
pi using the formula
atan(1.0) = pi/4.
A simple but complete C program defining and using my_pi is:
-
8/3/2019 C Programming Lecture Notes 2008-09
38/173
38
#include
#include
double my_pi();
int main()
{
printf("Value of PI: %18.14f\n",my_pi());
return 0;
}
double my_pi()
{
return (4.0*atan(1.0));
}
Execution gives the following output:
Value of PI: 3.14159265358979
The line: double my_pi();
is the prototype for the function my_pi. It tells the compiler, before the
function is used, that it is a function returning a double and taking no argument.
The purpose of the line #include is to provide the compiler
with the prototype for the function printf.
The effect of this line is to include the file stdio.h in the source code
processed by the compiler. stdio.h is a header file where the prototypes for
the standard input/output functions are given.
Similarly, the purpose of the line #include is to provide the
compiler with the prototype for the function atan.
-
8/3/2019 C Programming Lecture Notes 2008-09
39/173
39
Example: declaration of a void function.
#include
void message();
int main()
{
message();
return 0;
}
void message()
{
printf("Hello World\n");
}
4.4 Parameters and Arguments
C functions exchange information by means of parameters and arguments.
The term parameter refers to any declaration within the parentheses following
the function name in a function declaration or definition.
The term argument refers to any expression within the parentheses of a function
call.
A synonym for argument is actual arguments
A synonym for parameter is formal arguments
Arguments are passed by value; that is, when a function is called, the parameter
receives a copy of the corresponding argument's value, not its address as in
other languages such as FORTRAN.
This is a very important point. Its main consequence is that modifying a
parameter does not modify the corresponding argument passed by the function
call.
Consider the FORTRAN program:
-
8/3/2019 C Programming Lecture Notes 2008-09
40/173
40
program VALUE
integer i
i=0
call routine(i)
write(*,'(A,I3)')'value of i in programme:',i
end
subroutine routine(i)
integer i
i=i+1
write(*,'(A,I3)')'value of i in subroutine:',i
return
end
This produces the following output:
value of i in subroutine: 1
value of i in program: 1
Consider now the seemingly equivalent C program:
#include
void routine(int);
int main()
{
int i;
routine(i);
printf("value of i in programme: %3d\n",i);
return 0;
}
void routine(int i)
{
i=i+1;
printf("value of i in subroutine: %3d\n",i);
return;
}
This produces a different output:
-
8/3/2019 C Programming Lecture Notes 2008-09
41/173
41
value of i in routine: 1
value of i in program: 0
In the FORTRAN program, the modifications to the parameters are mirrored inthe arguments.
In C, these modifications have no effect on the arguments.
C nevertheless provides a mechanism whereby a function can modify a variable
in a calling routine. This necessitates the use of pointers
4.5 Function Definitions
A function definition includes the code for the function.
Function definitions can appear in any order, and in one source file or several.
A function definition has the following syntax:
return-type function-name(parameter declarations, if any){
declarations
statements
}
The type-specifier is the data type of the value returned by the function.
If no return value is specified, the function is declared to return a value of type
int.
A function can return a value of any type except array of type' or function
returning type'. Pointers to arrays and functions can be returned.
-
8/3/2019 C Programming Lecture Notes 2008-09
42/173
42
Consider the following definition of the function power
int power(int base, int exp)
{
int n=1;
if (exp < 0)
{
printf ("Error: negative exponent\n");
return -1;
}
for ( ; exp; exp--) n = base * n;
return n;
}
This function takes two integer parameters and returns an integer value
The sequence power(int base, int exp) is called a function
declarator.
It specifies the name of the function as well as the name and type of the
parameters.
A function definition with no parameters is defined with an empty parameter
list.
An empty parameter list can be specified in two ways:
1. Using the keyword void, if the prototype style is used. For example,
char msg(void)
{
return 'a';
}
2. Using empty parentheses. For example:
Char msg()
{
return 'a';
}
-
8/3/2019 C Programming Lecture Notes 2008-09
43/173
43
4.6 Function Prototypes
A function prototype is a function declaration that specifies the data types of
the function parameters in the parameter list, as well as the return type of the
function.
The compiler uses the information in a function prototype to ensure that:
1. the corresponding function definition and all corresponding function
declarations and calls (within the scope of the prototype) contain the
correct number of arguments or parameters,and that each argument or
parameter is of the correct data type.
2. the return value is consistent for all corresponding declarations as well
as the definition.
The function declaration used is the prototype style
Prototype Style:
Functions are declared explicitly with a prototype before they are called.
Multiple declarations must be compatible; parameter types must agree
exactly.
Arguments to functions are converted to the declared types of theparameters.
The number and type of arguments are checked against the prototype and
must agree with or be convertible to the declared types.
Empty parameter lists are designated using the void keyword.
Ellipses ... are used in the parameter list of a prototype to indicate that a
variable number of parameters are expected.
Consider the following definition:
char func(int lower, int *upper, char (*func)(), double y )
{}
The corresponding prototype declaration for this function is:
char func(int lower, int *upper, char (*func)(), double y);
-
8/3/2019 C Programming Lecture Notes 2008-09
44/173
44
A prototype is identical to the header of its corresponding function definition
specified in the prototype style, with the addition of a terminating semicolon (;) or
comma (,) at the end, as appropriate (depending on whether the prototype is
declared alone or in a multiple declaration).
Function prototypes need not use the same parameter identifiers as in thecorresponding function definition because identifiers in a prototype only have
scope within the identifier list.
For example, the following prototype declarations are equivalent:
char func(int lower, int *upper, char (*func)(), double y);
char func(int a, int *b, char (*c)(), double d );
char func(int, int *, char (*)(), double );
The last example shows that identifiers themselves need not be specified in the
prototype declaration
4.7 Argument Conversion
If a prototype is in scope, arguments are converted to the type of the
corresponding parameters.
If no prototype is in scope, integral types are converted to int, and floating
point types are converted to double.
-
8/3/2019 C Programming Lecture Notes 2008-09
45/173
45
4.8 Recursive Functions
A recursive function is a function is a function that is defined in terms of itself.
It is also called a self-referential function.
A recursive function in C is a function whose definition contains a call to the
function itself.
Perhaps the simplest example of a recursive function is that given by the
factorial function:
n! =
1 if n>0
n(n-1)! otherwise
Here n! is defined in terms of (n-1)!. Note that when the function refers to itself,
it uses an argument smaller than the one it was given.
In addition the value of the function for the smallest argument is defined
without self-reference, so that the function terminates.
Recursive functions can be implemented directly in C. For example, the
factorial function can be written as:
if (n == 0)
return 1;
else
return n * factorial(n-1);
This is often called tail recursion, because the last statement contains a single
recursive call.
The important point is that sometimes the recursive divide and conquer
approach provides a neater and more efficient solution to a problem than the
iterative approach.
Note that recursive functions have iterative equivalents; for the factorial
function this is:
-
8/3/2019 C Programming Lecture Notes 2008-09
46/173
46
int factorial(int n)
{
if (n == 0)
return 1;
else
{
int i, fact = 1;
for (i = 2; i
-
8/3/2019 C Programming Lecture Notes 2008-09
47/173
47
The idea behind recursive algorithms is to divide the original problem up into
problems of smaller size.
In general, recursive functions may divide the problem up into many sub-
problems and make several recursive calls in the process of recombining the
sub-problems, before generating the solution.
This approach is often called divide and conquerand usually leads to much
simpler solution of the problem than that taken by an iterative approach.
A good example where a recursive approach is also more efficient is provided
by the problem of simultaneously finding the maximum and minimum of an
array of integers.
The iterative solution can be written as follows:
void minmax(int a[], int n, int *p_max, int *p_min)
{
int i;
*p_max = *p_min = a[0];
for (i = 1; i < n; i++)
{if (a[i] > *p_max)
*p_max = a[i];
if (a[i] < *p_min)
*p_min = a[i];
}
}
The comparison count for this function is 2(n-1):
since at each of the n-1 iterations of the loop, 2 comparisons are made.
-
8/3/2019 C Programming Lecture Notes 2008-09
48/173
48
The recursive approach can be summarised as follows:
ifn =1
min = max = a[0];
ifn = 2 compare a[0] and a[1] and assign min the smallerand max the larger
else
divide the array in two, and recursively find
the min and max of each half; assign min the
smaller of the two min's, and max the larger
of the two max's.
The C implementation is:
void minmax(int numberlist, int n, int *p_min, int *p_max){
int min2, max2;
if (n == 1)
*p_min = *p_max = numberlist[0];
else if (n == 2)
{
if (numberlist[0] < numberlist[1])
{
*p_min = numberlist[0];*p_min = numberlist[1];
}
else
{
*p_min = numberlist[1];
*p_max = numberlist[0];
}
}
else
{minmax(numberlist, n/2, p_min, p_max);
minmax(numberlist + n/2, n - (n/2), &min2, &max2);
if (min2 < *p_min)
*p_min = min2;
if (max2 > *p_max)
*p_max = max2;
}
}
Although not obvious, this algorithm makes slightly less comparisons.
-
8/3/2019 C Programming Lecture Notes 2008-09
49/173
49
The Tower of Hanoi problem is another problem that can be solved recursively.
In C, the solution is given by the following program:
int main()
{
int n;
printf("Input the number of disks: ");
scanf("%d", &n);
if (n
-
8/3/2019 C Programming Lecture Notes 2008-09
50/173
50
5. The C Preprocessor
5.1 Concept of Preprocessor
Preprocessing is the first step in the compilation process. It is optional.
According to directives, the pre-processor modifies the source code before it is
passed to the compiler proper.
The C preprocessor provides the ability to perform macro substitution,
conditional compilation, and the inclusion of named files.
Preprocessor directives are lines beginning with #.
5.2 Macro Definition (#define)
The #define directive specifies a macro identifier and a replacement list, and
terminates with a newline character.
The replacement list, a sequence of preprocessing tokens, is substituted for
every subsequent occurrence of that macro identifier in the program text, unless
the identifier occurs inside a character constant, a comment, or a literal string.
#define is often used to define constants or array sizes.
For example:
#define PI 3.14159
#define TRUE 1
#define FALSE 0
#define NMAX 100
int main()
{
double a[NMAX];
...
return 0;
}
-
8/3/2019 C Programming Lecture Notes 2008-09
51/173
51
The #undef directive is used to cancel a definition for a macro.
A macro definition is independent of block structure, and is valid from the
#define directive that defines it until either a corresponding #undef
directive or the end of the compilation unit is encountered.
The #define directive has the syntax: #define identifier replacement-list
new-line
#define identifier (identifier-list) replacement-list new-line
The first form of the #define directive is called an object-like macro. The
second form is called a function-like macro.
The #undef directive has the following syntax:
#undef identifier newline
The replacement list in the macro definition, as well as arguments in a function-
like macro reference, can contain other macro references.
The following example shows nested #define directives:
/* Show multiple substitutions and listing format.
*/
#define AUTHOR james + LAST
int main()
{
int writer,james,michener,joyce;
#define LAST michener
writer = AUTHOR;
#undef LAST#define LAST joyce
writer = AUTHOR;
return 0;
}
After this example is compiled with the appropriate options to show
intermediate macro expansions, the following listing results:
-
8/3/2019 C Programming Lecture Notes 2008-09
52/173
52
/* Show multiple substitutions and listing format.
*/
#define AUTHOR james + LAST
int main()
{
1 int writer, james, michener, joyce;
2 #define LAST michener
3 writer = AUTHOR;
4 james + LAST
5 michener
6 #undef LAST
7 #define LAST joyce
8 writer = AUTHOR;
9 james + LAST
10 joyce
return 0;
}
On the first pass, the compiler replaces the identifier AUTHOR with the
replacement list james + LAST.
On the second pass, the compiler replaces the identifier LAST with its currently
defined replacement list value.
At line 5, the replacement list value for LAST is the identifier michener,
so michener is substituted at line 6.
At line 7, the replacement list value for LAST is redefined to be the
identifier joyce, so joyce is substituted at line 8.
The #define directive can be continued on to subsequent lines if necessary.To do this, end each line to be continued with a backslash \ immediately
followed by a newline character.
The first character in the next line is logically adjacent to the character that
immediately precedes the backslash.
-
8/3/2019 C Programming Lecture Notes 2008-09
53/173
53
5.3 Function-Like Form
The function-like form of macro definition includes a list of parameters.
References to such macros look like function calls.
When a function is called, control passes from the program to the function at
run time; when a macro is referenced, source code is inserted into the program
at compile time. The parameters are replaced by the corresponding arguments,
and the text is inserted into the program stream.
Consequently, for small amount of computing work, macros are more efficient
than functions because they do not have the overheads of argument passing.
The library macro _trouper, available on some systems in the ctype.h header
file, is a good example of macro replacement. The macro is defined as follows:
#define _trouper(c) ((c) >= 'a' && (c)
-
8/3/2019 C Programming Lecture Notes 2008-09
54/173
54
Consider the following macro defining the square of an expression:
#define SQUARE(A) A * A
This implementation will give in wrong results for some expressions such as:
y=SQUARE(x+1);
which after macro expansion is interpreted by the compiler as
y=x+1*x+1=2*x+1
instead of:
y=(x+1)*(x+1)
The correct implementation of the SQUARE macro is:
#define SQUARE(A) ((A)*(A))
Similarly, unwanted side-effects can occur if the increment (++), decrement (--
), and assignment operators (such as +=) are used in macro invocation.
For example:
j=SQUARE(i++);
will result in i being incremented twice; j will also be assigned a wrong
value.
The solution is obviously:
i++;
j=SQUARE(i);
-
8/3/2019 C Programming Lecture Notes 2008-09
55/173
-
8/3/2019 C Programming Lecture Notes 2008-09
56/173
56
6. Arrays
6.1 Basic Definition and Arrays Declarations
Arrays are a derived type because they are defined in terms of another type.
Typically, arrays are used to perform operations on some homogeneous set of
values.
The declaration of an array follows the following pattern: completed-type
identifier[[ const-size ]];
Example:
int a[10]; defines (i.e. declares and allocates corresponding storing
space in memory) an array of 10 consecutive integer, identified by the
name a
double x[10],y[10]; defines two arrays, x and y, of 10 doubles
each.
In C, arrays of any completed type can be declared. These include:
1. fundamental types
2. pointers
3. structures and union
4. arrays
A completed type is a type whose size is known from the compiler. The
restriction that array have to be defined in term of a completed type arises
because the compiler needs to know the size of an array element in order toaccess individual elements.
-
8/3/2019 C Programming Lecture Notes 2008-09
57/173
57
6.2 Referencing an Individual Array Element
A particular element of an array is referenced using the [ ] notation.
Given a declaration such as int a[10];, a[i] is an expression which value
is the ith element of array a
In C, arrays start at index 0. Given the declaration int a[SIZE];, elements
a[0], a[1], ..., a[SIZE-1] are correct.
For example: The following program displays the maximum value of an array
of floating point values
#include
#define SIZE 10
int main()
{
double a[SIZE];
double max;
int i,n;
printf("Enter the number of values >");
scanf("%d",&n);if ((n >SIZE)||(n
-
8/3/2019 C Programming Lecture Notes 2008-09
58/173
58
C is a very "lazy'' language. It does not check many things (unlike Pascal).
There is no mechanism for checking that the index used for referencing an array
element is within the bounds of the array.
Given the declaration int a[SIZE];, expressions such as a[-1] or
a[SIZE+100] are valid. The compiler assumes the programmer knows what
he/she is doing!
6.3 Incomplete Array Declarations
Declarations such as int x[]; can be valid in C.
Because the size of the array is unknown (at compilation time), this is referred
to as an incomplete array declaration.
The size of an array declared in this way must be specified elsewhere in the
programme.
This mechanism can be useful in the context of functions which deal with
arrays.
For example, if we want to write a function which computes the average of the
values of an array, the size of the array is nothing more than a parameter which
is important at run-time, not at compile time.
The following function definition satisfies this constraint:
double average(double a[],int n)
{
double sum,average;
int i;
sum=0.0;
for(i=0;i
-
8/3/2019 C Programming Lecture Notes 2008-09
59/173
59
The corresponding main program is the following:
#include
#define SIZE 10
double average(double [],int);
int main()
{
double a[SIZE];
int i,n;
printf("Enter the number of values >");
scanf("%d",&n);
if ((n >SIZE)||(n
-
8/3/2019 C Programming Lecture Notes 2008-09
60/173
60
6.4 Multi-Dimensional Arrays
An array in C has only one dimension.
Multi-dimensional arrays can be created by declaring arrays of arrays.
For example, the declaration:
double a[NROW][NCOL];
declares an NROW by NCOL matrix of double.
Internally, because the [ ] operator has left to right associativity, the
declaration is interpreted as:
double (a[NCOL])[NROW];
In other words, a is an one-dimensional array of NROW objects of type "array of
NCOL doubles''.
In memory, this result in the array being stored row after row. This is calledrow major order.
For example: consider the declaration int a[2][3]; the elements are stored
in memory in the following order:
a[0][0], a[0][1], a[0][2], a[1][0], a[1][1]
a[1][2]
Note: FORTRAN uses the converse convention: the column major orderwhere
multi-dimensional arrays are stored column by column.
Given the declaration a[NROW][NCOL]; the expression a[i][j] has the
value of the array element at the intersection of the ith
row and the jth
column.
The order in which a multi-dimensional array is stored is important if anefficient code is to be written.
It is common to have nested loop to access all the elements of the array
successively. The correct looping order in C is
for(i=0;i
-
8/3/2019 C Programming Lecture Notes 2008-09
61/173
61
By contrast, but for the same reasons, the correct order in FORTRAN is the
reverse:
do 10 j=1,ncol
do 20 i=1,nrow
call func(a(i,j))
20 continue
10 continue
-
8/3/2019 C Programming Lecture Notes 2008-09
62/173
62
7. Program Structure in C
7.1 External Variables
An internal variable is a variable which is defined within a function. Therefore,
it cannot be used outside thatparticular function because it has no meaning
everywhere else in the programme.
By contrast, an external variable is a variable which is defined outside any
function.
Because they are defined outside any function, external variables are available
to many functions.
For example:
double total;
void add(double x)
{
total+=x;
}
void subtract(double x)
{
total-=x;
}
Here, both functions operate on the external variable total.
External variables are useful when a set of functions operate on the same group
of data.
Because data is accessible to all the functions, argument lists are shorter.
The typical example is the C implementation of a stack.
External variables are nevertheless to be used with caution because they can
yield unnecessary data dependencies causing a loss of generality in functions.
-
8/3/2019 C Programming Lecture Notes 2008-09
63/173
63
For example: functions
void add2(double x,double *p_total)
{
*p_total+=x;
}
void subtract2(double x,double *p_total)
{
*p_total-=x;
}
are much more general than add and subtract because they are not restricted to
operating on a single variable total.
7.2 Storage Classes for Variables
The storage class of a variable determines the lifetime of the variable.
There are two storage classes:
1.automatic variables
2.static variables
7.2.1 Automatic variables
All the variable declarations we have been using so far have been automatic
variables.
An automatic variable is a variable that comes into existence only when the
block within which they are declared is executed. They disappear when the
block is exited.
Consequently:
1. an automatic variable can only be declared within a function/block, and
more precisely, at the beginning of the block.
2. an automatic variable is private to the function in which it is declared.
3. an automatic variable does not retain its value from one call to the other.
4. an automatic variable has to be initialised explicitly at each entry of the
function (otherwise it will contain garbage).
-
8/3/2019 C Programming Lecture Notes 2008-09
64/173
64
Automatic variables are often allocated on the stack.
An automatic variable is declared by using fundamental-type declarator;
For example:
int i;
char char[10];
Variables with block scope, i.e. variables that are declared at the beginning of a
block have automatic storage class by default.
7.2.2 Register Variables
Registervariables are automatic variables. However, their declaration hint to
the compiler that these variables will be often used.
As a result, the compiler may assign them storage in one of the registers of the
mp for faster access. However, this is not guaranteed.
A register variable is declared in the following way:
register fundamental-type declarator;
It is not possible to apply the unary operator & to a register variable, whether or
not it is actually stored in a register.
-
8/3/2019 C Programming Lecture Notes 2008-09
65/173
65
7.2.3 Static Variables
Static variables are in existence throughout the duration of the programme.
A static object can be declared anywhere a declaration may appear in the
programme:
1. inside a block, at the beginning of the block.
2. anywhere outside of a block
Static variables are declared using the static keyword:
static fundamental-type declarator;
static variables are initialised only once at the beginning of the programme. The
initialisation expression must be a constant.
If a static variable is not explicitly initialised, every arithmetic member of that
object is initialised to 0 and every pointer member is initialised as a NULL
pointer constant.
By default, variables declared outside any block have the static storage class.
Example : Consider the program: because a is auto while b is static:
#include void func(int);
int main()
{
int n=3;
int i;
for(i=1;i
-
8/3/2019 C Programming Lecture Notes 2008-09
66/173
-
8/3/2019 C Programming Lecture Notes 2008-09
67/173
67
7.3.1 Block Scope
An identifier appearing within a block or in a parameter list of a function
definition has block scope and is visible within the block, unless hidden by an
inner block declaration.
Block scope begins at the identifier declaration and ends at the closing brace }
completing the block.
Note: variables used in a block must be declared at the beginning of the block.
Example:
int func(void)
{
int i;
double func2(void);
...
}
Both variable i and the prototype for function func2 have block scope.
Example:
int main()
{
int i=0;
i++;
if(1) /* always true */
{
int i=2;
i++;
printf("i at end of block 2: %d\n",i);
}
printf("i at end of block 1: %d\n",i);
return 0;
}
This program will produce the output: i at end of block 2: 3i at end of block 1: 1
-
8/3/2019 C Programming Lecture Notes 2008-09
68/173
68
7.3.2 File Scope
An identifier whose declaration is located outside any block or function
parameter list has file scope.
An identifier with file scope is visible from the declaration of the identifier tothe end of the compilation unit (i.e. the file being compiled), unless hidden by
an inner block declaration.
In the example below, the identifier off has file scope:
int off = 5; /* Defines the integer identifier off. */int f(void); /* prototype for function f */int main ()
{
int on; /* Declares the integer identifier on. */
o n = o f f + 1 ;
/* Uses off, declared outside the function block
of main. This point of the program is still
within the active scope of off. */
on=f();/* call to f is checked by the compiler becausethe prototype is in scope */
if (on
-
8/3/2019 C Programming Lecture Notes 2008-09
69/173
69
7.3.3 Function Scope
Only statement labels have function scope.
An identifier with function scope is unique throughout the function in which it
is declared.
Labelled statements are used as targets for goto statements. They should
therefore only very seldom used!
Labelled statements are declared implicitly by their syntax: label : statement
For example:
int func1(int x,int y,int z)
{
label: x+=(x+y);
if(x >1) goto label;
}
7.3.4 Function Prototype Scope
An identifier that appears within a function prototype's list of parameter
declarations has function prototype scope: the scope of this identifier begins at
the identifier declaration and terminates at the end of the function prototype
declaration list.
-
8/3/2019 C Programming Lecture Notes 2008-09
70/173
70
7.4 Linkage
C was designed so that programs could easily be built by linking together
groups of functions that had been compiled separately.
Technically, a C programme is said to consist of one or several compilation
units.
A compilation unit is a file containing C source code that can be independently
processed by the compiler.
If we consider again the example of the generalised square root program and
write the gen_sqrt function in a file gen_sqrt.c and the main function in
a file tgen_sqrt.c, our program is made of two compilation units which can
be compiled separately (for example, by typing the commands
cc -c gen_sqrt.c
cc -c tgen_sqrt.c
on a UNIX system) and then linked together to build the executable file (forexample, by typing:
cc -o tgen_sqrt tgen_sqrt.o gen_sqrt.o -lm
on a UNIX system - note that -lm includes the math library; this has to be done
explicitly on a UNIX system. However, it is done implicitly on most othersystems)
The fact that a program can be made of several compilation units means that
there is a need for the compiler to distinguish between internal and external
objects: this is called the linkage of the object.
The linkage is a property of both static variables and functions.
-
8/3/2019 C Programming Lecture Notes 2008-09
71/173
71
7.4.1 Linkage for variables
Linkage properties only make sense for static variables. Since automatic
variables are private to a function, linkage issues do not arise.
A static variable with external linkage can be referenced by other compilationunits (provided it is in scope).
A variable with internal linkage can only be referenced within the compilation
unit (provided it is in scope).
external variables, i.e. variables defined outside any function, have external
linkage by default.
A static variable can be given internal linkage by using the keyword static:
static type identifier;
Example: total has external linkage and can be referenced by other compilation
units:
double total;
void add(double x)
{
total+=x;
}
void subtract(double x)
{
total-=x;
}
Example: total has internal linkage and cannot be referenced by other
compilation units:
static double total;
void add(double x)
{
total+=x;
}
void subtract(double x)
{
total-=x;}
-
8/3/2019 C Programming Lecture Notes 2008-09
72/173
72
Internal linkage provides added data security. In a self-contained application
such as a stack, it ensures that other routines are not allowed to tamper with the
data manipulated by the group of functions in the compilation unit.
7.5 Variable Declaration and Variable Definition
A variable cannot be referenced within a compilation unit unless it is in scope,
i.e. it has to be declared.
The declaration of variables with external linkage raises some issues. Consider
for example the following code split into two compilation units:
/* compilation unit 1 */
double var;
void func1()
{
...
var=...;...
}
and
/* compilation unit 2 */
double var;
void func2()
{
...
func3(var);
...
}
-
8/3/2019 C Programming Lecture Notes 2008-09
73/173
73
These functions are intended to manipulate the same variable var. In fact, this is
a wrong implementation. var will be associated with a different address in
memory in each compilation unit because it is defined twice.
When a variable is defined, it is not only in scope so that it can be referenced
but it is also allocated storing space.
Consider the correct implementation:
/* compilation unit 1 */
double var;
void func1()
{
...
var=...;
...
}
and
/* compilation unit 2 */
extern double var;
void func2()
{
...
func3(var);
...
}
Here, func1 and func2 correctly manipulate the same variable var.
This is because we have:
1. a variable definition in the first compilation unit (line double
var;)
2. a variable declaration in the second compilation unit (line extern
double var;)
-
8/3/2019 C Programming Lecture Notes 2008-09
74/173
74
A variable declaration puts the variable in scope but does not allocate storing
space for it.
The keyword extern informs the compiler that the variable declared is defined
elsewhere. Its syntax is: extern type list-of-declarators;
Because storing space is not allocated, incomplete type are allowed in variable
declarations.
For example:
/* compilation unit 1 */
double a[10]; /* definition */
void func1()
{
...
var=...;
...
}
and
/* compilation unit 2 */
extern a[]; /* declaration */
void func2()
{
...func3(var);
...
}
Every variable with external linkage must be defined only once and declared in
all other units.
-
8/3/2019 C Programming Lecture Notes 2008-09
75/173
75
7.5 Linkage of Functions
Like static variables, functions have external linkage by default: they can be
called within any compilation unit which makes the program.
A function can be given internal linkage, i.e. it can only be called within thecompilation unit by using the keyword static:
The syntax for the definition of an function with internal scope is:
static type function-designator function-body;
It should also be prototyped in the following way: static type function-
designator;
Functions with internal linkage are useful to protect internal functions (i.e. lowlevel functions used by higher level functions within the compilation unit) from
outside functions.
Note that the distinction between definition and declaration exists also for
function. A prototype is a function declaration.
This is why the extern keyword can be used with function prototypes to inform
the compiler that the function is defined in another compilation unit.
For example: consider the two compilation units:
/* compilation unit 1 */
double func1(void)
{
...
}
and
/* compilation unit 2 */
extern double func1(void);
int main()
{
...
}
extern is, however, optional for function declarations.
-
8/3/2019 C Programming Lecture Notes 2008-09
76/173
76
7.6 Header Files
A header file is a file which contains information that has to be shared by
several source files.
Header files typically contains:
1. function prototypes
2. constants (macro) definitions
3. type definitions
4. global variables
They are included into the compilation stream by the pre-processor directive#include
The C has the following standard header files:
locale.h>
It is good programming practice to create header files because they are a
convenient and safe way to insure uniform declarations of global objects for all
the files which make up a particular applications.
-
8/3/2019 C Programming Lecture Notes 2008-09
77/173
77
8. Pointers
8.1 Concept of Pointers: Pointers and Addresses
A pointer is a variable which contains the address of another variable (it points'
to another variable).
Note that a pointer is a variable. In other words, it is a portion of memory
where the address of another memory cell can be stored.
For example: the 6501 mp can manage up to 64K-bytes of memory. Addresses
are integer numbers between 0 and 0xFFFF (in hexadecimal notation). An
address needs two bytes to be stored. On a 6501-based machine, a pointer
variable will be a group of two continuous bytes in which any address can be
stored.
In the following example, p is a pointer variable that points' to the variable
letter (which contains the character 'A').
On a typical PC, a char is stored in 1 byte and a pointer needs 2 bytes.
This example is expressed in C in the following way
char letter='A';
char *p;
p = &letter;
Line 1 contains a variable declaration: letter is a variable of type char
Line 2 is also a variable declaration: it declares p as a pointer to a char.
Finally, in line 3, p is assigned with the address of letter
If we suppose that on a particular machine, with a particular compiler, the
variable letter is associated with the address 0xFF0, the pointer p will contain
the value 0xFFF0
-
8/3/2019 C Programming Lecture Notes 2008-09
78/173
78
8.2 Declaration of Pointer Variables
The pointer type is a derived type. A pointer variable points to variable of a
particular type.
For reasons that will become clear later, it is important that the compiler knowsthe size of the object pointed to by a pointer.
However, whatever the object it points to, a pointer is always the same size
because it always contains the
same thing: an address.
Pointer declarations follow the pattern: type *variable-name;
8.3 The & Operator
The address of letter is determined by using the & operator. This returns a
pointer value that can be assigned to a pointer variable or used as a parameter.
A good example is the library function scanf:
scanf("%d, &x);
Here scanf is given the address ofx as its second argument, so that it can
place the value it reads at that address.
Using the & argument with scanf is an example of how pointers can be used tohave function calls by reference in a C programme instead of function calls by
value which is the default.
8.4 The * Operator
To access the memory location to which a pointer variable "points'', the * prefix
operator is used on the pointer. For example:
*p = 'B';
This C statement stores the code for character 'B' at the address given by the
pointer p, i.e. at the memory location pointed to by p.
The * operator is also called the dereferencing or indirection operator.
One way of defining the * operator is to interpret it as the instruction follow the
arrow'. Then *p' can be thought of as follow the arrow stored in p'.
-
8/3/2019 C Programming Lecture Notes 2008-09
79/173
79
If, before the statement *p='B'; there is a statement such as
p=&letter;
*p and letter then refer to the same contents of the same memory
location.
Hence, the statement *p='B'; is rigorously equivalent to letter='B';.
*p and letter are not merely equal, but refer to the same physical object in the
computer's memory.
Hence, changing the value of*p alters the value of letter as the following
example shows:
#include
int main()
{
char letter, *p;
letter='A';
printf("letter before: %c\n",letter);
p=&letter
*p='B';
printf("letter after: %c\n",letter);
return 0;
}
The output of the programme is the following:
letter before: Aletter after: B
This is one reason why it is necessary to declare pointer variables as pointers to
a particular type. When a reference to *p is made, the computer then knows
what kind of data to expect.
-
8/3/2019 C Programming Lecture Notes 2008-09
80/173
80
Another example:
int main()
{
char letter, *p;
char character;
p = &letter
letter = 'A';
printf("letter=%c,*p=%c\n",letter,*p);
*p = 'B';
printf("letter=%c,*p=%c\n",letter,*p);
p = &character
*p = 'Z';
printf("letter=%c,*p=%c,character=%c\n",letter,
*p,character);
return 0;
}
The output of the program is
letter=A,*p=Aletter=B,*p=B
letter=B,*p=Z,character=Z
8.5 Pointers to Pointers and Pointer Casting
Because pointers are "normal'' variables, it is possible to have pointers to
pointers.
For instance: double **a; declares a as a pointer to a pointer to a double.
This construction is very useful in C and will be used later.
It is also possible to cast a pointer of a certain type into a pointer to another
type.
For example:
char *charpt;
int *intpt;
intpt=(int *)charpt;
-
8/3/2019 C Programming Lecture Notes 2008-09
81/173
81
8.6 Pointers and Function Arguments: Difficulties with calls by value
When a function is called, memory space for the declared parameters is
allocated, and the values of the arguments are copied into that space.
This form of parameter passing is known as call by value.
Memory space is then allocated for the local variables used in the function, and
execution begins.
The fact that the values of the arguments are copied into the space used by the
parameters means that a function cannot alter the argument values used in the
calling routine.
When a function finishes execution, all its parameters are destroyed and their
memory space returned to the available memory pool.
For example, the following swap program does not work as intended:
#include
void swap(int,int);
int main()
{int x, y;
x = 0 ;
y = 1 ;
printf("in main before swap: x = %d, y=%d\n", x, y);
swap(x, y);
printf("in main after swap: x = %d, y=%d\n", x, y);
return 0;
}
void swap(int x,int y)
{
int temp;
printf("in swap at start, x = %d and y = %d\n", x, y);
temp = x;
x = y ;
y = temp;
printf("in swap at end, x = %d and y = %d\n", x, y);
}
-
8/3/2019 C Programming Lecture Notes 2008-09
82/173
82
Values are swapped in the function but not in the main program:
in main before swap: x = 0, y=1
in swap at start, x = 0 and y = 1
i n s w a p a t e n d , x = 1 a n d y = 0
in main after swap: x = 0, y=1
This is because parameter and arguments refer to different variables; swap only
swaps copies of the arguments.
8.7 Use of Pointers to Implement Call by Reference
It is possible for a function to modify a variable if a pointer to that variable is
passed to it.
It is thus possible to write a new swap2 of the swapping subroutine that will
work properly:
#include
void swap2(int *,int *);
int main()
{
int x, y;
x = 0 ;
y = 1 ;printf("in main before swap: x = %d, y=%d\n", x, y);
swap2(&x, &y);
printf("in main after swap: x = %d, y=%d\n", x, y);
return 0;
}
void swap2(int *p_x, int *p_y)
{int temp;
printf("in swap at start,x=%d and y=%d\n",*p_x,*p_y);
temp = *p_x;
*p_x = *p_y;
*p_y = temp;
printf("in swap at end, x=%d and y=%d\n",*p_x,*p_y);
}
-
8/3/2019 C Programming Lecture Notes 2008-09
83/173
83
Instead of passing x and y, the addresses ofx and y are passed. These are
copied into the pointerparameters p_x and p_y used by swap2.
As a result, p_x and p_y will point to x and y.
The situation at the start of swap2 can be represented in the following way:
Within the function swap2, the expressions *p_x and *p_y refer the function
arguments themselves, not copies.
This is why the lines:
*p_x = *p_y;
*p_y = temp;
are modifying the arguments x and y themselves.
The results, this time, are correct:
in main before swap: x = 0, y=1
in swap at start, x = 0 and y = 1
in swap at end, x = 1 and y = 0
in main after swap: x = 1, y=0
8.8 Using Pointers for "Returning'' Function Results
Since the return statement can only return one value, pointers can be used as
parameters when a function computes two or more values, which it needs to
communicate to the calling function.
When a pointer is used, the function does not, strictly speaking, return a value:rather it is abl