You are on page 1of 216

C Programming

Welcome to C!
Course Objectives

 Be able to read and write C programs


 Understand all C language constructs
 Be able to use pointers
 Have a good overview of the Standard Library
 Be aware of some of C’s traps and pitfalls
Practical Exercises

 Practical exercises are a very important part of


the course
 An opportunity to experience some of the traps
first hand!
 Solutions are provided, discuss these amongst
yourselves and/or with the tutor
 If you get stuck, ask
 If you can’t understand one of the solutions, ask
 If you have an alternative solution, say
Features of C

 C can be thought of as a “high level assembler”


 Designed for maximum processor speed
 Safety a definite second!
 THE system programming language
 (Reasonably) portable
 Has a “write only” reputation
History of C

 Developed by Brian Kernighan and Dennis


Ritchie of AT&T Bell Labs in 1972
 In 1983 the American National Standards Institute
began the standardisation process
 In 1989 the International Standards Organisation
continued the standardisation process
 In 1990 a standard was finalised, known simply
as “Standard C”
 Everything before this is known as “K&R C”
Standard C vs K&R C

 Parameter type checking added


 Proper floating point support added
 Standard Library covered too
 Many “grey areas” addressed
 New features added

 Standard C is now the choice


 All modern C compilers are Standard C
 The course discusses Standard C
A C Program

tells compiler about standard input and output functions (i.e. printf + others)

#include
#include <stdio.h>
<stdio.h> /*
/* comment
comment */
*/
main function
int
int main(void)
main(void)
{{
printf("Hello\n");
printf("Hello\n");
“begin” printf("Welcome
printf("Welcome to
to the
the Course!\n");
Course!\n");

return
return 0;
0;
}}
flag success Hello
Hello
to operating “end” Welcome
Welcome to
to the
the Course!
Course!
system
The Format of C

 Statements are terminated with semicolons


 Indentation is ignored by the compiler
 C is case sensitive - all keywords and Standard
Library functions are lowercase
 Strings are placed in double quotes
 Newlines are handled via \n
 Programs are capable of flagging success or
error, those forgetting to do so have one or other
chosen randomly!
Another Example

create two integer


#include
#include <stdio.h>
<stdio.h>
variables, “a” and “b”
int
int main(void)
main(void)
{{
int
int a,
a, b;
b;
read two integer
numbers into “a” printf("Enter
printf("Enter two
two numbers:
numbers: ");
");
and “b” scanf("%i
scanf("%i %i",
%i", &a,
&a, &b);
&b);

printf("%i
printf("%i -- %i
%i == %i\n",
%i\n", a,
a, b,
b, aa -- b);
b);
write “a”, “b” and “a-b” return
return 0;
0;
in the format specified }
} Enter
Enter two
two numbers:
numbers: 21
21 17
17
21
21 -- 17
17 == 44
Variables

 Variables must be declared before use


immediately after “{”
 Valid characters are letters, digits and “_”
 First character cannot be a digit
 31 characters recognised for local variables
(more can be used, but are ignored)
 Some implementations recognise only 6
characters in global variables (and function
names)!
 Upper and lower case letters are distinct
printf and scanf

 printf writes integer values to screen when %i


is used
 scanf reads integer values from the keyboard
when %i is used
 “&” VERY important with scanf (required to
change the parameter, this will be investigated
later) - absence will make program very ill
 “&” not necessary with printf because current
value of parameter is used
Integer Types in C

 C supports different kinds of integers


 maxima and minima defined in “limits.h”
type format bytes minimum maximum
char %c 1 CHAR_MIN CHAR_MAX
signed char %c 1 SCHAR_MIN SCHAR_MAX
unsigned char %c 1 0 UCHAR_MAX
short [int] %hi 2 SHRT_MIN SHRT_MAX
unsigned short %hu 2 0 USHRT_MAX
int %i 2 or 4 INT_MIN INT_MAX
unsigned int %u 2 or 4 0 UINT_MAX
long [int] %li 4 LONG_MIN LONG_MAX
unsigned long %lu 4 0 ULONG_MAX
Integer Example

#include
#include <stdio.h>
<stdio.h>
#include
#include <limits.h>
<limits.h>
int
int main(void)
main(void)
{{
unsigned
unsigned long
long big
big == ULONG_MAX;
ULONG_MAX;
printf("minimum
printf("minimum int
int == %i,
%i, ",
", INT_MIN);
INT_MIN);
printf("maximum
printf("maximum int
int == %i\n",
%i\n", INT_MAX);
INT_MAX);
printf("maximum
printf("maximum unsigned
unsigned == %u\n",
%u\n", UINT_MAX);
UINT_MAX);
printf("maximum
printf("maximum long
long int
int == %li\n",
%li\n", LONG_MAX);
LONG_MAX);
printf("maximum
printf("maximum unsigned
unsigned long
long == %lu\n",
%lu\n", big);
big);
return
return 0;
0; minimum int = -32768, maximum int = 32767
}} minimum int = -32768, maximum int = 32767
maximum
maximum unsigned
unsigned == 65535
65535
maximum
maximum long
long int
int == 2147483647
2147483647
maximum
maximum unsigned
unsigned long
long == 4294967295
4294967295
Character Example
Note: print integer
value of character
#include
#include <stdio.h>
<stdio.h>
#include <limits.h>
#include <limits.h>
int
int main(void)
main(void)
{{
char
char lower_a
lower_a == 'a';
'a';
char
char lower_m
lower_m == 'm';
'm';
printf("minimum
printf("minimum char
char == %i,
%i, ",
", CHAR_MIN);
CHAR_MIN);
printf("maximum
printf("maximum char
char == %i\n",
%i\n", CHAR_MAX);
CHAR_MAX);
printf("after
printf("after '%c'
'%c' comes
comes '%c'\n",
'%c'\n", lower_a,
lower_a, lower_a
lower_a ++ 1);
1);
printf("uppercase
printf("uppercase is
is '%c'\n",
'%c'\n", lower_m
lower_m -- 'a'
'a' ++ 'A');
'A');
return
return 0;
0;
}} minimum
minimum char
char == 0,
0, maximum
maximum char
char == 255
255
after
after 'a'
'a' comes
comes 'b'
'b'
uppercase
uppercase isis 'M'
'M'
Integers With Different Bases

 It is possible to work in octal (base 8) and


hexadecimal (base 16)
zero puts compiler zero “x” puts
into octal mode! compiler into
#include hexadecimal
#include <stdio.h>
<stdio.h>
mode
int
int main(void)
main(void)
{{
int
int dec
dec == 20,
20, oct
oct == 020,
020, hex
hex == 0x20;
0x20;
printf("dec=%d,
printf("dec=%d, oct=%d,
oct=%d, hex=%d\n",
hex=%d\n", dec,
dec, oct,
oct, hex);
hex);
printf("dec=%d,
printf("dec=%d, oct=%o,
oct=%o, hex=%x\n",
hex=%x\n", dec,
dec, oct,
oct, hex);
hex);
return
return 0;
0;
}}
dec=20,
dec=20, oct=16,
oct=16, hex=32
hex=32
dec=20, oct=20, hex=20
dec=20, oct=20, hex=20
Real Types In C

 C supports different kinds of reals


 maxima and minima are defined in “float.h”

type format bytes minimum maximum


float %f %e %g 4 FLT_MIN FLT_MAX
double %lf %le %lg 8 DBL_MIN DBL_MAX
long double %Lf %Le %Lg 10 LDBL_MIN LDBL_MAX
Real Example

#include
#include <stdio.h>
<stdio.h>
#include
#include <float.h>
<float.h>
int
int main(void)
main(void)
{{
double
double ff == 3.1416,
3.1416, gg == 1.2e-5,
1.2e-5, hh == 5000000000.0;
5000000000.0;
printf("f=%lf\tg=%lf\th=%lf\n",
printf("f=%lf\tg=%lf\th=%lf\n", f,
f, g,
g, h);
h);
printf("f=%le\tg=%le\th=%le\n",
printf("f=%le\tg=%le\th=%le\n", f,
f, g,
g, h);
h);
printf("f=%lg\tg=%lg\th=%lg\n",
printf("f=%lg\tg=%lg\th=%lg\n", f,
f, g,
g, h);
h);
printf("f=%7.2lf\tg=%.2le\th=%.4lg\n",
printf("f=%7.2lf\tg=%.2le\th=%.4lg\n", f,
f, g,
g, h);
h);
return
return 0;
0;
}} f=3.141600 g=0.000012 h=5000000000.000000
f=3.141600 g=0.000012 h=5000000000.000000
f=3.141600e+00 g=1.200000e-05
f=3.141600e+00 g=1.200000e-05 h=5.000000e+09
h=5.000000e+09
f=3.1416
f=3.1416 g=1.2e-05
g=1.2e-05 h=5e+09
h=5e+09
f=
f= 3.14
3.14 g=1.20e-05
g=1.20e-05 h=5e+09
h=5e+09
Constants

 Constants have types in C


 Numbers containing “.” or “e” are double: 3.5,
1e-7, -1.29e15
 For float constants append “F”: 3.5F, 1e-7F
 For long double constants append “L”:
-1.29e15L, 1e-7L
 Numbers without “.”, “e” or “F” are int, e.g.
10000, -35 (some compilers switch to long int if
the constant would overflow int)
 For long int constants append “L”, e.g.
9000000L
Warning!

#include double constant created


#include <stdio.h>
<stdio.h> because of “.”
int
int main(void)
main(void)
{{
double
double ff == 5000000000.0;
5000000000.0;
double
double gg == 5000000000;
5000000000;
constant is int or long but
printf("f=%lf\n", 2,147,483,647 is the
printf("f=%lf\n", f);
f);
printf("g=%lf\n", maximum!
printf("g=%lf\n", g);
g);

return
return 0;
0;
}}
f=5000000000.000000
f=5000000000.000000
g=705032704.000000 OVERFLOW
g=705032704.000000
Named Constants

 Named constants may be created using const

#include
#include <stdio.h>
<stdio.h>
creates an
integer int
int main(void)
main(void)
constant {{
const
const long
long double
double pi
pi == 3.141592653590L;
3.141592653590L;
const
const int
int days_in_week
days_in_week == 7;7;
const sunday = 0;
const sunday = 0;

days_in_week
days_in_week == 5;
5;
error!
return
return 0;
0;
}}
Preprocessor Constants

 Named constants may also be created using the


Preprocessor
– Needs to be in “search and replace” mode
– Historically these constants consist of capital letters

search for “PI”, replace with 3.1415....


Note: no “=”
#include
#include <stdio.h>
<stdio.h> and no “;”
#define
#define PI
PI 3.141592653590L
3.141592653590L
#define
#define DAYS_IN_WEEK
DAYS_IN_WEEK 77
#define
#define SUNDAY
SUNDAY 00

int
int day
day == SUNDAY;
SUNDAY;
long
long flag = USE_API;
flag = USE_API;

“PI” is NOT substituted here


Take Care With printf And scanf!

#include
#include <stdio.h>
<stdio.h>
“%c” fills one byte
of “a” which is two int
int main(void)
main(void)
bytes in size {{
short
short aa == 256,
256, bb == 10;
10;

printf("Type
printf("Type aa number:
number: ");
");
scanf("%c",
scanf("%c", &a);
&a);
“%f” expects 4 byte
float in IEEE format,
printf("a
printf("a == %hi,
%hi, bb == %f\n",
%f\n", a,
a, b);
b);
“b” is 2 bytes and
NOT in IEEE format
return
return 0;
0;
}}
Type
Type aa number:
number: 11
aa == 305
305 bb == Floating
Floating support
support not
not loaded
loaded
Summary

 K&R C vs Standard C
 main, printf
 Variables
 Integer types
 Real types
 Constants
 Named constants
 Preprocessor constants
 Take care with printf and scanf
Operators in C

 Arithmetic operators
 Cast operator
 Increment and Decrement
 Bitwise operators
 Comparison operators
 Assignment operators
 sizeof operator
 Conditional expression operator
Arithmetic Operators

 C supports the arithmetic operators:


+ addition
- subtraction
* multiplication
/ division
% modulo (remainder)

 “%” may not be used with reals


Using Arithmetic Operators

 The compiler uses the types of the operands to


determine how the calculation should be done

“i” and “j” are ints, integer


division is done, 1 is int
assigned to “k” int main(void)
main(void)
{{
int
int ii == 5,
5, jj == 4, 4, k; k;
“f” and “g” are double, double
double ff == 5.0,
5.0, gg == 4.0,
4.0, h;
h;
double division is done, 1.25
is assigned to “h” kk == ii // j;
j;
hh == ff // g;
g;
hh == ii // j;
j;
integer division is still done,
despite “h” being double. return
return 0;
0;
Value assigned is 1.00000 }}
The Cast Operator

 The cast operator temporarily changes the type


of a variable

if either operand is a double, int


int main(void)
main(void)
the other is automatically {{
promoted int
int ii == 5,
5, jj == 4;
4;
double
double f;
f;

ff == (double)i
(double)i // j;
j;
ff == ii // (double)j;
(double)j;
ff == (double)i
(double)i // (double)j;
(double)j;
ff == (double)(i
(double)(i // j);
j);
integer division is done here,
return
return 0;
0;
the result, 1, is changed to a
}}
double, 1.00000
Increment and Decrement

 C has two special operators for adding and


subtracting one from a variable
++ increment
-- decrement

 These may be either prefix (before the variable) or


postfix (after the variable):

int
int ii == 5,
5, jj == 4;
4;
“i” becomes 6
i++;
i++;
“j” becomes 3 --j;
--j;
++i;
++i;
“i” becomes 7
Prefix and Postfix

 The prefix and postfix versions are different

#include
#include <stdio.h>
<stdio.h> equivalent to:
1. j++;
int
int main(void)
main(void) 2. i = j;
{{
int
int i,
i, jj == 5;
5;
ii == ++j;
++j;
printf("i=%d,
printf("i=%d, j=%d\n",
j=%d\n", i,
i, j);
j); equivalent to:
jj == 5; 1. i = j;
5;
ii == j++; 2. j++;
j++;
printf("i=%d,
printf("i=%d, j=%d\n",
j=%d\n", i,
i, j);
j);
return
return 0;
0;
}} i=6,
i=6, j=6
j=6
i=5,
i=5, j=6
j=6
Truth in C

 To understand C’s comparison operators (less


than, greater than, etc.) and the logical operators
(and, or, not) it is important to understand how C
regards truth
 There is no boolean data type in C, integers are
used instead
 The value of 0 (or 0.0) is false
 Any other value, 1, -1, 0.3, -20.8, is true
if(32)
if(32)
printf("this
printf("this will
will always
always be
be printed\n");
printed\n");

if(0)
if(0)
printf("this
printf("this will
will never
never be
be printed\n");
printed\n");
Comparison Operators

 C supports the comparison operators:


< less than
<= less than or equal to
> greater than
>= greater than or equal to
== is equal to
!= is not equal to

 These all give 1 (non zero value, i.e. true) when


the comparison succeeds and 0 (i.e. false) when
the comparison fails
Logical Operators

 C supports the logical operators:


&& and
|| or
! not

 These also give 1 (non zero value, i.e. true) when


the condition succeeds and 0 (i.e. false) when the
condition fails

int
int i,
i, jj == 10,
10, kk == 28;
28;

ii == ((j
((j >> 5)
5) &&
&& (k
(k << 100))
100)) ||
|| (k
(k >> 24);
24);
Logical Operator Guarantees

 C makes two important guarantees about the


evaluation of conditions
 Evaluation is left to right
 Evaluation is “short circuit”

“i < 10” is evaluated first, if false the whole statement is


false (because false AND anything is false) thus “a[i] > 0”
would not be evaluated

if(i
if(i << 10
10 &&
&& a[i]
a[i] >> 0)
0)
printf("%i\n",
printf("%i\n", a[i]);
a[i]);
Warning!

 Remember to use parentheses with conditions,


otherwise your program may not mean what you
think
in this attempt to say “i not equal to five”, “!i”
is evaluated first. As “i” is 10, i.e. non zero,
i.e. true, “!i” must be false, i.e. zero. Zero is
compared with five

int
int ii == 10;
10;

if(!i
if(!i ==
== 5)
5)
printf("i
printf("i is
is not
not equal
equal toto five\n");
five\n");
else
else
printf("i
printf("i is
is equal
equal to
to five\n");
five\n");
ii is
is equal
equal to
to five
five
Bitwise Operators

 C has the following bit operators which may only


be applied to integer types:
& bitwise and
| bitwise inclusive or
^ bitwise exclusive or
~ one’s compliment
>> right shift
<< left shift
Bitwise Example
#include
#include <stdio.h>
<stdio.h> 0x6eb9
0x6eb9 0110
0110 1110
1110 1011
1011 1001
1001
0x5d27 0101 1101 0010 0111
0x5d27 0101 1101 0010 0111
int
int main(void)
main(void) 0x4c21
0x4c21 0100
0100 1100
1100 0010
0010 0001
0001
{{
short
short aa == 0x6eb9;
0x6eb9; 0x6eb9
short b = 0x5d27; 0x6eb9 0110
0110 1110
1110 1011
1011 1001
1001
short b = 0x5d27; 0x5d27 0101 1101 0010 0111
0x5d27 0101 1101 0010 0111
unsigned
unsigned short
short cc == 7097;
7097; 0x7fbf
0x7fbf 0111
0111 1111
1111 1011
1011 1111
1111
printf("0x%x,
printf("0x%x, ",
", aa && b);
b);
printf("0x%x, 0x6eb9
0x6eb9 0110
0110 1110
1110 1011
1011 1001
printf("0x%x, ",
", aa || b);
b); 1001
printf("0x%x\n", a ^ b); 0x5d27 0101 1101 0010 0111
0x5d27 0101 1101 0010 0111
printf("0x%x\n", a ^ b);
0x339e
0x339e 0011
0011 0011
0011 1001
1001 1110
1110
printf("%u,
printf("%u, ",
", cc <<
<< 2);
2);
printf("%u\n",
printf("%u\n", cc >>>> 1);
1);
7097
7097 0001
0001 1011
1011 1011
1011 1001
1001
return
return 0;
0; 28388
28388 0110
0110 1110
1110 1110
1110 0100
0100
}}
0x4c21,
0x4c21, 0x7fbf,
0x7fbf, 0x339e
0x339e 7097
7097 0001
0001 1011
1011 1011
1011 1001
1001
28388,
28388, 3548
3548 3548 0000 1101 1101 1100
3548 0000 1101 1101 1100
Assignment

 Assignment is more flexible than might first


appear
 An assigned value is always made available for
subsequent use
“n = 22” happens first, this
makes 22 available for
assignment to “m”. Assigning
int 22 to “m” makes 22 available
int i,
i, j,
j, k,
k, l,
l, m,
m, n;
n; for assignment to “l” etc.
ii == jj == kk == ll == mm == nn == 22;
22;

printf("%i\n",
printf("%i\n", jj == 93);
93);
“j” is assigned 93, the 93 is then
made available to printf for
printing
Warning!

 One of the most frequent mistakes is to confuse


test for equality, “==”, with assignment, “=”
#include
#include <stdio.h>
<stdio.h>

int
int main(void)
main(void)
{{
int
int ii == 0;
0;

if(i
if(i == 0)
0)
printf("i
printf("i is
is equal
equal to
to zero\n");
zero\n");
else
else
printf("somehow
printf("somehow ii is
is not
not zero\n");
zero\n");

return
return 0;
0;
}}
somehow
somehow ii is
is not
not zero
zero
Other Assignment Operators

 There is a family of assignment operators:


+= -= *= /= %=
&= |= ^=
<<= >>=
 In each of these:
expression1 op= expression2

is equivalent to:
(expression1) = (expression1) op (expression2)

aa +=
+= 27;
27; ff /=
/= 9.2;
9.2; ii *=
*= jj ++ 2;
2;
aa == aa ++ 27;
27; ff == ff // 9.2;
9.2; ii == ii ** (j
(j ++ 2);
2);
sizeof Operator

 C has a mechanism for determining how many


bytes a variable occupies
#include
#include <stdio.h>
<stdio.h>
int
int main(void)
main(void)
{{
long
long big;
big;
printf("\"big\"
printf("\"big\" isis %u
%u bytes\n",
bytes\n", sizeof(big));
sizeof(big));
printf("a short is %u bytes\n", sizeof(short));
printf("a short is %u bytes\n", sizeof(short));
printf("a
printf("a double
double is
is %u
%u bytes\n",
bytes\n", sizeof
sizeof double);
double);
return
return 0;
0;
}}
"big"
"big" is
is 44 bytes
bytes
aa short
short is 2 bytes
is 2 bytes
aa double
double is 8 bytes
is 8 bytes
Conditional Expression Operator

 The conditional expression operator provides an


in-line if/then/else
 If the first expression is true, the second is
evaluated
 If the first expression is false, the third is
evaluated
int
int i,
i, jj == 100,
100, kk == -1;
-1; int
int i,
i, jj == 100,
100, kk == -1;
-1;
ii == (j
(j >> k)
k) ?? jj :: k;
k; ii == (j
(j << k)
k) ?? jj :: k;
k;
if(j
if(j >> k)
k) if(j
if(j << k)k)
ii == j;
j; ii == j;
j;
else
else else
else
ii == k;
k; ii == k;
k;
Precedence of Operators

 C treats operators with different importance,


known as precedence
 There are 15 levels
 In general, the unary operators have higher
precedence than binary operators
 Parentheses can always be used to improve
clarity #include
#include <stdio.h>
<stdio.h>
int
int main(void)
main(void)
{{
int
int jj == 33 ** 44 ++ 48
48 // 7;
7;
printf("j
printf("j == %i\n",
%i\n", j);
j);
return
return 0;
0;
}} jj == 18
18
Associativity of Operators

 For two operators of equal precedence (i.e. same


importance) a second rule, “associativity”, is
used
 Associativity is either “left to right” (left operator
first) or “right to left” (right operator first)
#include
#include <stdio.h>
<stdio.h>
int
int main(void)
main(void)
{{
int
int ii == 66 ** 44 // 7;
7;
printf("i
printf("i == %d\n",
%d\n", i);
i);
return
return 0;
0;
}}
ii == 33
Precedence/Associativity Table
Operator Associativity
() [] -> . left to right
! ~ ++ -- - + (cast) * & sizeof right to left
* / % left to right
+ - left to right
<< >> left to right
< <= >= > left to right
== != left to right
& left to right
| left to right
^ left to right
&& left to right
|| left to right
?: right to left
= += -= *= /= %= etc right to left
, left to right
Review

#include
#include <stdio.h>
<stdio.h>
int
int main(void)
main(void)
{{
int
int ii == 0,
0, j,
j, kk == 7,
7, mm == 5,
5, n;
n;
jj == mm +=
+= 2;
2;
printf("j
printf("j == %d\n",
%d\n", j);
j);
jj == k++
k++ >> 7;
7;
printf("j
printf("j = %d\n",
= %d\n", j);
j);
jj == ii ==
== 00 && k;
k;
printf("j
printf("j = %d\n", j);
= %d\n", j);
nn == !i
!i >> kk >>
>> 2;
2;
printf("n
printf("n == %d\n",
%d\n", n);
n);
return
return 0;
0;
}}
Control Flow

 Decisions - if then else


 More decisions - switch
 Loops - while, do while, for
 Keyword break
 Keyword continue
Decisions - if then

 Parentheses surround the test


 One statement becomes the “then part”
 If more are required, braces must be used

scanf("%i",
scanf("%i", &i);
&i);

if(i
if(i >> 0)
0)
printf("a
printf("a positive
positive number
number was
was entered\n");
entered\n");

if(i
if(i << 0)
0) {{
printf("a
printf("a negative
negative number
number was
was entered\n");
entered\n");
ii == -i;
-i;
}}
Warning!

 A semicolon after the condition forms a “do


nothing” statement

printf("input
printf("input an
an integer:
integer: ");
");
scanf("%i",
scanf("%i", &j);
&j);

if(j
if(j >> 0);
0);
printf("a
printf("a positive
positive number
number was
was entered\n");
entered\n");

input
input an
an integer:
integer: -6
-6

aa positive
positive number
number was
was entered
entered
if then else

 An optional else may be added


 One statement by default, if more are required,
braces must be used
if(i
if(i >> 0)
0)
printf("i
printf("i is
is positive\n");
positive\n");
else
else
printf("i
printf("i is
is negative\n");
negative\n");

if(i
if(i >> 0)
0)
printf("i
printf("i is
is positive\n");
positive\n");
else {
else {
printf("i
printf("i is
is negative\n");
negative\n");
ii == -i;
-i;
}}
Nesting ifs

 else associates with the nearest if


int
int ii == 100;
100;
if(i
if(i >> 0)
0)
if(i
if(i >> 1000)
1000)
printf("i
printf("i is
is big\n");
big\n");
else
else
printf("i i is reasonable
printf("i is
is reasonable\n");
reasonable\n"); i is reasonable

int
int ii == -20;
-20;
if(i
if(i >> 0)
0) {{
if(i
if(i >> 1000)
1000)
printf("i
printf("i is
is big\n");
big\n");
}} else
else ii is
is negative
negative
printf("i
printf("i isis negative\n");
negative\n");
switch

 C supports a switch for multi-way decision


making
switch(c)
switch(c) {{
case
case 'a':
'a': case
case 'A':
'A':
printf("area
printf("area == %.2f\n",
%.2f\n", rr ** rr ** pi);
pi);
break;
break;
case
case 'c':
'c': case
case 'C':
'C':
printf("circumference
printf("circumference == %.2f\n",
%.2f\n", 22 ** rr ** pi);
pi);
break;
break;
case
case 'q':
'q':
printf("quit
printf("quit option
option chosen\n");
chosen\n");
break;
break;
default:
default:
printf("unknown
printf("unknown option
option chosen\n");
chosen\n");
break;
break;
}}
More About switch

 Only integral constants may be tested


 If no condition matches, the default is executed
 If no default, nothing is done (not an error)
 The break is important

float ii == 3;
float f;
f; 3;
switch(f) switch(i)
switch(i) {{
switch(f) {{ case
case
case 2:
2: case 3:3: printf("i
printf("i == 3\n");
3\n");
.... case 2: printf("i = 2\n");
case 2: printf("i = 2\n");
.... case
case 1:1: printf("i
printf("i == 1\n");
1\n");
switch(i) }}
switch(i) {{ ii == 33
case
case 22 ** j:
j: ii == 22
....
.... ii == 11
A switch Example
printf("On
printf("On thethe ");
");
switch(i)
switch(i) { {
case
case 1:
1: printf("1st");
printf("1st"); break;
break;
case 2:
case 2: printf("2nd");
printf("2nd"); break;
break;
case 3:
case 3: printf("3rd");
printf("3rd"); break;
break;
default:
default: printf("%ith", i); break;
printf("%ith", i); break;
}}
printf("
printf(" dayday of
of Christmas
Christmas my
my true
true love
love sent
sent toto me
me ");
");
switch(i)
switch(i) { {
case
case 12:
12: printf("twelve
printf("twelve lords
lords aa leaping,
leaping, ");
");
case 11:
case 11: printf("eleven ladies dancing,
printf("eleven ladies dancing, "); ");
case 10:
case 10: printf("ten
printf("ten pipers
pipers piping,
piping, ");
");
case 9:
case 9: printf("nine
printf("nine drummers drumming, ");
drummers drumming, ");
case 8:
case 8: printf("eight maids a milking,
printf("eight maids a milking, "); ");
case 7:
case 7: printf("seven
printf("seven swans
swans aa swimming,
swimming, ");
");
case 6:
case 6: printf("six geese a laying,
printf("six geese a laying, "); ");
case 5:
case 5: printf("five
printf("five gold
gold rings,
rings, ");
");
case 4:
case 4: printf("four
printf("four calling birds, ");
calling birds, ");
case 3:
case 3: printf("three French hens,
printf("three French hens, "); ");
case 2:
case 2: printf("two
printf("two turtle
turtle doves
doves and
and ");
");
case 1:
case 1: printf("a
printf("a partridge in a pear tree\n");
partridge in a pear tree\n");
}}
while Loop

 The simplest C loop is the while


 Parentheses must surround the condition
 One statement forms the body of the loop
 Braces must be added if more statements are to
be executed
int
int jj == 5;
5;
while(j jj == 55
while(j >> 0) 0)
printf("j jj == 44
printf("j == %i\n",
%i\n", j--);
j--);
jj == 33
while(j jj == 22
while(j >> 0)
0) {{ jj == 11
printf("j
printf("j == %i\n",
%i\n", j);
j);
j--;
j--;
}}
(Another) Semicolon Warning!

 A semicolon placed after the condition forms a


body that does nothing

int program disappears


int jj == 5;
5;
into an infinite loop
while(j
while(j >> 0);0);
printf("j
printf("j == %i\n",
%i\n", j--);
j--);

• Sometimes an empty loop body is required


int
int c,
c, j;
j; placing semicolon
while(scanf("%i", on the line below
while(scanf("%i", &j)
&j) !=
!= 1)
1)
while((c makes the
while((c = getchar()) !=
= getchar()) != '\n')
'\n')
;; intention obvious
while, Not Until!

 Remember to get the condition the right way


around!

int
int jj == 5;
5;
user probably
printf("start\n");
printf("start\n");
intends “until j is while(j
equal to zero”, while(j == == 0)
0)
printf("j
printf("j == %i\n",
%i\n", j--);
j--);
however this is NOT printf("end\n");
printf("end\n");
the way to write it
start
start
end
end
do while

 do while guarantees execution at least once


int
int jj == 5;
5; start
start
printf("start\n");
printf("start\n"); jj == 55
do
do jj == 44
printf("j
printf("j == %i\n",
%i\n", j--);
j--); jj == 33
while(j >
while(j > 0);0); jj == 22
printf("stop\n");
printf("stop\n"); jj == 11
stop
stop
int
int jj == -10;
-10;
printf("start\n");
printf("start\n");
do
do {{
printf("j start
printf("j == %i\n",
%i\n", j);
j); start
j--; jj == -10
-10
j--;
}} while(j stop
while(j >> 0);
0); stop
printf("stop\n");
printf("stop\n");
for Loop

 for encapsulates the essential elements of a


loop into one statement
for(initial-part;
for(initial-part; while-condition;
while-condition; update-part)
update-part)
body;
body;

jj == 55
int
int j;
j; jj == 44
for(j jj == 33
for(j == 5;
5; jj >> 0;
0; j--)
j--)
printf("j jj == 22
printf("j = %i\n", j);
= %i\n", j);
jj == 11

for(j jj == 55 odd
for(j == 5;
5; jj >> 0;
0; j--)
j--) {{ odd
jj == 44 even
printf("j
printf("j == %i %i ",
", j);
j); even
printf("%s\n", ((j%2)==0)?"even":"odd"); jj == 33 odd
odd
printf("%s\n", ((j%2)==0)?"even":"odd"); jj == 22 even
}} even
jj == 11 odd
odd
for Is Not Until Either!

 Remember to get the for condition the right way


around (it is really a while condition)

int
int j;
j;
user probably printf("start\n");
printf("start\n");
intends “until j is for(j
for(j == 5;
5; jj ==
== 0;
0; j--)
j--)
equal to zero”, printf("j
printf("j == %i\n",
%i\n", j);
j);
however this is NOT printf("end\n");
printf("end\n");
the way to write it start
start
either! end
end
Stepping With for

 Unlike some languages, the for loop is not


restricted to stepping up or down by 1

#include
#include <math.h>
<math.h>

int
int main(void)
main(void)
{{
double
double angle;
angle;

for(angle
for(angle == 0.0;
0.0; angle
angle << 3.14159;
3.14159; angle
angle +=
+= 0.2)
0.2)
printf("sine
printf("sine of of %.1lf
%.1lf is
is %.2lf\n",
%.2lf\n",
angle,
angle, sin(angle));
sin(angle));

return
return 0;
0;
}}
Extending the for Loop

 The initial and update parts may contain multiple


comma separated statements
int
int i,
i, j,
j, k;
k;
for(i
for(i == 0,
0, jj == 5,
5, kk == -1;
-1; ii << 10;
10; i++,
i++, j++,
j++, k--)
k--)

 The initial, condition and update parts may


contain no statements at all!
for(;
for(; ii << 10;
10; i++,
i++, j++,
j++, k--)
k--)

for(;i
for(;i << 10;)
10;) use of a while loop would
be clearer here!
for(;;)
for(;;)
creates an infinite loop
break

 The break keyword forces immediate exit from


the nearest enclosing loop
 Use in moderation!
if scanf returns 1, jump
out of the loop
for(;;)
for(;;) {{
printf("type
printf("type an an int:
int: ");
");
if(scanf("%i",
if(scanf("%i", &j) == 1)
&j) == 1)
break;
break;
while((c
while((c == getchar())
getchar()) != != '\n')
'\n')
;;
}}
printf("j = %i\n", j); type
type an
an int:
int: an
an int
int
printf("j = %i\n", j); type an int: no
type an int: no
type
type anan int:
int: 16
16
jj == 16
16
continue

 The continue keyword forces the next iteration


of the nearest enclosing loop
 Use in moderation!
if j is exactly divisible
by 3, skip

for(j jj == 11
for(j == 1;
1; jj <=
<= 10;
10; j++)
j++) {{
if(j jj == 22
if(j %% 33 ==== 0)
0)
continue; jj == 44
continue;
printf("j jj == 55
printf("j == %i\n",
%i\n", j);
j);
}} jj == 77
jj == 88
jj == 10
10
Summary

 if (then) else - watch the semicolons


 switch can test integer values
 while, do while, for - watch the semicolons
again
 break
 continue
Functions

 Rules of functions
 Examples - writing a function, calling a function
 Function prototypes
 Visibility
 Call by value
 The stack
 auto, static and register
The Rules

 A function may accept as many parameters as it


needs, or no parameters (like main)
 A function may return either one or no values
 Variables declared inside a function are only
available to that function, unless explicitly
passed to another function
Writing a Function - Example
this is the TYPE of the value handed back

accept 3 doubles when called

int
int print_table(double
print_table(double start,
start, double
double end,
end, double
double step)
step)
{{
double
double d;d;
int
int lines
lines == 1;
1;

printf("Celsius\tFarenheit\n");
printf("Celsius\tFarenheit\n");
for(d
for(d == start;
start; dd <=
<= end;
end; dd +=
+= step,
step, lines++)
lines++)
printf("%.1lf\t%.1lf\n",
printf("%.1lf\t%.1lf\n", d, d * 1.8
d, d * 1.8 ++ 32);
32);

return
return lines;
lines;
}}

this is the ACTUAL value handed back


Calling a Function - Example
IMPORTANT: this tells the compiler how print_table works

#include
#include <stdio.h>
<stdio.h>

int
int print_table(double,
print_table(double, double,
double, double);
double);

int
int main(void)
main(void)
{{
int
int how_many;
how_many;
double
double end
end == 100.0;
100.0;
the compiler knows these
how_many
how_many == print_table(1.0,
print_table(1.0, end,
end, 3);
3); should be doubles and
print_table(end, 200, 15);
print_table(end, 200, 15); converts them automatically

return
return 0;
0;
}}

here the function’s return value is ignored - this


is ok, if you don’t want it, you don’t have to use it
Calling a Function - Disaster!

now the compiler does not know how the function works

#include
#include <stdio.h>
<stdio.h>

int
int main(void)
main(void)
{{
int
int how_many;
how_many; the compiler does NOT
double
double end
end == 100.0;
100.0; convert these ints to
doubles. The function
how_many
how_many == print_table(1.0,
print_table(1.0, end,
end, 3);
3); picks up doubles
print_table(end, 200, 15);
print_table(end, 200, 15); anyway!

return
return 0;
0;
}}
Prototypes

 The (optional) line


int print_table(double, double, double);

is known as a prototype
 If the compiler meets a call to an unknown
function it “guesses”
– Guess 1: the function returns an int, even if it doesn’t
– Guess 2: you have passed the correct number of parameters
and made sure they are all of the correct type, even if you
haven’t
 The prototype provides the compiler with
important information about the return type and
parameters
Prototyping is Not Optional

 To achieve working programs the compiler is


best given a prototype for each function called
 When calling a Standard Library function,
#include the file specified in the help page(s) -
this file will contain the prototype
 When calling one of your own functions, write a
prototype by hand
Writing Prototypes

 Prototype:
int
int print_table(double,
print_table(double, double,
double, double);
double);

 Function header:
int
int print_table(double
print_table(double start,
start, double
double end,
end, double
double step)
step)
{{

 The function prototype may optionally include


variable names (which are ignored)
int
int print_table(double
print_table(double start,
start, double
double end,
end, double
double step);
step);

int
int print_table(double
print_table(double x,
x, double
double y,
y, double
double z);
z);
Take Care With Semicolons

 The prototype has a semicolon


int
int print_table(double
print_table(double start,
start, double
double end,
end, double
double step);
step);

 The function header has an open brace


int
int print_table(double
print_table(double start,
start, double
double end,
end, double
double step)
step)
{{

 Don’t confuse the compiler by adding a


semicolon into the function header!
int
int print_table(double
print_table(double start,
start, double
double end,
end, double
double step);
step);
{{
Example Prototypes
/*
/* no
no parameters,
parameters, int
int return
return value
value */
*/
int get_integer(void);
int get_integer(void);

/*
/* no
no parameters,
parameters, double
double return
return value
value */
*/
double get_double(void);
double get_double(void);

/*
/* no
no parameters,
parameters, no
no return
return value
value */
*/
void
void clear_screen(void);
clear_screen(void);

/*
/* three
three int
int parameters,
parameters, int
int return
return value
value */
*/
int day_of_year(int day, int month, int year);
int day_of_year(int day, int month, int year);

/*
/* three
three int
int parameters,
parameters, long
long int
int return
return value
value */
*/
long day_since_1_jan_1970(int, int,
long day_since_1_jan_1970(int, int, int);int);

/*
/* parameter
parameter checking
checking DISABLED,
DISABLED, double
double return
return value
value */
*/
double
double k_and_r_function();
k_and_r_function();

/*
/* short
short int
int parameter,
parameter, (default)
(default) int
int return
return value
value */
*/
transfer(short int s);
transfer(short int s);
Example Calls
int
int i;
i;
double
double d;
d;
long
long l;
l;
short
short int
int ss == 5;
5;

ii == get_integer();
get_integer();
no mention of “void”
dd == get_double();
get_double(); when calling these
functions
clear_screen();
clear_screen();

ii == day_of_year(16,
day_of_year(16, 7,
7, 1969);
1969);

ll == day_since_1_jan_1970(1,
day_since_1_jan_1970(1, 4,
4, 1983);
1983);
the compiler cannot tell
dd == k_and_r_function();
k_and_r_function(); which of these (if any) is
dd == k_and_r_function(19.7);
k_and_r_function(19.7); correct - neither can we
dd == k_and_r_function("hello
k_and_r_function("hello world");
world"); without resorting to
documentation!
ii == transfer(s);
transfer(s);
Rules of Visibility

 C is a block structured language, variables may


only be used in functions declaring them
int
int main(void)
main(void)
{{
int
int ii == 5,
5, j,
j, kk == 2;
2; compiler does not
float f = 2.8F,
float f = 2.8F, g; g;
know about “d”
dd == 3.7;
3.7;
}}
void
void func(int
func(int v)
v) “i” and “g” not
{{
double available here
double d,
d, ee == 0.0,
0.0, f;
f;
i++;
i++; g--;
g--;
ff == 0.0;
0.0; func’s “f” is used,
}} not main’s
Call by Value

 When a function is called the parameters are


copied - “call by value”
 The function is unable to change any variable
passed as a parameter
 In the next chapter pointers are discussed which
allow “call by reference”
 We have already had a sneak preview of this
mechanism with scanf
Call by Value - Example
#include
#include <stdio.h>
<stdio.h>
void
void change(int
change(int v);
v);
int
int main(void)
main(void)
{{ the function
int
int var
var == 5;
5; was not able
change(var); to alter “var”
change(var);
printf("main:
printf("main: var
var == %i\n",
%i\n", var);
var);
return
return 0;
0;
}}
the function is
void change(int
void change(int v)v)
{{ able to alter “v”
vv *=
*= 100;
100;
printf("change:
printf("change: vv == %i\n",
%i\n", v);
v);
}} change:
change: vv == 500
500
main: var =
main: var = 5 5
C and the Stack

 C uses a stack to store local variables (i.e. those


declared in functions), it is also used when
passing parameters to functions
 The calling function pushes the parameters
 The function is called
 The called function picks up the parameters
 The called function pushes its local variables
 When finished, the called function pops its local
variables and jumps back to the calling function
 The calling function pops the parameters
 The return value is handled
Stack Example
#include
#include <stdio.h>
<stdio.h>
double
double power(int,
power(int, int);
int);
int
int main(void)
main(void)
{{
int
int xx == 2;
2;
double d;
double d;
dd == power(x,
power(x, 5);
5);
printf("%lf\n",
printf("%lf\n", d); d);
return 32.0 power: result
return 0;
0;
}}
2 power: n
double
double power(int
power(int n,
n, int
int p)
p)
{{ 5 power: p
double
double result
result == n;
n;
? main: d
while(--p
while(--p >> 0)
0)
result
result *=*= n;
n; 2 main: x
return
return result;
result;
}}
Storage

 C stores local variables on the stack


 Global variables may be declared. These are not
stack based, but are placed in the data segment
 Special keywords exist to specify where local
variables are stored:
auto - place on the stack (default)
static - place in the data segment
register - place in a CPU register
 Data may also be placed on the heap, this will be
discussed in a later chapter
auto

 Local variables are automatically allocated on


entry into, and automatically deallocated on exit
from, a function
 These variables are therefore called “automatic”
 Initial value: random
 Initialisation: recommended

int
int table(void)
table(void)
{{
int lines auto keyword
int lines == 13;
13;
auto int columns;
auto int columns; redundant
static

 The static keyword instructs the compiler to


place a variable into the data segment
 The data segment is permanent (static)
 A value left in a static in one call to a function
will still be there at the next call
 Initial value: 0
 Initialisation: unnecessary if you like zeros

int
int running_total(void)
running_total(void)
{{ permanently allocated,
static
static int
int rows;
rows;
but local to this
function
rows++;
rows++;
register

 The register keyword tells the compiler to place


a variable into a CPU register (you cannot specify
which)
 If a register is unavailable the request will be
ignored
 Largely redundant with optimising compilers
 Initial value: random
 Initialisation: recommended
void
void speedy_function(void)
speedy_function(void)
{{
register
register int
int i;
i;
for(i
for(i == 0;
0; ii << 10000;
10000; i++)
i++)
Global Variables

 Global variables are created by placing the


declaration outside all functions
 They are placed in the data segment
 Initial value: 0
 Initialisation: unnecessary if you like zeros

#include
#include <stdio.h>
<stdio.h> variable “d” is global
double
double d;
d; and available to all
int functions defined
int main(void)
main(void)
{{ below it
int
int i;
i;
return
return 0;
0;
}}
Review

 Writing and calling functions


 The need for function prototypes
 Visibility
 C is “call by value”
 Local variables are stack based, this can be
changed with the static and register
keywords
 Global variables may be created, they are stored
in the data segment
Pointers

 Declaring pointers
 The “&” operator
 The “*” operator
 Initialising pointers
 Type mismatches
 Call by reference
 Pointers to pointers
Pointers - Why?

 Using pointers allows us to:


– Achieve call by reference (i.e. write functions which change
their parameters)
– Handle arrays efficiently
– Handle structures (records) efficiently
– Create linked lists, trees, graphs etc.
– Put data onto the heap
– Create tables of functions for handling Windows events,
signals etc.
 Already been using pointers with scanf
 Care must be taken when using pointers since
there are no safety features
Declaring Pointers

 Pointers are declared by using “*”


 Declare an integer:
int
int i;
i;

 Declare a pointer to an integer:


int
int *p;
*p;

 There is some debate as to the best position of


the “*”
int*
int* p;
p;
Example Pointer Declarations

int
int *pi;
*pi; /*
/* pi
pi is
is aa pointer
pointer to
to an
an int
int */
*/
long
long int
int *p;
*p; /*
/* pp is
is aa pointer
pointer to
to aa long
long int
int */
*/
float*
float* pf;
pf; /*
/* pf
pf is
is aa pointer
pointer to
to aa float
float */
*/
char
char c,
c, d,
d, *pc;
*pc; /*
/* cc and
and dd are
are aa char
char
pc
pc is a pointer to
is a pointer to char
char */
*/
double*
double* pd,
pd, e,
e, f;
f; /*
/* pd
pd is
is pointer
pointer to
to aa double
double
ee and f are double
and f are double */ */
char*
char* start;
start; /*
/* start
start is
is aa pointer
pointer to
to aa char
char */
*/

char*
char* end;
end; /*
/* end
end is
is aa pointer
pointer to
to aa char
char */
*/
The “&” Operator

 The “&”, “address of” operator, generates the


address of a variable
 All variables have addresses except register
variables
char
char gg == 'z';
'z';
int
int main(void)
main(void) p c
{{
char cc == 'a'; 0x1132 'a'
char 'a';
char *p; 0x1132
char *p;
pp == &c;
&c; p g
pp == &g;
&g; 0x91A2 'z'
return
return 0;
0; 0x91A2
}}
Rules

 Pointers may only point to variables of the same


type as the pointer has been declared to point to
 A pointer to an int may only point to an int
– not to char, short int or long int, certainly not to float,
double or long double
 A pointer to a double may only point to a double
– not to float or long double, certainly not to char or any of
the integers
 Etc......
int
int *p;
*p; /*
/* pp is
is aa pointer
pointer to
to an
an int
int */
*/
long
long large
large == 27L;
27L; /* large is a long
/* large is a long int,int,
initialised
initialised with
with 27
27 */
*/

pp == &large;
&large; /*
/* ERROR
ERROR */
*/
The “*” Operator

 The “*”, “points to” operator, finds the value at


the end of a pointer

#include
#include <stdio.h>
<stdio.h> p c
char
char gg == 'z';
'z'; 0x1132 'a'
0x1132
int
int main(void)
main(void)
{{
char cc == 'a'; p g
char 'a';
char
char *p;
*p; 0x91A2 'z'
pp == &c; 0x91A2
&c;
printf("%c\n",
printf("%c\n", *p);
*p);
pp == &g;
&g;
print “what p points to”
printf("%c\n",
printf("%c\n", *p);
*p);
return
return 0;
0; aa
}} zz
Writing Down Pointers

 It is not only possible to read the values at the


end of a pointer as with:
char
char cc == 'a';
'a';
char
char *p;
*p;
pp == &c;
&c;
printf("%c\n",
printf("%c\n", *p);
*p);

 It is possible to write over the value at the end of


a pointer:
p c
char
char cc == 'a';
'a';
char *p; 0x1132 'a' 'b'
char *p; 0x1132
pp == &c;
&c;
*p
*p == 'b';
'b'; make what p points to
printf("%c\n",
printf("%c\n", *p);
*p); equal to ‘b’
Initialisation Warning!

 The following code contains a horrible error:

#include p i
#include <stdio.h>
<stdio.h>
? 13
int
int main(void)
main(void) 0x1212
{{
short
short ii == 13;
13;
short
short *p;
*p;

*p
*p == 23;
23;
printf("%hi\n",
printf("%hi\n", *p);
*p);

return
return 0;
0;
}}
Initialise Pointers!

 Pointers are best initialised!


 A pointer may be declared and initialised in a
single step
short
short ii == 13;
13;
short
short *p
*p = &i;
= &i;

 This does NOT mean “make what p points to


equal to the address of i”
 It DOES mean “declare p as a pointer to a short
int, make p equal to the address of i”
short
short *p
*p == &i;
&i;
short
short *p
*p == &i;
&i;
short
short *p
*p == &i;
&i;
NULL

 A special invalid pointer value exists #defined in


various header files, called NULL
 When assigned to a pointer, or when found in a
pointer, it indicates the pointer is invalid
#include
#include <stdio.h>
<stdio.h>
int
int main(void)
main(void)
{{
short
short ii == 13;
13;
short
short *p
*p = NULL;
= NULL;
if(p
if(p ==
== NULL)
NULL)
printf("the
printf("the pointer
pointer is
is invalid!\n");
invalid!\n");
else
else
printf("the
printf("the pointer
pointer points
points to
to %hi\n",
%hi\n", *p);
*p);
return
return 0;
0;
}}
A World of Difference!

 There is a great deal of difference between:


p i
int
int ii == 10,
10, jj == 14;
14;
int *p 0x15A0 10 14
int *p == &i;
&i; 0x15A0
int
int *q = &j;
*q = &j;
q j
*p
*p == *q;
*q;
0x15A4 14
0x15A4

and:
p i
int
int ii == 10,
10, jj == 14;
14;
int
int *p
*p == &i;
&i; 0x15A0 10
int
int *q
*q == &j;
&j; 0x15A4 0x15A0
q j
pp == q;
q;
0x15A4 14
0x15A4
Fill in the Gaps

int
int main(void)
main(void)
{{ i
int
int ii == 10,
10, jj == 14,
14, k;
k; 0x2100
int
int *p = &i;
*p = &i;
int
int *q
*q == &j;
&j; j
0x2104
*p
*p +=
+= 1;
1;
k
pp == &k;
&k; 0x1208
*p
*p == *q;
*q; p
pp == q;
q; 0x120B

*p q
*p == *q;
*q;
0x1210
return
return 0;
0;
}}
Type Mismatch

 The compiler will not allow type mismatches


when assigning to pointers, or to where pointers
point
p i
0x15A0 10
int
int ii == 10,
10, jj == 14;
14; 0x15A0
int
int *p
*p == &i;
&i; q j
int
int *q = &j;
*q = &j;
0x15A4 14
pp == *q;
*q; 0x15A4
*p
*p == q;
q;

cannot write
cannot write
0x15A4 into i
14 into p
Call by Value - Reminder
#include
#include <stdio.h>
<stdio.h>
void
void change(int
change(int v);
v);
int
int main(void)
main(void)
{{ the function
int
int var
var == 5;
5; was not able
change(var); to alter “var”
change(var);
printf("main:
printf("main: var
var == %i\n",
%i\n", var);
var);
return
return 0;
0;
}}
the function is
void change(int
void change(int v)v)
{{ able to alter “v”
vv *=
*= 100;
100;
printf("change:
printf("change: vv == %i\n",
%i\n", v);
v);
}} change:
change: vv == 500
500
main: var =
main: var = 5 5
Call by Reference
prototype “forces” us to pass a pointer
#include
#include <stdio.h>
<stdio.h>
void
void change(int*
change(int* p);
p);
int
int main(void)
main(void)
{{
int main: var
int var
var == 5;
5;
change(&var); 5
change(&var); 0x1120
printf("main:
printf("main: var
var == %i\n",
%i\n", var);
var);
return
return 0;
0; change: p
}}
0x1120
void
void change(int*
change(int* p)
p) 0x1124
{{
*p
*p *=
*= 100;
100;
printf("change:
printf("change: *p
*p == %i\n",
%i\n", *p);
*p);
}}
change:
change: *p
*p == 500
500
main: var = 500
main: var = 500
Pointers to Pointers

 C allows pointers to any type


 It is possible to declare a pointer to a pointer

pp is a “pointer to” a
#include
#include <stdio.h>
<stdio.h> “pointer to an int”
int
int main(void)
main(void)
{{
int
int ii == 16;
16;
int i
int *p = &i;
*p = &i; 16
int **pp;
int **pp; 0x2320

pp p
pp == &p;
&p; 0x2320
printf("%i\n",
printf("%i\n", **pp);
**pp); 0x2324
return
return 0;
0; pp
}} 0x2324
0x2328
Review

int
int main(void)
main(void) i j k
{{
int
int ii == 10,
10, jj == 7,
7, k;
k;
int
int *p
*p == &i;
&i;
int
int *q = &j;
*q = &j;
int *pp p q
int *pp == &p;
&p;
**pp
**pp +=
+= 1;
1;
*pp pp
*pp == &k;
&k;
**pp
**pp == *q;
*q;
ii == *q***pp;
*q***pp;

ii == *q/**pp;
*q/**pp; /*
/* headache?
headache? */;
*/;
return
return 0;
0;
}}
Arrays in C

 Declaring arrays
 Accessing elements
 Passing arrays into functions
 Using pointers to access arrays
 Strings
 The null terminator
Declaring Arrays

 An array is a collection of data items (called


elements) all of the same type
 It is declared using a type, a variable name and a
CONSTANT placed in square brackets
 C always allocates the array in a single block of
memory
 The size of the array, once declared, is fixed
forever - there is no equivalent of, for instance,
the “redim” command in BASIC
Examples
#define
#define SIZE
SIZE 10
10
int
int a[5];
a[5]; /*
/* aa is
is an
an array
array of
of 55 ints
ints */
*/
long
long int
int big[100];
big[100]; /* big is 400 bytes!
/* big is 400 bytes! */ */
double
double d[100];
d[100]; /*
/* but
but dd is
is 800
800 bytes!
bytes! */*/
long
long double
double v[SIZE];
v[SIZE]; /*
/* 10 long doubles, 100 bytes
10 long doubles, 100 bytes */
*/
all five
int a[5] == {{ 10, elements
int a[5] 10, 20,
20, 30,
30, 40,
40, 50
50 };
}; initialised
double
double d[100]
d[100] == {{ 1.5,
1.5, 2.7
2.7 };
};
short
short primes[]
primes[] = { 1, 2, 3, 5, 7,
= { 1, 2, 3, 5, 7, 11,
11, 13
13 };
};
long
long n[50]
n[50] = { 0
= { 0 };};
first two elements
compiler fixes initialised,
size at 7 remaining ones
int
int ii == 7;
7; elements set to zero
const int c =
const int c = 5;5;
int
int a[i];
a[i]; quickest way of setting
double
double d[c];
d[c]; ALL elements to zero
short
short primes[];
primes[];
Accessing Elements

 The elements are accessed via an integer which


ranges from 0..size-1
 There is no bounds checking
int
int main(void)
main(void)
{{
int
int a[6];
a[6]; a
int
int ii == 7;
7; 0
a[0]
a[0] == 59;
59; 1
a[5] = -10;
a[5] = -10;
a[i/2] 2
a[i/2] == 2;
2;
3
a[6]
a[6] == 0;
0;
a[-1]
a[-1] = 5;
= 5; 4

return
return 0;
0; 5
}}
Array Names

 There is a special and unusual property of array


names in C
 The name of an array is a pointer to the start of
the array, i.e. the zeroth element, thus
a == &a[0]

int
int a[10];
a[10];
int
int *p;
*p;
float
float f[5]
f[5] p a
float *fp;
float *fp;
pp == a; /* fp f
a; /* pp == &a[0]
&a[0] */
*/
fp
fp == f;
f; /*
/* fp
fp == &f[0]
&f[0] */
*/
Passing Arrays to Functions

 When an array is passed to a function a pointer to


the zeroth element is passed across
 The function may alter any element
 The corresponding parameter may be declared as
a pointer, or by using the following special syntax

int
int add_elements(int
add_elements(int a[],
a[], int
int size)
size)
{{

int
int add_elements(int
add_elements(int *p,
*p, int
int size)
size)
{{
Example
primes
#include
#include <stdio.h>
<stdio.h> 1
void
void sum(long
sum(long [],
[], int);
int); 2
int
int main(void)
main(void)
{{ 3
long
long primes[6]
primes[6] == {{ 1,
1, 2,
2,
3, 5
3, 5, 7, 11
5, 7, 11 };
};
sum(primes,
sum(primes, 6);
6);
7
printf("%li\n",
printf("%li\n", primes[0]);
primes[0]); 11
return
return 0;
0; a
}}
void
void sum(long
sum(long a[],
a[], int
int sz)
sz)
{{ sz 6
int
int i;i;
long
long total == 0;
total 0;
for(i provides bounds checking
for(i == 0;
0; ii << sz;
sz; i++)
i++)
total += a[i];
total += a[i];
a[0]
a[0] == total;
total; the total is written over
}}
element zero
Using Pointers

 Pointers may be used to access array elements


rather than using constructs involving “[ ]”
 Pointers in C are automatically scaled by the size
of the object pointed to when involved in
arithmetic
long
long v[6]
v[6] == {{ 1,2,
1,2,
3,4,5,6
3,4,5,6 };};
long p += 4
long *p;*p; p++
pp == v;
v;
printf("%ld\n",
printf("%ld\n", *p); *p);
p++; p
p++;
printf("%ld\n",
printf("%ld\n", *p); *p); 1000 1 2 3 4 5 6
pp += 11 v
+= 4;
4; 1000 1008 1016
printf("%ld\n", 2
printf("%ld\n", *p); *p); 2 1004 1012 1020
66
Pointers Go Backwards Too

 Scaling not only happens when addition is done,


it happens with subtraction too

long
long v[6]
v[6] == {{ 1,2,
1,2,
3,4,5,6
3,4,5,6 };
};
long
long *p;*p;
pp == vv ++ 5;
5; p--
p-=2
printf("%ld\n",
printf("%ld\n", *p); *p);
p--; p
p--;
printf("%ld\n",
printf("%ld\n", *p); *p); 1020 1 2 3 4 5 6
pp -= 2; 66 v
-= 2; 1000 1008 1016
printf("%ld\n", 55
printf("%ld\n", *p); *p); 1004 1012 1020
33
Pointers May be Subtracted

 When two pointers into the same array are


subtracted C scales again, giving the number of
array elements separating them

double
double d[7]
d[7] == {{ 1.1,
1.1, 2.2,
2.2, p1 p2
3.3,
3.3, 4.4, 5.5, 6.6, 7.7
4.4, 5.5, 6.6, 7.7 };
};
double 2008 2048
double *p1;
*p1;
double
double *p2;
*p2;

p1 d 1.1 2.2 3.3 4.4 5.5 6.6 7.7


p1 == dd ++ 1;
1;
p2 = d +
p2 = d + 6; 6;
2000 2016 2032 2048
2008 2024 2040
printf("%i\n",
printf("%i\n", p2
p2 -- p1);
p1);
55
Using Pointers - Example
#include
#include <stdio.h>
<stdio.h>
long
long sum(long*,
sum(long*, int);
int); primes
int
int main(void)
main(void) 1 1000
{{
long
long primes[6]
primes[6] == {{ 1,
1, 2,
2, 2 1004
3,
3, 5, 7, 11 };
5, 7, 11 };
3 1008
printf("%li\n",
printf("%li\n", sum(primes,
sum(primes, 6));
6));
5 1012
return
return 0;
0;
}} 7 1016
long
long sum(long
sum(long *p,
*p, int
int sz)
sz) 11 1020
{{
long 1024
long *end
*end == pp ++ sz;
sz;
long
long total
total == 0;
0; p
while(p
while(p << end)
end) 1000
total
total +=
+= *p++;
*p++; end 1024
return
return total;
total;
}}
* and ++

*p++ means:
*p++ find the value at the end of the pointer
*p++ increment the POINTER to point to the
next element
(*p)++ means:
(*p)++ find the value at the end of the pointer
(*p)++ increment the VALUE AT THE END OF THE
POINTER (the pointer never moves)
*++p means:
*++p increment the pointer
*++p find the value at the end of the pointer
Which Notation?

 An axiom of C states a[i] is equivalent to *(a + i)

short
short a[8]
a[8] == {{ 10,
10, 20,
20, 30,
30, 40,
40, 50,
50, 60,
60, 70,
70, 80
80 };
};
short *p = a;
short *p = a;

printf("%i\n",
printf("%i\n", a[3]);
a[3]);
printf("%i\n",
printf("%i\n", *(a ++ 3));
*(a 3));
printf("%i\n", *(p + 3)); 40
40
printf("%i\n", *(p + 3)); 40
printf("%i\n",
printf("%i\n", p[3]);
p[3]); 40
printf("%i\n", 40
40
printf("%i\n", 3[a]);
3[a]); 40
40
40
40

p a
1000 10 20 30 40 50 60 70 80
1000 1004 1008 1012
1002 1006 1010 1014
Strings

 C has no native string type, instead we use arrays


of char
 A special character, called a “null”, marks the
end (don’t confuse this with the NULL pointer )
 This may be written as ‘\0’ (zero not capital ‘o’)
 This is the only character whose ASCII value is
zero
 Depending on how arrays of characters are built,
we may need to add the null by hand, or the
compiler may add it for us
Example

char
char first_name[5]
first_name[5] == {{ 'J',
'J', 'o',
'o', 'h',
'h', 'n',
'n', '\0'
'\0' };
};
char
char last_name[6]
last_name[6] == "Minor";
"Minor";
char
char other[]
other[] == "Tony
"Tony Blurt";
Blurt";
char
char characters[7]
characters[7] == "No
"No null";
null";

this special case specifically


excludes the null terminator
first_name 'J' 'o' 'h' 'n' 0

last_name 'M' 'i' 'n' 'o' 'r' 0

other 'T' 'o' 'n' 'y' 32 'B' 'l' 'u' 'r' 't' 0

characters 'N' 'o' 32 'n' 'u' 'l' 'l'


Printing Strings

 Strings may be printed by hand


 Alternatively printf supports “%s”

char
char other[]
other[] == "Tony
"Tony Blurt";
Blurt";

char
char *p; *p; int
int ii == 0;
0;
pp == other;
other; while(other[i]
while(other[i] !=
!= '\0')
'\0')
while(*p
while(*p != != '\0')
'\0') printf("%c",
printf("%c", other[i++]);
other[i++]);
printf("%c",
printf("%c", *p++);
*p++); printf("\n");
printf("\n");
printf("\n");
printf("\n");

printf("%s\n",
printf("%s\n", other);
other);
Null Really Does Mark the End!

#include
#include <stdio.h>
<stdio.h>

int
int main(void)
main(void) even though the rest of
{{ the data is still there,
char
char other[]
other[] == "Tony
"Tony Blurt";
Blurt"; printf will NOT move
past the null terminator
printf("%s\n",
printf("%s\n", other);
other);

other[4]
other[4] == '\0';
'\0';

printf("%s\n",
printf("%s\n", other);
other);

return
return 0;
0;
}} Tony
Tony Blurt
Blurt
Tony
Tony

other 'T' 'o' 'n' 'y' 32 'B' 'l' 'u' 'r' 't' 0
Assigning to Strings

 Strings may be initialised with “=”, but not


assigned to with “=”
 Remember the name of an array is a CONSTANT
pointer to the zeroth element

#include
#include <stdio.h>
<stdio.h>
#include <string.h>
#include <string.h>
int
int main(void)
main(void)
{{
char
char who[]
who[] == "Tony
"Tony Blurt";
Blurt";
who
who == "John
"John Minor";
Minor";
strcpy(who,
strcpy(who, "John
"John Minor");
Minor");
return
return 0;
0;
}}
Pointing to Strings

 To save us declaring many character arrays to


store strings, the compiler can store them
directly in the data segment
 We need only declare a pointer
 The compiler may recycle some of these strings,
therefore we must NOT alter any of the characters

char
char *p
*p == "Data
"Data segment!!";
segment!!";
char *q = "nt!!";
char *q = "nt!!";
q 0xF10A

p 0xF100

'D' 'a' 't' 'a' 32 's' 'e' 'g' 'm' 'e' 'n' 't' '!' '!' 0
0xF100 0xF10A
Example
this utterly pointless statement causes the
compiler to store the characters, unfortunately
we forget to save the address
#include
#include <stdio.h>
<stdio.h>

int
int main(void)
main(void)
{{
char
char *p
*p == "a
"a string
string in
in the
the data
data segment\n";
segment\n";

"a
"a second
second string
string in
in the
the data
data segment\n";
segment\n";

printf("a
printf("a third
third string
string in
in the
the data
data segment\n");
segment\n");

printf("%s",
printf("%s", p);
p);

printf(p);
printf(p);

return aa third
third string
string in
in the
the data
data segment
return 0;
0; segment
}} aa string in the data segment
string in the data segment
aa string
string in
in the
the data
data segment
segment
Multidimensional Arrays

 C does not support multidimensional arrays


 However, C does support arrays of any type
including arrays of arrays

float “rainfall” is an array of 12


float rainfall[12][365];
rainfall[12][365];
arrays of 365 float

short “exam_marks” is an array of


short exam_marks[500][10];
exam_marks[500][10]; 500 arrays of 10 short int

const
const int
int brighton
brighton == 7;
7;
int day_of_year = 238;
int day_of_year = 238;

rainfall[brighton][day_of_year]
rainfall[brighton][day_of_year] == 0.0F;
0.0F;
Review

 How many times does the following program


loop?
#include
#include <stdio.h>
<stdio.h>
int
int main(void)
main(void)
{{
int
int i;
i;
int
int a[10];
a[10];
for(i
for(i == 0;
0; ii <=
<= 10;
10; i++)
i++) {{
printf("%d\n",
printf("%d\n", i); i);
a[i]
a[i] == 0;0;
}}
return
return 0;
0;
}}
Summary

 Arrays are declared with a type, a name, “[ ]” and


a CONSTANT
 Access to elements by array name, “[ ]” and an
integer
 Arrays passed into functions by pointer
 Pointer arithmetic
 Strings - arrays of characters with a null
terminator
 Sometimes compiler stores null for us (when
double quotes are used) otherwise we have to
store it ourselves
Structures in C

 Concepts
 Creating a structure template
 Using the template to create an instance
 Initializing an instance
 Accessing an instance’s members
 Passing instances to functions
 Linked lists
Concepts

 A structure is a collection of one of more


variables grouped together under a single name
for convenient handling
 The variables in a structure are called members
and may have any type, including arrays or other
structures
 The steps are:
– set-up a template (blueprint) to tell the compiler how to build
the structure
– Use the template to create as many instances of the structure
as desired
– Access the members of an instance as desired
Setting up the Template

 Structure templates are created by using the


struct keyword
struct
struct Library_member
Library_member
struct {{
struct Date
Date
{{ char
char name[80];
name[80];
int char address[200];
int day;
day; char address[200];
int month; long
long member_number;
member_number;
int month;
int float fines[10];
int year;
year; float fines[10];
}; struct Date dob;
struct Date dob;
};
struct
struct Date
Date enrolled;
enrolled;
};
};
struct
struct Book
Book
{{
struct
struct Library_book
Library_book
char
char title[80];
title[80]; {{
char author[80];
char author[80]; struct
struct Book
Book b;
b;
float
float price;
price; struct Date
struct Date due;
due;
char isbn[20];
char isbn[20]; struct Library_member *who;
struct Library_member *who;
};
}; };
};
Creating Instances

 Having created the template, an instance (or


instances) of the structure may be declared

struct
struct Date
Date
{{ instances must be
int
int day;
day; declared before the ‘;’ ...
int
int month;
month;
int year;
int year;
}} today,
today, tomorrow;
tomorrow;

struct ... or “struct Date” has


struct Date
Date next_monday;
next_monday; to be repeated
struct
struct Date
Date next_week[7];
next_week[7];

an array of 7
date instances
Initialising Instances

 Structure instances may be initialised using


braces (as with arrays)
int
int primes[7]
primes[7] == {{ 1,
1, 2,
2, 3,
3, 5,
5, 7,
7, 11,
11, 13
13 };
};

struct
struct Date
Date bug_day
bug_day == {{ 1,
1, 1,
1, 2000
2000 };
};

struct
struct Book
Book k_and_r
k_and_r == {{
"The
"The CC Programming
Programming Language
Language 2nd
2nd edition",
edition",
"Brian
"Brian W. Kernighan and Dennis M. Ritchie",
W. Kernighan and Dennis M. Ritchie",
31.95,
31.95,
"0-13-110362-8"
"0-13-110362-8" struct Book
struct Book
};
}; {
{
char title[80];
char title[80];
char
char author[80];
author[80];
float
float price;
price;
char
char isbn[20];
isbn[20];
};
};
Structures Within Structures

struct Library_member
struct Library_member
{
{
char name[80];
char
char
name[80];
address[200];
initialises first 4
char address[200];
long member_number; elements of array
long member_number;
float
float
fines[10];
fines[10]; “fines”, remainder are
struct Date dob;
struct
struct
Date
Date
dob;
enrolled;
initialised to 0.0
struct Date enrolled;
};
};
struct
struct Library_member
Library_member mm == {{
"Arthur
"Arthur Dent",
Dent",
"16 New Bypass",
"16 New Bypass",
42,
42,
{{ 0.10,
0.10, 2.58,
2.58, 0.13,
0.13, 1.10
1.10 },
}, initialises day, month
{{ 18,
18, 9,
9, 1959
1959 },
}, and year of “dob”
{{ 1,
1, 4,4, 1978
1978 }}
};
}; initialises day, month
and year of “enrolled”
Accessing Members

 Members are accessed using the instance name,


“.” and the member name
struct
struct Library_member
Library_member
{{
char
char name[80];
name[80];
char address[200];
char address[200];
long member_number;
long member_number;
float fines[10];
float fines[10];
struct
struct
Date
Date
dob;
dob; struct
struct Library_member
Library_member m;
m;
struct
struct Date
Date enrolled;
enrolled;
};
};
printf("name
printf("name == %s\n",
%s\n", m.name);
m.name);
printf("membership
printf("membership number == %li\n",
number %li\n", m.member_number);
m.member_number);
printf("fines:
printf("fines: ");
");
for(i
for(i = 0; i < 10 &&
= 0; i < 10 && m.fines[i]
m.fines[i] >> 0.0;
0.0; i++)
i++)
printf("£%.2f ", m.fines[i]);
printf("£%.2f ", m.fines[i]);
printf("\njoined
printf("\njoined %i/%i/%i\n",
%i/%i/%i\n", m.enrolled.day,
m.enrolled.day,
m.enrolled.month,
m.enrolled.month, m.enrolled.year);
m.enrolled.year);
Unusual Properties

 Structures have some very “un-C-like” properties, certainly


when considering how arrays are handled

Arrays Structures
Name is pointer to the structure itself
zeroth element
Passed to functions by pointer value or pointer

Returned from functions no way by value or pointer

May be assigned with “=” no way yes


Instances may be Assigned

 Two structure instances may be assigned to one


another via “=”
 All the members of the instance are copied
(including arrays or other structures)

struct
struct Library_member
Library_member mm == {{
"Arthur
"Arthur Dent",
Dent",
.....
.....
};
};
struct
struct Library_member
Library_member tmp;
tmp;
tmp
tmp == m;
m;
copies array “name”, array “address”,
long integer “member_number”, array
“fines”, Date structure “dob” and Date
structure “enrolled”
Passing Instances to Functions

 An instance of a structure may be passed to a


function by value or by pointer
 Pass by value becomes less and less efficient as
the structure size increases
 Pass by pointer remains efficient regardless of
the structure size
void
void by_value(struct
by_value(struct Library_member);
Library_member);
void
void by_reference(struct
by_reference(struct Library_member
Library_member *);
*);

by_value(m);
by_value(m);
by_reference(&m);
by_reference(&m);

compiler writes a pointer compiler writes 300+


(4 bytes?) onto the stack bytes onto the stack
Pointers to Structures

 Passing pointers to structure instances is more


efficient
 Dealing with an instance at the end of a pointer is
not so straightforward!
void
void member_display(struct
member_display(struct Library_member
Library_member *p)
*p)
{{
printf("name
printf("name == %s\n",
%s\n", (*p).name);
(*p).name);
printf("membership
printf("membership number == %li\n",
number %li\n", (*p).member_number);
(*p).member_number);
printf("fines:
printf("fines: ");
");
for(i
for(i = 0; i < 10 &&
= 0; i < 10 && (*p).fines[i]
(*p).fines[i] >> 0.0;
0.0; i++)
i++)
printf("£%.2f ", (*p).fines[i]);
printf("£%.2f ", (*p).fines[i]);
printf("\njoined
printf("\njoined %i/%i/%i\n",
%i/%i/%i\n", (*p).enrolled.day,
(*p).enrolled.day,
(*p).enrolled.month,
(*p).enrolled.month, (*p).enrolled.year);
(*p).enrolled.year);
}}
Why (*p).name ?

 The messy syntax is needed because “.” has


higher precedence than “*”, thus:
*p.name
means “what p.name points to” (a problem
because there is no structure instance “p”)
 As Kernighan and Ritchie foresaw pointers and
structures being used frequently they invented a
new operator
p->name = (*p).name
Using p->name

 Now dealing with the instance at the end of the


pointer is more straightforward

void
void member_display(struct
member_display(struct Library_member
Library_member *p)
*p)
{{
printf("name
printf("name == %s\n",
%s\n", p->name);
p->name);
printf("address
printf("address == %s\n",
%s\n", p->address);
p->address);
printf("membership
printf("membership number == %li\n",
number %li\n", p->member_number);
p->member_number);
printf("fines:
printf("fines: ");
");
for(i
for(i = 0; i < 10 &&
= 0; i < 10 && p->fines[i]
p->fines[i] >> 0.0;
0.0; i++)
i++)
printf("£%.2f ", p->fines[i]);
printf("£%.2f ", p->fines[i]);
printf("\njoined
printf("\njoined %i/%i/%i\n",
%i/%i/%i\n", p->enrolled.day,
p->enrolled.day,
p->enrolled.month,
p->enrolled.month, p->enrolled.year);
p->enrolled.year);
}}
Pass by Reference - Warning

 Although pass by reference is more efficient, the


function can alter the structure (perhaps
inadvertently)
 Use a pointer to a constant structure instead

void
void member_display(struct
member_display(struct Library_member
Library_member *p)*p)
{{ function alters
printf("fines: the library
printf("fines: "); "); member instance
for(i
for(i == 0;
0; ii << 10
10 &&
&& p->fines[i]
p->fines[i] == 0.0;
0.0; i++)
i++)
printf("£%.2f ", p->fines[i]);
printf("£%.2f ", p->fines[i]);
}}

void
void member_display(const
member_display(const struct
struct Library_member
Library_member *p)
*p)
{{
....
....
}}
Returning Structure Instances

 Structure instances may be returned by value


from functions
 This can be as inefficient as with pass by value
 Sometimes it is convenient!
struct
struct Complex
Complex add(struct
add(struct Complex
Complex a,
a, struct
struct Complex
Complex b)
b)
{{
struct
struct Complex
Complex result
result == a;
a;
result.real_part
result.real_part +=
+= b.real_part;
b.real_part;
result.imag_part += b.imag_part;
result.imag_part += b.imag_part;
return
return result;
result;
}} struct
struct Complex
Complex c1
c1 == {{ 1.0,
1.0, 1.1
1.1 };
};
struct Complex
struct Complex c2 = { 2.0, 2.1
c2 = { 2.0, 2.1 }; };
struct
struct Complex
Complex c3;
c3;
c3
c3 == add(c1,
add(c1, c2);
c2); /*
/* c3
c3 == c1
c1 ++ c2
c2 */
*/
Linked Lists

 A linked list node containing a single forward


pointer may be declared as follows
struct
struct Node
Node {{
int
int data;
data; /*
/* or
or whatever
whatever */
*/
struct
struct Node
Node *next_in_line;
*next_in_line;
};
}; pointer to next
Node structure

 A linked list node containing a forward and a


backward pointer may be declared as follows
struct
struct Node
Node {{ pointer to next
int
int data;
data; Node structure
struct
struct Node
Node *next_in_line;
*next_in_line;
struct Node
struct Node *previous_in_line;
*previous_in_line; pointer to previous
};
}; Node structure
Example
#include
#include <stdio.h>
<stdio.h>

struct
struct Node
Node {{
char
char name[10];
name[10];
struct
struct Node
Node *next_in_line;
*next_in_line;
};
};

struct
struct Node
Node a1
a1 == {{ "John",
"John", NULL
NULL };
};
struct
struct Node
Node a2
a2 == {{ "Harriet",
"Harriet", &a1
&a1 },},
struct Node a3 = { "Claire",
struct Node a3 = { "Claire", &a2 }&a2 }
struct
struct Node
Node a4
a4 == {{ "Tony",
"Tony", &a3
&a3 };
};

a4 a3 a2 a1
Tony\0 Claire\0 Harriet\0 John\0

0x1020 0x102E 0x1032 NULL


0x1012 0x1020 0x102E 0x1032
Printing the List

 The list may be printed with the following code:

struct
struct Node
Node ** current
current == &a4;
&a4;

while(current
while(current !=
!= NULL)
NULL) {{
printf("%s\n",
printf("%s\n", current->name);
current->name);
current
current = current->next_in_line;
current = current->next_in_line;
0x1012 }}

a4
Tony\0 Claire\0 Harriet\0 John\0

0x1020 0x102E 0x1032 NULL


0x1012 0x1020 0x102E 0x1032
Summary

 Creating structure templates using struct


 Creating and initialising instances
 Accessing members
 Passing instances to functions by value and by
reference
 A new operator: “->”
 Return by value
 Linked lists
Reading C Declarations

 Introduction
 SOAC
 Examples
 typedef
 Examples revisited
Introduction

 Up until now we have seen straightforward


declarations:
long
long sum;
sum;
int*
int* p;
p;

 Plus a few trickier ones:


void
void member_display(const
member_display(const struct
struct Library_member
Library_member *p);
*p);

 However, they can become much worse:


int
int *p[15];
*p[15];
float (*pfa)[23];
float (*pfa)[23];
long
long (*f)(char,
(*f)(char, int);
int);
double
double *(*(*n)(void))[5];
*(*(*n)(void))[5];
SOAC

 Find the variable being declared


 Spiral Outwards Anti Clockwise
 On meeting: say:
* pointer to
[] array of
() function taking .... and returning

 Remember to read “struct S”, “union U” or


“enum E” all at once
 Remember to read adjacent collections of [ ] [ ] all
at once
Example 1.

 What is “int * p[15]” ?

int * p [15] ;

 p is an array of 15 pointers to integers


Example 2.

 What is “double (*p)[38]” ?

double (* p ) [38];

 p is a pointer to an array of 38 doubles


Example 3.

 What is “short **ab[5][10]” ?

short * * ab [5][10] ;

 ab is an array of 5 arrays of 10 arrays of pointers


to pointers to short int
Example 4.

 What is “long * f(int, float)” ?

long * f (int, float) ;

 f is a function taking an int and a float returning a


pointer to a long int
Example 5.

 What is “int (*pf)(void)” ?

int ( * pf ) (void) ;

 pf is a pointer to a function taking no parameters


and returning an int
Example 6.

 What is “struct Book (*fpa[8])(void)” ?

struct Book ( * fpa[8] ) (void) ;

 fpa is an array of 8 pointers to functions, taking


no parameters, returning Book structures
Example 7.

 What is “char (*(*fprp)(void))[6]” ?

char ( * ( * fprp ) (void) ) [6] ;

 fprp is a pointer to a function taking no


parameters returning a pointer to an array of 6
char
Example 8.

 What is “int * (*(*ptf)(int))(char)” ?

int * ( * ( * ptf ) (int) ) (char) ;

 ptf is a pointer to a function, taking an integer,


returning a pointer to a function, taking a char,
returning a pointer to an int
typedef

 It doesn’t have to be this difficult!


 The declaration can be broken into simpler steps
by using typedef
 To tackle typedef, pretend it isn’t there and read
the declaration as for a variable
 When finished remember that a type has been
declared, not a variable
Example 1 Revisited

 Simplify “int * p[15]”

typedef int * pti ; pti is a pointer to an int

pti p[15]; p is an array of 15


pointer to int
Example 3 Revisited

 Simplify “short **ab[5][10]”

typedef short * * pt_pt_s ; typedef pt_pt_s ao5[5];

ao5 is an array of 5 pointers


to pointers to short
pt_pt_s is a pointer to a
pointer to a short

ao5 ab[10]; ab is an array of 10 arrays of


5 pointers to pointers to short
Example 5 Revisited

 Simplify “int (*pf)(void)”

typedef int fri(void); fri * pf ;

fri is a function, taking no pf is a pointer to a function,


parameters, returning an int taking no parameters,
returning an int
Example 6 Revisited

 Simplify “struct Book (*fpa[8])(void)”

typedef struct Book f(void); typedef f * fp ;

f is a function, taking no fp is a pointer to a function,


parameters, returning a taking no parameters,
Book structure returning a Book structure

fpa is an array of 8 pointers


fp fpa[8]; to functions, taking no
parameters, returning a
Book structure
Example 7 Revisited

 Simplify “char (*(*fprp)(void))[6]”

typedef char ( * pta6c ) [6] ; typedef pta6c f(void);

f is a function, taking no
pta6c is a pointer to an
parameters, returning a
array of 6 char
pointer to an array of 6
char

fprp is a pointer to a
function, taking no
f * fprp ;
parameters, returning a
pointer to an array of 6
char
Example 8 Revisited

 Simplify “int * (*(*ptf)(int))(char)”

typedef int * pti ; typedef pti f(char);

f is a function, taking a
pti is a pointer to an int char, returning a pointer
to an int

typedef f * ptfri ; ptfri ( * ptf )(int) ;

ptfri is a pointer to a ptf is a pointer to a function, taking int,


function, taking a char, returning a pointer to a function, taking a
returning a pointer to an int char, returning a pointer to an int
Summary

 Don’t Panic!
 SOAC - Spiral Outwards Anti Clockwise
 To simplify, use typedef(s)
Handling Files in C

 Streams
 stdin, stdout, stderr
 Opening files
 When things go wrong - perror
 Copying files
 Accessing the command line
 Dealing with binary files
Introduction

 File handling is not built into the C language itself


 It is provided by The Standard Library (via a set
of routines invariably beginning with “f”)
 Covered by The Standard, the routines will
always be there and work the same way,
regardless of hardware/operating system
 Files are presented as a sequence of characters
 It is easy to move forwards reading/writing
characters, it is less easy (though far from
impossible) to go backwards
Streams

 Before a file can be read or written, a data


structure known as a stream must be associated
with it
 A stream is usually a pointer to a structure
(although it isn’t necessary to know this)
 There are three streams opened by every C
program, stdin, stdout and stderr
 stdin (standard input) is connected to the
keyboard and may be read from
 stdout (standard output) and stderr (standard
error) are connected to the screen and may be
written to
What is a Stream?

 Although implementations vary, a stream creates


a buffer between the program running in memory
and the file on the disk
 This reduces the program’s need to access slow
hardware devices
 Characters are silently read a block at a time into
the buffer, or written a block at a time to the file

a b c d e f g h i j k l

output stream

a b c d e f g h i j

input stream
Why stdout and stderr?

 There are two output streams because of


redirection, supported by Unix, DOS, OS/2 etc.

#include
#include <stdio.h>
<stdio.h>
int
int main(void)
main(void)
{{ output written to
printf("written
printf("written to
to stdout\n");
stdout\n"); stderr first
fprintf(stderr,
fprintf(stderr, "written
"written to
to stderr\n");
stderr\n"); because it is
C:> unbuffered
return
return 0;
0; C:> outprog
outprog
}} written
written to
to stderr
stderr
written to stdout
written to stdout
C:>
C:> outprog
outprog >> file.txt
file.txt
written
written to
to stderr
stderr
C:> type file.txt
C:> type file.txt
written
written to
to stdout
stdout
stdin is Line Buffered

 Characters typed at the keyboard are buffered


until Enter/Return is pressed
C:>
C:> inprog
inprog
#include
#include <stdio.h>
<stdio.h> abc
abc
int read
read 'a'
int main(void)
main(void) 'a'
{{ read
read 'b'
'b'
int ch; read
read 'c'
'c'
int ch;
read '
read '
while((ch
while((ch == getchar())
getchar()) !=
!= EOF)
EOF) ''
printf("read
printf("read '%c'\n", ch);
'%c'\n", ch); dd
printf("EOF\n"); read
read 'd'
'd'
printf("EOF\n");
read
read ''
return
return 0;
0; ''
}} ^Z
^Z
EOF
EOF
declared as an int, even though C:>
C:>
we are dealing with characters
Opening Files

 Files are opened and streams created with the


fopen function
FILE*
FILE* fopen(const
fopen(const char*
char* name,
name, const
const char*
char* mode);
mode);

#include
#include <stdio.h>
<stdio.h>

int
int main(void)
main(void)
{{ streams, you’ll
FILE* in; need one for each
FILE* in;
FILE* out; file you want
FILE* out;
FILE* append; open
FILE* append;

in
in == fopen("autoexec.bat",
fopen("autoexec.bat", "r");
"r");
out
out = fopen("autoexec.bak", "w");
= fopen("autoexec.bak", "w");
append
append = fopen("config.sys", "a");
= fopen("config.sys", "a");
Dealing with Errors

 fopen may fail for one of many reasons, how to


tell which?
void
voidperror(const
perror(const char*
char* message);
message);

#include
#include <stdio.h>
<stdio.h>
int
int main(void)
main(void)
{{
FILE*
FILE* in;
in;
if((in
if((in == fopen("autoexec.bat",
fopen("autoexec.bat", "r"))
"r")) ==
== NULL)
NULL) {{
fprintf(stderr,
fprintf(stderr, "open
"open of
of autoexec.bat
autoexec.bat failed
failed ");
");
perror("because");
perror("because");
return
return 1;1;
}}
open
open of
of autoexec.bat
autoexec.bat failed
failed because:
because: No
No such
such file
file or
or directory
directory
File Access Problem

 Can you see why the following will ALWAYS fail,


despite the file existing and being fully
accessible?
if((in
if((in == fopen("C:\autoexec.bat",
fopen("C:\autoexec.bat", "r"))
"r")) ==
== NULL)
NULL) {{
fprintf(stderr,
fprintf(stderr, "open
"open of
of autoexec.bat
autoexec.bat failed
failed ");
");
perror("because");
perror("because");
return
return 1;1;
}}
C:>
C:> dir
dir C:\autoexec.bat
C:\autoexec.bat
Volume
Volume in drive
in drive CC is
is MS-DOS_62
MS-DOS_62
Directory
Directory ofof C:\
C:\
autoexec
autoexec bat
bat 805
805 29/07/90
29/07/90 8:15
8:15
11 file(s)
file(s) 805
805 bytes
bytes
1,264,183,808
1,264,183,808 bytes free
bytes free
C:>
C:> myprog
myprog
open
open of
of autoexec.bat
autoexec.bat failed
failed because:
because: No
No such
such file
file or
or directory
directory
Displaying a File
#include
#include <stdio.h>
<stdio.h>
int
int main(void)
main(void)
{{
char
char in_name[80];
in_name[80];
FILE *in_stream;
FILE *in_stream;
int
int ch;
ch;
printf("Display
printf("Display file:
file: ");
");
scanf("%79s", in_name);
scanf("%79s", in_name);
if((in_stream
if((in_stream == fopen(in_name,
fopen(in_name, "r"))
"r")) ==== NULL)
NULL) {{
fprintf(stderr,
fprintf(stderr, "open
"open of
of %s
%s for
for reading
reading failed
failed ",
", in_name);
in_name);
perror("because");
perror("because");
return
return 1;
1;
}}
while((ch
while((ch == fgetc(in_stream))
fgetc(in_stream)) !=
!= EOF)
EOF)
putchar(ch);
putchar(ch);
fclose(in_stream);
fclose(in_stream);
return
return 0;
0;
}}
Example - Copying Files
#include <stdio.h>
#include <stdio.h>
int main(void)
int main(void)
{
{
char
char in_name[80],
in_name[80], out_name[80];
out_name[80];
FILE
FILE *in_stream,
*in_stream, *out_stream;
*out_stream;
int
int ch;
ch;
printf("Source
printf("Source file:
file: ");
"); scanf("%79s",
scanf("%79s", in_name);
in_name);
if((in_stream
if((in_stream = fopen(in_name, "r")) == NULL)
= fopen(in_name, "r")) == NULL) {{
fprintf(stderr,
fprintf(stderr, "open
"open of
of %s
%s for
for reading
reading failed
failed ",
", in_name);
in_name);
perror("because");
perror("because");
return 1;
return 1;
}
}
printf("Destination file: "); scanf("%79s", out_name);
printf("Destination file: "); scanf("%79s", out_name);
if((out_stream = fopen(out_name, "w")) == NULL) {
if((out_stream = fopen(out_name, "w")) == NULL) {
fprintf(stderr, "open of %s for writing failed ", out_name);
fprintf(stderr, "open of %s for writing failed ", out_name);
perror("because");
perror("because");
return
return 1;
1;
}}
while((ch = fgetc(in_stream)) != EOF)
while((ch = fgetc(in_stream)) != EOF)
fputc(ch, out_stream);
fputc(ch, out_stream);
fclose(in_stream);
fclose(in_stream);
fclose(out_stream);
fclose(out_stream);
return
return 0;
0;
}}
Convenience Problem

 Although our copy file program works, it is not as


convenient as the “real thing”
C:>
C:> copyprog
copyprog
Source
Source file:
file: \autoexec.bat
\autoexec.bat
Destination
Destination file: \autoexec.bak
file: \autoexec.bak
C:> dir C:\autoexec.*
C:> dir C:\autoexec.*
Volume
Volume in
in drive
drive CC is
is MS-DOS_62
MS-DOS_62
Directory
Directory ofof C:\
C:\
autoexec
autoexec bak
bak 805
805 31/12/99
31/12/99 12:34
12:34
autoexec
autoexec bat
bat 805
805 29/07/90
29/07/90 8:15
8:15
22 file(s)
file(s) 1610 bytes
1610 bytes
1,264,183,003
1,264,183,003 bytes
bytes free
free
C:> copyprog \autoexec.bat \autoexec.000
C:> copyprog \autoexec.bat \autoexec.000
Source
Source file:
file:

program still prompts despite begin given file


names on the command line
Accessing the Command Line

 The command line may be accessed via two


parameters to main, by convention these are
called “argc” and “argv”
 The first is a count of the number of words -
including the program name itself
 The second is an array of pointers to the words

int
int main(int
main(int argc,
argc, char
char *argv[])
*argv[])

argc 3 argv c o p y p r o g . e x e \0
\ a u t o e x e c . b a t \0
\ a u t o e x e c . 0 0 0 \0
NULL
Example

#include
#include <stdio.h>
<stdio.h>
int
int main(int
main(int argc,
argc, char
char *argv[])
*argv[])
{{
int
int j;
j;
for(j
for(j == 0;
0; jj << argc;
argc; j++)
j++)
printf("argv[%i]
printf("argv[%i] == \"%s\"\n",
\"%s\"\n", j,
j, argv[j]);
argv[j]);
return
return 0;
0;
}}
C:>
C:> argprog
argprog one
one two
two three
three
argv[0] = "C:\cct\course\cprog\files\slideprog\argprog.exe"
argv[0] = "C:\cct\course\cprog\files\slideprog\argprog.exe"
argv[1]
argv[1] == "one"
"one"
argv[2] = "two"
argv[2] = "two"
argv[3]
argv[3] == "three"
"three"
Useful Routines

 File reading routines:


int
int fscanf(FILE*
fscanf(FILE* stream,
stream, const
const char*
char* format,
format, ...);
...);
int
int fgetc(FILE*
fgetc(FILE* stream);
stream);
char*
char* fgets(char*
fgets(char* buffer,
buffer, int
int size,
size, FILE*
FILE* stream);
stream);

 File writing routines:


int
int fprintf(FILE*
fprintf(FILE* stream,
stream, const
const char*
char* format,
format, ...);
...);
int
int fputc(int ch, FILE* stream);
fputc(int ch, FILE* stream);
int
int fputs(const
fputs(const char*
char* buffer,
buffer, FILE*
FILE* stream);
stream);
Example

long
long l1,
l1, l2;
l2;
int
int j,
j, ch;
ch; example input
double
double d;
d;
float f; 28.325|9000000:68000/13
28.325|9000000:68000/13
float f;
char
char buf[200];
buf[200];
in
in == fopen("in.txt",
fopen("in.txt", "r")
"r") ....
....
out
out = fopen("out.txt", "w") ....
= fopen("out.txt", "w") ....
9000000:13:28.33
9000000:13:28.33
fscanf(in,
fscanf(in, "%lf|%li:%li/%i",
"%lf|%li:%li/%i", &d,
&d, &l1,
&l1, &l2,
&l2, &j);
&j);
fprintf(out, "%li:%i:%.2lf\n", l1, j,
fprintf(out, "%li:%i:%.2lf\n", l1, j, d);d);

fgetc(in); ignore next character


fgetc(in); in input file (newline?)
fgets(buf,
fgets(buf, sizeof(buf),
sizeof(buf), in);
in);
fputs(buf, out);
fputs(buf, out); read next line, or next
199 characters,
whichever is less
write that line to the output file (null terminator
provided by fgets tells fputs how long the line was)
Binary Files

 The Standard Library also allows binary files to


be manipulated
– “b” must be added into the fopen options
– Character translation is disabled
– Random access becomes easier
– Finding the end of file can become more difficult
– Data is read and written in blocks

size_t
size_t fread(void*
fread(void* p,
p, size_t
size_t size,
size, size_t
size_t n,
n, FILE*
FILE* stream);
stream);
size_t
size_t fwrite(const void* p, size_t size, size_t n, FILE*
fwrite(const void* p, size_t size, size_t n, FILE* stream);
stream);
int
int fseek(FILE*
fseek(FILE* stream,
stream, long
long offset,
offset, int
int whence);
whence);
long
long ftell(FILE* stream);
ftell(FILE* stream);
void
void rewind(FILE*
rewind(FILE* stream);
stream);
int
int fgetpos(FILE*
fgetpos(FILE* stream,
stream, fpos_t*
fpos_t* pos);
pos);
int
int fsetpos(FILE*
fsetpos(FILE* stream, const fpos_t* pos);
stream, const fpos_t* pos);
Example
double
double d;
d; read one chunk
long
long double
double lda[35];
lda[35]; of 8 bytes
fpos_t
fpos_t where;
where;

in
in == fopen("binary.dat",
fopen("binary.dat", "rb");
"rb"); remember current
out
out == fopen("binnew.dat",
fopen("binnew.dat", "wb");
"wb"); position in file

fread(&d,
fread(&d, sizeof(d),
sizeof(d), 1,
1, in);
in);

fgetpos(in, read one chunk


fgetpos(in, &where);
&where); of 350 bytes
fread(lda,
fread(lda, sizeof(lda),
sizeof(lda), 1,
1, in);
in);

fsetpos(in, return to previous


fsetpos(in, &where);
&where); position
fread(lda,
fread(lda, sizeof(long double),
sizeof(long double), 35,
35, in);
in);

fwrite(lda,
fwrite(lda, sizeof(long
sizeof(long double),
double), 20,
20, out);
out); read 35 chunks
of 10 bytes
fseek(in,
fseek(in, 0L,
0L, SEEK_END);
SEEK_END);
write 20 long
move to end of binary.dat doubles from lda
Summary

 Streams stdin, stdout, stderr


 fopen opening text files
 functions: perror, fprintf, fscanf, fgetc,
fputc
 variables: argc, argv
 “b” option to fopen to open binary files
 functions: fread, fwrite, fseek, ftell
Miscellaneous Things

 Unions
 Enumerated types
 The Preprocessor
 Working with multiple .c files
Unions

 A union is a variable which, at different times,


may hold objects of different types and sizes

s
struct
struct SS union
union UU
{{ {{ u
short
short s;
s; short s;
short s;
long
long l;
l; long l;
long l;
double
double d;
d; double d;
double d;
char
char c;
c; char c;
char c;
}} s;
s; }} u;
u;
s.s
s.s == 10; u.s
10; u.s == 10;
10;
s.l = 10L;
s.l = 10L; u.l = 10L;
u.l = 10L;
s.d
s.d == 10.01; u.d
10.01; u.d == 10.01;
10.01;
s.c = '1';
s.c = '1'; u.c = '1';
u.c = '1';
Remembering

 It is up to the programmer to remember what type


a union currently holds
 Unions are most often used in structures where a
member records the type currently stored
struct #define
#define N_SIZE 10
struct preprocessor_const
preprocessor_const N_SIZE 10
{{ #define PI
#define PI 3.1416
3.1416
char*
char* name;
name;
int
int stored;
stored;
union struct
struct preprocessor_const
preprocessor_const s[10000];
s[10000];
union
{{
s[0].name
s[0].name == "N_SIZE";
"N_SIZE";
long
long lval;
lval; s[0].u.lval
s[0].u.lval = 10L;
= 10L;
double
double dval;
dval; s[0].stored = STORED_LONG;
s[0].stored = STORED_LONG;
char*
char* sval;
sval;
}} u;
u; s[1].name
s[1].name == "PI";
"PI";
};
}; s[1].u.dval
s[1].u.dval = 3.1416;
= 3.1416;
s[1].stored = STORED_DOUBLE;
s[1].stored = STORED_DOUBLE;
Enumerated Types

 Enumerated types provide an automated


mechanism for generating named constants

#define
#define sun
sun 00
#define mon
#define mon 11
enum
enum day
day {{ sun,
sun, mon,
mon, tue, #define
wed,
tue, #define tue
tue 22
wed, thu,
thu, fri,
fri, sat
sat };
}; #define wed
#define wed 33
#define
#define thu
thu 44
enum
enum day
day today
today == sun; #define
sun; #define fri
fri 55
#define sat
#define sat 66
if(today
if(today ==
== mon)
mon)
.... int
.... int today
today == sun;
sun;

if(today
if(today ==
== mon)
mon)
....
....
Using Different Constants

 The constants used may be specified

enum
enum day
day {{ sun
sun == 5,
5, mon,
mon, tue,
tue, wed,
wed, thu,
thu, fri,
fri, sat
sat };
};
enum
enum direction
direction {{ north
north == 0,
0, east
east == 90,
90, south
south == 180,
180,
west
west = 270 };
= 270 };

 What you see is all you get!


 There are no successor or predecessor functions
The Preprocessor

 Preprocessor commands start with ‘#’ which may


optionally be surrounded by spaces and tabs
 The preprocessor allows us to:
– include files
– define, test and compare constants
– write macros
– debug
Including Files

 The #include directive causes the preprocessor


to “edit in” the entire contents of another file

#define
#define JAN
JAN 11
#define FEB
#define FEB 22
#define
#define MAR 33 #define
MAR #define JAN
JAN 11
#define FEB
#define FEB 22
#define
#define PI 3.1416 #define
PI 3.1416 #define MAR
MAR 33

double
double my_global; #define
my_global; #define PI
PI 3.1416
3.1416
mydefs.h double
double my_global;
my_global;

#include double
double angle
angle == 22 ** 3.1416;
#include "mydefs.h"
"mydefs.h" 3.1416;
printf("%s", month[2]);
printf("%s", month[2]);
double
double angle
angle == 22 ** PI;
PI;
printf("%s", month[FEB]); myprog.i
printf("%s", month[FEB]);

myprog.c
Pathnames

 Full pathnames may be used, although this is not


recommended
#include
#include "C:\cct\course\cprog\misc\slideprog\header.h"
"C:\cct\course\cprog\misc\slideprog\header.h"

 The “I” directive to your local compiler allows


code to be moved around much more easily
#include
#include "header.h"
"header.h"

cc
cc -I
-I c:\cct\course\cprog\misc\slideprog
c:\cct\course\cprog\misc\slideprog myprog.c
myprog.c
Preprocessor Constants

 Constants may be created, tested and removed

#if
#if !defined(SUN)
!defined(SUN) if “SUN” is not defined, then begin
#define
#define SUN
SUN 00 define “SUN” as zero
#endif
#endif end

#if if “SUN” and “MON” are equal, then begin


#if SUN
SUN ==
== MON
MON
#undef
#undef SUN
SUN remove definition of “SUN”
#endif
#endif end

#if
#if TUE
TUE if “TUE” is defined with a non zero value

#if if “WED” is greater than zero or “SUN” is


#if WED
WED >> 00 ||
|| SUN
SUN << 33 less than 3

#if if “SUN” is greater than “SAT” and “SUN”


#if SUN
SUN >> SAT
SAT &&
&& SUN
SUN >> MON
MON is greater than “MON”
Avoid Temptation!

 The following attempt to write Pascal at the C


compiler will ultimately lead to tears

#define
#define begin
begin {{
#define
#define end
end ;}
;}
#define
#define if
if if(
if(
#define
#define then
then ))
#define
#define integer
integer int
int

integer
integer i;
i; int
int i;
i;
if
if ii >> 00 then
then begin
begin if(
ii == 17 if( ii >> 00 )) {{
17 ii == 17
17
end
end ;}
;}
Preprocessor Macros

 The preprocessor supports a macro facility which


should be used with care

#define
#define MAX(A,B)
MAX(A,B) AA >> BB ?? AA :: BB
#define
#define MIN(X,Y)
MIN(X,Y) ((X)
((X) << (Y)
(Y) ?? (X)(X) :: (Y))
(Y))

int
int ii == 10,
10, jj == 12,
12, k;
k;

kk == MAX(i,
MAX(i, j);
j); printf("k
printf("k == %i\n",
%i\n", k);
k);
kk == MAX(j,
MAX(j, i) ** 2;
i) 2; printf("k = %i\n", k);
printf("k = %i\n", k);
kk == MIN(i, j) * 3;
MIN(i, j) * 3; printf("k
printf("k == %i\n",
%i\n", k);
k); k = 12
kk == MIN(i--,
MIN(i--, j++);
j++); printf("i = %i\n", i); kk == 12
printf("i = %i\n", i); 12
k = 12
kk == 30
30
ii == 88
A Debugging Aid

 Several extra features make the preprocessor an


indespensible debugging tool

#define
#define GOT_HERE
GOT_HERE printf("reached
printf("reached %i%i in
in %s\n",
%s\n", \\
___LINE_
_LINE__,
_, ___FILE_
_FILE__)
_)

#define
#define SHOW(E,
SHOW(E, FMT)
FMT) printf(#E
printf(#E "" == "" FMT
FMT "\n",
"\n", E)
E)

printf("reached
printf("reached %i
%i in
in %s\n",
%s\n", 17,
17, "mysource.c");
"mysource.c");
GOT_HERE;
GOT_HERE;
SHOW(i,
SHOW(i, "%x");
"%x"); printf("i
printf("i == %x\n",
%x\n", i);
i);
SHOW(f/29.5,
SHOW(f/29.5, "%lf");
"%lf");
printf("f/29.5
printf("f/29.5 == %lf\n",
%lf\n", f/29.5);
f/29.5);
Working With Large Projects

 Large projects may potentially involve many


hundreds of source files (modules)
 Global variables and functions in one module
may be accessed in other modules
 Global variables and functions may be
specifically hidden inside a module
 Maintaining consistency between files can be a
problem
Data Sharing Example
extern
extern float
float step;
step;
void
void print_table(double,
print_table(double, float);
float);
int
int main(void)
main(void)
{{
step
step == 0.15F;
0.15F;

print_table(0.0,
print_table(0.0, 5.5F);
5.5F);
#include
#include <stdio.h>
<stdio.h>
return 0;
return 0;
}} float
float step;
step;
void
void print_table(double
print_table(double start,
start, float
float stop)
stop)
{{
printf("Celsius\tFarenheit\n");
printf("Celsius\tFarenheit\n");
for(;start
for(;start << stop;
stop; start
start +=
+= step)
step)
printf("%.1lf\t%.1lf\n",
printf("%.1lf\t%.1lf\n", start,
start,
start
start ** 1.8
1.8 ++ 32);
32);
}}
Data Hiding Example

 When static is placed before a global variable, or


function, the item is locked into the module
static
static int
int entries[S_SIZE];
entries[S_SIZE];
static void push(int
push(int value);
static int
int current;
current; void value);
int
int pop(void);
pop(void);
void push(int value)
void push(int value) void print(void);
void print(void);
{{ extern
extern int
int entries[];
entries[];
entries[current++] = value
entries[current++] = value
}} int
int main(void)
main(void)
{{
int
int pop(void) push(10);
pop(void) push(10); push(15);
push(15);
{{ printf("%i\n",
printf("%i\n", pop());
pop());
return entries[--current];
return entries[--current];
}} entries[3]
entries[3] == 77;
77;
static print();
static void
void print(void)
print(void) print();
{{ return
}} return 0;
0;
}}
Disaster!
extern
extern float
float step;
step;
void
void print_table(double,
print_table(double, float);
float);
int
int main(void)
main(void)
{{
step
step == 0.15F;
0.15F;

print_table(0.0,
print_table(0.0, 5.5F);
5.5F);
#include
#include <stdio.h>
<stdio.h>
return 0;
return 0;
}} double
double step;
step;
void
void print_table(double
print_table(double start,
start, double
double stop)
stop)
{{
printf("Celsius\tFarenheit\n");
printf("Celsius\tFarenheit\n");
for(;start
for(;start << stop;
stop; start
start +=
+= step)
step)
printf("%.1lf\t%.1lf\n",
printf("%.1lf\t%.1lf\n", start,
start,
start
start ** 1.8
1.8 ++ 32);
32);
}}
Use Header Files

 Maintain consistency between modules by using


header files
 NEVER place an extern declaration in a module
 NEVER place a prototype of a non static (i.e.
sharable) function in a module
Getting it Right

project.h
extern
extern double
double step;
step;
void
voidprint_table(double,
print_table(double, double);
double);

#include
#include "project.h"
"project.h" #include
#include <stdio.h>
<stdio.h>
#include "project.h"
#include "project.h"
int
int main(void)
main(void)
{{ double
double step;
step;
step
step == 0.15F;
0.15F; void
void print_table(double
print_table(double start,
start, double
double stop)
stop)
print_table(0.0,
print_table(0.0, 5.5F);
5.5F); {{

return
return 0;
0; }}
}}
Be as Lazy as Possible

 Get the preprocessor to declare the variables too!

#if
#if defined(MAIN)
defined(MAIN)
#define
#define EXTERN
EXTERN
#else
#else
#define
#define EXTERN
EXTERN extern
extern
#endif
#endif

EXTERN
EXTERN double
double step;
step;
EXTERN long
EXTERN long current;
current;
EXTERN
EXTERN short
short res;
res;

#define
#define MAIN
MAIN #include
#include "globals.h"
"globals.h" #include
#include "globals.h"
"globals.h"
#include
#include "globals.h"
"globals.h"
first.c second.c
main.c
Summary

 A union may store values of different types at


different times
 enum provides an automated way of setting up
constants
 The preprocessor allows constants and macros
to be created
 Data and functions may be shared between
modules
 static stops sharing of data and functions
 Use the preprocessor in large, multi module
projects
C and the Heap

 What is the Heap?


 Dynamic arrays
 The calloc/malloc/realloc and free routines
 Dynamic arrays of arrays
 Dynamic data structures
What is the Heap?

 An executing program is divided into four parts:


 Stack: provides storage for local variables, alters
size as the program executes
 Data segment: global variables and strings stored
here. Fixed size.
 Code segment: functions main, printf, scanf
etc. stored here. Read only. Fixed size
 Heap: otherwise known as “dynamic memory”
the heap is available for us to use and may alter
size as the program executes
How Much Memory?

 With simple operating systems like MS-DOS there


may only be around 64k available (depending on
memory model and extended memory device
drivers)
 With complex operating systems using virtual
memory like Unix, NT, OS/2, etc. it can be much
larger, e.g. 2GB
 In the future (or now with NT on the DEC Alpha)
this will be a very large amount (17 thousand
million GB)
Dynamic Arrays

 Arrays in C have a fundamental problem - their


size must be fixed when the program is written
 There is no way to increase (or decrease) the size
of an array once the program is compiled
 Dynamic arrays are different, their size is fixed at
run time and may be changed as often as
required
 Only a pointer is required
Using Dynamic Arrays

 The following steps create a dynamic array:


 Declare a pointer corresponding to the desired
type of the array elements
 Initialise the pointer via calloc or malloc using
the total storage required for all the elements of
the array
 Check the pointer against NULL
 Increase or decrease the number of elements by
calling the realloc function
 Release the storage by calling free
calloc/malloc Example
#include
#include <stdio.h>
<stdio.h>
#include <stdlib.h>
#include <stdlib.h>
int
int main(void)
main(void)
{{
unsigned
unsigned i,i, s;
s;
double
double *p;
*p;
printf("How
printf("How many
many doubles?
doubles? ");
");
scanf("%u",
scanf("%u", &s);
&s);
if((p
if((p == calloc(s,
calloc(s, sizeof(double)))
sizeof(double))) == == NULL)
NULL) {{
fprintf(stderr,
fprintf(stderr, "Cannot
"Cannot allocate
allocate %u %u bytes
bytes ""
"for
"for %u%u doubles\n",
doubles\n", ss ** sizeof(double),
sizeof(double), s); s);
return
return 1;1;
}}
for(i
for(i == 0;
0; ii << s;
s; i++)
i++) here we access the “s”
p[i]
p[i] == i;i; doubles from 0..s-1
free(p);
free(p); all of the allocated
return
return 0;
0; memory is freed
}}
if((p
if((p == malloc(s
malloc(s ** sizeof(double)))
sizeof(double))) ==
== NULL)
NULL) {{
realloc Example
double
double *p;
*p;
double
double *p2;
*p2;
if((p
if((p == calloc(s,
calloc(s, sizeof(double)))
sizeof(double))) ==== NULL)
NULL) {{
fprintf(stderr,
fprintf(stderr, "Cannot
"Cannot allocate
allocate %u%u bytes
bytes ""
"for
"for %u
%u doubles\n",
doubles\n", ss ** sizeof(double),
sizeof(double), s);
s);
return 1;
return 1;
}}
printf("%u
printf("%u doubles
doubles currently,
currently, how
how many
many now?
now? ",
", s);
s);
scanf("%u",
scanf("%u", &s);
&s);
calculate new array
p2 = realloc(p, s * sizeof(double));
p2 = realloc(p, s * sizeof(double)); size and allocate
if(p2 storage
if(p2 ==== NULL)
NULL) {{
fprintf(stderr,
fprintf(stderr, "Could
"Could not
not increase/decrease
increase/decrease array
array ""
"to
"to contain
contain %u
%u doubles\n",
doubles\n", s);
s);
free(p);
free(p);
return
return 1;
1; pointer “p” is still
}} valid at this point
pp == p2;
p2;
free(p);
free(p); pointer “p” is invalid at this point, so
a new value is assigned to it
realloc can do it all

 The routines malloc and free are almost


redundant since realloc can do it all
 There is some merit in calloc since the memory
it allocates is cleared to zero

pp == malloc(s
malloc(s ** sizeof(double));
sizeof(double));
pp == realloc(NULL,
realloc(NULL, ss ** sizeof(double));
sizeof(double));

free(p);
free(p);
realloc(p,
realloc(p, 0);
0);
Allocating Arrays of Arrays

 Care must be taken over the type of the pointer


used when dealing with arrays of arrays
float
float *p;
*p;
pp == calloc(s,
calloc(s, sizeof(float));
sizeof(float));

float
float **rain;
**rain;
rain
rain == calloc(s,
calloc(s, 365
365 ** sizeof(float));
sizeof(float));

float
float (*rainfall)[365];
(*rainfall)[365];
rainfall
rainfall == calloc(s,
calloc(s, 365
365 ** sizeof(float));
sizeof(float));
rainfall[s-1][18]
rainfall[s-1][18] == 4.3F;
4.3F;
Dynamic Data Structures

 It is possible to allocate structures in dynamic


memory too
struct
struct Node
Node {{
int
int data;
data;
struct
struct Node
Node *next_in_line;
*next_in_line;
};
};
struct
struct Node*
Node* new_node(int
new_node(int value)
value)
{{
struct
struct Node*
Node* p;
p;
if((p
if((p == malloc(sizeof(struct
malloc(sizeof(struct Node)))
Node))) ==== NULL)
NULL) {{
fprintf(stderr,
fprintf(stderr, "ran
"ran out
out of
of dynamic
dynamic memory\n");
memory\n");
exit(9);
exit(9);
}}
p->data
p->data == value;
value; p->next_in_line
p->next_in_line == NULL;
NULL;
return
return p;
p;
}}
Linking the List
struct
struct Node
Node *first_node,
*first_node, *second_node,
*second_node, *third_node,
*third_node, *current;
*current;

first_node
first_node == new_node(-100);
new_node(-100);

second_node
second_node == new_node(0);
new_node(0);

first_node->next_in_line
first_node->next_in_line == second_node;
second_node;

third_node
third_node == new_node(10);
new_node(10);

second_node->next_in_line
second_node->next_in_line == third_node;
third_node;

current
current == first_node;
first_node;
while(current
while(current !=!= NULL)
NULL) {{
printf("%i\n",
printf("%i\n", current->data);
current->data);
current = current->next_in_line;
current = current->next_in_line;
}}
Summary

 The heap and stack grow towards one another


 Potentially a large amount of heap storage is
available given the right operating system
 The routines malloc, calloc, realloc and free
manipulate heap storage
 Only realloc is really necessary
 Allocating dynamic arrays
 Allocating dynamic arrays of arrays
 Allocating dynamic structures

You might also like