You are on page 1of 105

Pointers

Pointer Fundamentals
 When a variable is defined the compiler
(linker/loader actually) allocates a real 00000000
memory address for the variable.
00000000
 int x; will allocate 4 bytes in the x
main memory, which will be used to 00000000
store an integer value. 00000011
 When a value is assigned to a variable,
the value is actually placed to the
memory that was allocated.
 x=3; will store integer 3 in the 4
bytes of memory.
Pointers
 When the value of a variable is used, 00000000
the contents in the memory are used. 00000000
 y=x; will read the contents in x
00000000
the 4 bytes of memory, and then
assign it to variable y. 00000011
 &x can get the address of x.
(referencing operator &)
 The address can be passed to a
function:
 scanf("%d", &x);

 The address can also be stored in a y


variable ……
Pointers
 To declare a pointer variable
type * pointername;
 For example:
 int * p1; p1 is a variable that tends to point to
an integer, (or p1 is a int pointer)
 char *p2;
 unsigned int * p3;
 p1 = &x; /* Store the address in p1 */
 scanf("%d", p1); /* i.e. scanf("%d",&x); */
 p2 = &x; /* Will get warning message */
Initializing Pointers
 Like other variables, always initialize pointers before
using them!!!
 For example:
int main(){
int x;
int *p;
scanf("%d",p); /* Don’t */
p = &x;
scanf("%d",p); /* Correct */
}
Using Pointers
 You can use pointers to access the values of other
variables, i.e. the contents of the memory for other
variables.
 To do this, use the * operator (dereferencing operator).
 Depending on different context, * has different meanings.
 For example:
int m=3, *p;
p=&m;
n=*p;
printf("%d\n", n);
printf("%d\n",*p);
An Example
int m=3, n=100, *p;
p=&m;
printf("m is %d\n",*p);
m++;
printf("now m is %d\n",*p);
p=&n;
printf("n is %d\n",*p);
*p=500; /* *p is at the left of "=" */
printf("now n is %d\n", n);
Pointers as Function
Parameters
 Sometimes, you want a function to assign a value to a
variable.
 e.g. scanf()

 E.g. you want a function that computes the minimum AND


maximum numbers in 2 integers.
 Method 1, use two global variables.
 In the function, assign the minimum and maximum
numbers to the two global variables.
 When the function returns, the calling function can read
the minimum and maximum numbers from the two
global variables.
 This is bad because the function is not reusable.
Pointers as Function
Parameters
 Instead, we use the following
int main()
function {
void min_max(int a, int b,
int *min, int *max){
int x,y;
if(a>b){ int small,big;
*max=a;
*min=b;
printf("Two integers: ");
} scanf("%d %d", &x, &y);
else{
*max=b;
min_max(x,y,&small,&big);
*min=a; printf("%d <= %d", small,
} big);
} return 0;
}
Using Pointers to Access Array
Elements (1)
 Pointer can be used to access array elements

int a[ 10 ], *p; int a[ 10 ], *p;


p = &a[2];
*p = 10; a[2] = 10;
*(p+1) = 10; a[3] = 10;
printf("%d", *(p+3)); printf("%d", a[5]);

p p+1 p+2 p+3 p+4 p+5 p+6 p+7

a a[0] a[1] a[2] a[3] a[4] a[5] a[6] a[7] a[8] a[9]
Using Pointers to Access Array
Elements (2)
 A pointer can be used just like an array
int a[ 10 ], *p; int a[ 10 ], *p;
p = &a[2];
p[0] = 10; a[2] = 10;
p[1] = 10; a[3] = 10;
printf(“%d”, p[3]); printf(“%d”, a[5]);

p
p[0] p[1] p[2] p[3] p[4] p[5] p[6] p[7]
a a[0] a[1] a[2] a[3] a[4] a[5] a[6] a[7] a[8] a[9]
An Example
/* Sum – sum up the ints /* In another function */
in the given array */ int a[1000],x;
int sum(int *ary, int size) ……
{ x= sum(&a[100],50);
int i, s; /* This sums up
for(i = 0, s=0; i<size;i++) a[100], a[101], …,
{ a[149] */
s+=ary[i];
}
return s;
}
Pointer Arithmetic (1)
 When a pointer variable points to an array element, there is
a notion of adding or substracting an integer to/from the
pointer.
int a[10], *p, *q;
p = &a[2];
q = p + 3; /* q points to a[5] now */
p = q – 1; /* p points to a[4] now */
p++; /* p points to a[5] now */
p--; /* p points to a[4] now */
*p = 123; /* a[4] = 123 */
*q = *p; /* a[5] = a[4] */
q = p; /* q points to a[4] now */
scanf("%d", q) /* scanf("%d", &a[4]) */
Pointer Arithmetic (2)
 If two pointers point to elements of a same array, then there
are notions of subtraction and comparisons between the two
pointers.
int a[10], *p, *q , i;
p = &a[2];
q = &a[5];
i = q - p; /* i is 3*/
i = p - q; /* i is -3 */
a[2] = a[5] = 0;
i = *p - *q; /* i = a[2] – a[5] */
p < q; /* true */
p == q; /* false */
p != q; /* true */
Allocating Memory for a
Pointer (1)
 The following  This one is correct:
program is wrong! #include <stdio.h>
#include <stdio.h> int main()
int main() {
int *p;
{
int a;
int *p; p = &a;
scanf("%d",p); scanf("%d",p);
return 0; return 0;
} }
Allocating Memory for a
Pointer (2)
 There is another way to allocate memory so the pointer can
point to something:
#include <stdio.h>
#include <stdlib.h>
int main(){
int *p;
p = (int *) malloc( sizeof(int) ); /* Allocate 4 bytes */
scanf("%d", p);
printf("%d", *p);
free(p); /* This returns the memory to the system.*/
/* Important !!! */
}
Allocating Memory for a
Pointer (3)
 Prototypes of malloc() and free() are defined in stdlib.h
void * malloc(size_t number_of_bytes);
void free(void * p);
 You can use malloc and free to dynamically allocate and
release the memory;
int *p;
p = (int *) malloc(1000 * sizeof(int) );
for(i=0; i<1000; i++)
p[i] = i;
p[1000]=3; /* Wrong! */
free(p);
p[0]=5; /* Wrong! */
Returning a Pointer From a
Function int main(int argc, char *
char * dup1(const char * str){
argv[]){
char * p;
char *p;
int n,k;
if(argc != 2){
for(n=0;str[n]!='\0';n++)
printf("Usage: %s
;
str\n", argv[0]);
p = (char*) malloc(2*n+1);
return 0;
for(k=0;k<n;k++){
}
p[2*k] = str[k];
p[2*k+1] = str[k];
p = dup1(argv[1]);
} puts(p);
return p; free(p);
} }
A Wrong Version
char * dup2(const char * str){  Anything that comes
char p[1000]; automatically also goes
int n,k; automatically.
for(n=0;str[n]!=‘\0’;n++)  A calling function cannot use
; the returned value to access
the 1000-char array
for(k=0;k<n;k++){ anymore.
p[2*k] = str[k];  In this case, the array p
p[2*k+1] = str[k]; disappears off of the stack
as soon as dup2() finishes,
}
and is gone forever!
return p; /* WRONG!!! */
}
A Different Wrong Version
char * dup3(const char * str, int main(int argc, char *
char *dest)
{
argv[]){
int n,k; char *p, buf[1000];
for(n=0;str[n]!='\0';n++) if(argc != 2){
;
printf(“Usage: %s str\n”,
for(k=0;k<n;k++){
dest[2*k] = str[k];
argv[0]);
dest[2*k+1] = str[k]; return 0;
} }
return dest;
}
p = dup3(argv[1], buf);
puts(p);
free(p); /* WRONG!!! */
}
Error Handling Functions (1)
 C has very few debugging facilities.
#include <assert.h>
void assert (int expression) ;
 Expression is anything you want to test.
 If true, assert does nothing.
 If false, assert displays an error message on stderr and
terminates immediately with a core file.
 Example:
assert (interest_rate >=0) ;
 If interest_rate<0 then a relatively informative error

message is printed and the program terminates


abnormally.
Error Handling Functions (2)
 Many standard C functions set an internal variable called errno
when they fail to reflect the cause of the failure.
#include <errno.h>
int errno;
void perror(const char *s);
 For example:
if ((fp = fopen("testfile", "r")) == NULL) {
status = errno; /* Must save before it is reset!!! */
perror("fopen() of testfile failed");
fprintf(stderr, "errno was %d!\n", status);
}
Recursion
 We use the following question as an example
to demonstrate how to use recursion to solve
a problem.
 Fibonacci Numbers
1, 1, 2, 3, 5, 8, 13, ... (
add the last two to get the next)
 An interesting fact about Fibonacci numbers
is that the division between two consecutive
numbers approach the Golden Number
(sqrt(5)-1)/2 ~ 0.6180339890
The Original Example of Fibonacii
Numbers
Recursion for Fibonacci
Numbers (1)
 Fib(1)=1;
 Fib(2)=1;
 Fib(n)=Fib(n-1)+Fib(n-2);
/* fib.c */
int Fib(int n){
if(n<=2) return 1;
else return Fib(n-1)+Fib(n-2);
}

/* fib.h */
int Fib(int n);
Recursion for Fibonacci
Numbers (2)
/* main.c */
#include <stdio.h>
#include "fib.h"
int main(int argc, char * argv[]){
if(argc!=2){
printf("Usage: %s nnn\n",
argv[0]);
return 0;
}
int n = atoi(argv[1]);
printf("Fib(%d)=%d\n", n,
Fib(n));
return 0;
}
More Efficient Programming
n
 The previous program is
simple, but it is very
inefficient!
 Programming is not only n-1 n-2
coding, but also about
the design of data
structures and
algorithms. n-2 n-3 n-3 n-4
 The time needed by the
previous Fib(n) function
call is Fib(n) time units.Time(1)=Time(2)=constant;
Time(n)=Time(n-1)+Time(n-2);
Another Method to Compute
Fib  This only needs to
Fib(int n){ loop the for loop n
int * fib = (int *) malloc(n *
sizeof (int) ); times.
int i;  This type of
fib[0]=fib[1]=1;
for(i=2;i<n;i++){ method/algorithm is
fib[i]=fib[i-1] + fib[i-2]; called Dynamic
}
i = fib[n-1]; Programming.
free(fib);
return i;
}
Pointers to Pointers
 A pointer variable is a variable that takes some memory
address as its value. Therefore, you can have another pointe
pointing to it.
int x;
int * px;
int ** ppx;
ppx = &px;
px = &x; /* i.e. *ppx = &x */
**ppx = 10; /* i.e. *px =10; i.e. x=10; */
ppx = (int **) malloc(sizeof(int *));
**ppx = 20; /* Wrong, since *ppx is uninitialized! */
Arrays of Pointers (1)
 If we have an array of structures, each structure can
potentially be very big.
 To sort such an array, a lot of memory copying and
movements are necessary, which can be expensive.
 For efficiency, we can use array of pointers instead:
struct book{
float price;
char abstract[5000];
};
struct book book_ary[1000];
struct book * pbook_ary[1000];
……
for(i=0;i<1000;i++)
pbook_ary[i] = &book_ary[i];
Arrays of Pointers (2)
void my_sort(struct book * pbook_ary[ ], int size)
{
int i, j;
struct book *p;
for(i=1;i<size;i++){
p=pbook_ary[i];
for(j=i-1;j>=0;j--)
if(pbook_ary[ j ] -> price > p -> price)
pbook_ary[ j+1 ]= pbook_ary[ j ];
else
break;
pbook_ary[ j+1 ] = p;
}
}
Arrays of Pointers (3)
struct book ** search_range(struct book * pbook_ary[ ], int size,
float low, float high, int *num)
{
int i, j;
for(i=0;i<size;i++)
if(pbook_ary[i] -> price >= low) break;
for( j=size; j>0;j--)
if(pbook_ary[ j] -> price <= high) break;
/* i , i+1, …, j are the elements in the range */
*num = j – i + 1;
return &pbook_ary[ i ];
}
Dynamic Two Dimensional
Arrays
int ** ary;
int m, n;
srand( time(NULL) );
m = rand( ) % 5000 +10;
n = rand( ) % 5000 +10;
ary = (int **) malloc( m * sizeof(int *) );
for( j =0; j< m; j++){
ary[ j ]= (int *) malloc (n *sizeof(int));
}
ary[3][4] = 6;
*( *( ary + 3) + 4) = 6;
ary->[3]->[4] = 6; /* NO! You can not do this */
const Pointers (1)
 The const keyword has a different meaning when
applied to pointers.
void test( const int k, const int * m)
{
k ++; /* 1 */
(*m) ++; /* 2 */
m ++; /* 3 */
printf("%d,%d", k, *m);
}
 The compiler will warn you about the 1 st and 2nd
increments, but not the 3 rd .
const Pointers (2)
 Thereason we use const before
parameters is to indicate that we will not
modify the value of the corresponding
parameter inside the function.

 Forexample: we would not worry about


the format_str is going to be modified by
printf when we look at its prototype:
 int printf(const char * format_str, ……);
Pointers to Functions (1)
 Since a pointer merely contains an
address, it can point to anything.
 A function also has an address -- it must
be loaded in to memory somewhere to
be executed.
 So, we can also point a pointer to a
function.
int (*compare)(int, int);
1. Compare is a pointer
2. To a function
3. That returns an int value
Pointers to Functions (2)
typedef struct{
float price;
char title[100];
} book;
int (*ptr_comp)(const book *, const book *);
/* compare with
int * ptr_comp(const book *, const book *);
*/
 Do not forget to initialize the pointer -- point the
pointer to a real function!
Pointers to Functions
int main( ){
(3)
book a, b;
a.price=19.99;
#include <string.h> strcpy(a.title, "unix");
int compare_price(const book * b.price=20.00;
p, const book *q)
strcpy(b.title, "c");
{
ptr_comp = compare_price;
return p->price-q->price; printf("%d", ptr_comp(&a, &b));
} ptr_comp = compare_title;
int compare_title(const book * p, printf("%d", ptr_comp(&a, &b));
const book *q)
return 0;
{
}
return strcmp(p->title,q->
title);
}
Deallocating Dynamic
Structures
 For every call to malloc used to build a dynamically
allocated structure, there should be a corresponding
call to free.
 A table inside malloc and free keeps track of the
starting addresses of all allocated blocks from the
heap, along with their sizes.
 When an address is passed to free, it is looked up
in the table, and the correct amount of space is
deallocated.
 You cannot deallocate just part of a string or any
other allocated block!
Example: Code for Freeing a
List of Items
In ItemTypes.h: In ItemImplementation.c:
typedef struct listItem { #include <string.h>
int count;
#include "ItemInterface.h"
char *str;
...
} Item;
In ItemInterface.h:
Item *createItem(char *s) {
#include "ItemTypes.h" Item *it = (Item *)malloc(
Item *createItem( char *s ); sizeof(item) );
void freeItem( Item *it ); it->count = 1;
void incCounter( Item *it ); it->str = strdup( s );
char *getItemStr( Item *it ); return it;
... }
Example: Code for Freeing a
List of Items
In ItemImplementation.c: In NodeTypes.h:
void freeItem( Item *it ) { #include "ItemTypes.h"
free( it->str ); typedef struct node {
free( it ); Item *value;
} struct node *next;
void incCounter( Item *it ) { } Node;
(it->count)++; In NodeInterface.h:
} …
char *getItemStr( Item *it ) { void freeNode( Node *n );
return it->str; Item *getValue( Node *n );
} Node *getPtr( Node *n );
... ...
Example: Code for Freeing a
List of Items
In NodeImplementation.c: In ListTypes.h:
void freeNode( Node *n ) { #include "NodeTypes.h"
freeItem( n->value ); typedef struct list {
free( n ); int size;
}
Node *head;
Item *getValue( Node *n ) {
Node *tail;
return n->value;
} List;
}
Node *getPtr( Node *n ) { In ListInterface.h:
return n->next; …
} void freeList( List *n );
... ...
Example: Code for Freeing a
List of Items
In ListImplementation.c: In
ListImplementation.c
void freeChain( Node *n ) { :
/* a private function --
not in List interface */
void freeList( List *ll ) {
if ( n != NULL ) {
freeChain(getPtr( n )); freeChain( ll-
freeNode( n ); >head );
} free( ll );
} }
Example: Code for Freeing a
List of Items
 In the previous example, it was assumed that
everything in the list was allocated
dynamically, including the list header.
 If a list had been declared using LinkedList x;
the function freeList could not be used to
deallocate the dynamically allocated space
contained inside the list.
 The list interface could provide a resetList
function to clean up such a structure.
Example: Code for Freeing a
List of Items
void resetList( List *ll )
{
/* Free up the list that ll currently points at, and
reset
ll to be the empty List.
*/
freeChain( ll->head );
ll->head = ll->tail = NULL;
ll->size = 0;
}
Structures
Structures (1)
 Structures are C’s way of grouping collections of data
into a single manageable unit.
 This is also the fundamental element of C upon which most
of C++ is built (i.e., classes).
 Similar to Java's classes.
 An example:
 Defining a structure type:
struct coord {
int x ;
int y ;
};
 This defines a new type struct coord. No variable is actually
declared or generated.
Structures (2)
 Define struct variables:
struct coord {
int x,y ;
} first, second;
 Another Approach:
struct coord {
int x,y ;
};
...............
struct coord first, second; /* declare variables */
struct coord third;
Structures (3)
 You can even use a typedef if your
don't like having to use the word “struct”
typedef struct coord coordinate;
coordinate first, second;
 Insome compilers, and all C++
compilers, you can usually simply say
just:
coord first, second;
Structures

(4)
Access structure variables by the dot (.) operator
 Generic form:
structure_var.member_name
 For example:
first.x = 50 ;
second.y = 100;
 These member names are like the public data members of a
class in Java (or C++).
 No equivalent to function members/methods.
 struct_var.member_name can be used anywhere a variable
can be used:
 printf ("%d , %d", second.x , second.y );
 scanf("%d, %d", &first.x, &first.y);
Structures (5)
 You can assign structures as a unit with =
first = second;
instead of writing:
first.x = second.x ;
first.y = second.y ;
 Although the saving here is not great
 It will reduce the likelihood of errors and
 Is more convenient with large structures
 This is different from Java where variables are simply
references to objects.
first = second;
makes first and second refer to the same object.
Structures Containing Structures
 Any “type” of thing can be a member of a structure.
 We can use the coord struct to define a rectangle
struct rectangle {
struct coord topleft;
struct coord bottomrt;
};
 This describes a rectangle by using the two points
necessary:
struct rectangle mybox ;
 Initializing the points:
mybox.topleft.x = 0 ;
mybox.topleft.y = 10 ;
mybox.bottomrt.x = 100 ;
mybox.bottomrt.y = 200 ;
An Example int main () {
int length, width;
long area;
#include <stdio.h> struct rectangle mybox;
struct coord { mybox.topleft.x = 0;
mybox.topleft.y = 0;
int x;
mybox.bottomrt.x = 100;
int y; mybox.bottomrt.y = 50;
}; width = mybox.bottomrt.x –
mybox.topleft.x;
struct rectangle { length = mybox.bottomrt.y –
struct coord mybox.topleft.y;
topleft; area = width * length;
printf ("The area is %ld units.\n",
struct coord area);
bottomrt; }
};
Structures Containing Arrays
 Arrays within structures are the same
as any other member element.
 For example:
struct record {
float x;
char y [5] ;
};
 Logical organization:

float char[5]
#include <stdio.h>
struct data {
float amount;
char fname[30];
char lname[30];
} rec;
int main () {
struct data rec;
printf ("Enter the donor's first and last names, \n");
printf ("separated by a space: ");
scanf ("%s %s", rec.fname, rec.lname);
printf ("\nEnter the donation amount: ");
scanf ("%f", &rec.amount);
printf ("\nDonor %s %s gave $%.2f.\n",
rec.fname,rec.lname,rec.amount);
}
Arrays of Structures
 The converse of a structure with arrays:
 Example:
struct entry {
char fname [10] ;
char lname [12] ;
char phone [8] ;
};
struct entry list [1000];
 This creates a list of 1000 identical entry(s).
 Assignments:
list [1] = list [6];
strcpy (list[1].phone, list[6].phone);
list[6].phone[1] = list[3].phone[4] ;
An Example
int main() {
struct entry list[4];
int i;
for (i=0; i < 4; i++) {
#include <stdio.h> printf ("\nEnter first name: ");
struct entry { scanf ("%s", list[i].fname);
printf ("Enter last name: ");
char fname [20]; scanf ("%s", list[i].lname);
char lname [20]; printf ("Enter phone in 123-4567 format: ");
scanf ("%s", list[i].phone);
char phone [10]; }
printf ("\n\n");
}; for (i=0; i < 4; i++) {
printf ("Name: %s %s", list[i].fname, list[i].lna
printf ("\t\tPhone: %s\n", list[i].phone);
}
}
Initializing Structures
 Simple example:
struct sale {
char customer [20] ;
char item [20] ;
int amount ;
};

struct sale mysale = { "Acme Industries",


"Zorgle blaster",
1000 } ;
Initializing Structures
 Structures within structures:
struct customer {
char firm [20] ;
char contact [25] ;
};
struct sale {
struct customer buyer ;
char item [20] ;
int amount ;
} mysale =
{ { "Acme Industries", "George Adams"} ,
"Zorgle Blaster", 1000
};
Initializing Structures
 Arrays of structures struct sale y1990 [100] = {
struct customer { { { "Acme Industries",
"George Adams"} ,
char firm [20] ; "Left-handed Idiots" ,
char contact [25] ; 1000
}; },
{ { "Wilson & Co.",
struct sale {
"Ed Wilson"} ,
struct customer buyer "Thingamabob" , 290
; }
char item [20] ; };
int amount ;
};
Pointers to Structures
struct part {
float price ;
char name [10] ;
};
struct part *p , thing;
p = &thing;
/* The following three statements are equivalent */
thing.price = 50;
(*p).price = 50; /* () around *p is needed */
p -> price = 50;
Pointers to Structures
thing.price thing.name [ ]

p is set to point to the first byte of the


struct variable
Pointers to Structures
struct part * p, *q;
p = (struct part *) malloc( sizeof(struct part) );
q = (struct part *) malloc( sizeof(struct part) );
p -> price = 199.99 ;
strcpy( p -> name, "hard disk" );
(*q) = (*p);
q = p;
free(p);
free(q); /* This statement causes a problem !!!
Why? */
Pointers to Structures
 You can allocate a structure array as well:
{
struct part *ptr;
ptr = (struct part *) malloc(10 * sizeof(struct part) );
for( i=0; i< 10; i++)
{
ptr[ i ].price = 10.0 * i;
sprintf( ptr[ i ].name, "part %d", i );
}
……
free(ptr);
}
Pointers to Structures
 You can use pointer arithmetic to access the elements of
the array:
{
struct part *ptr, *p;
ptr = (struct part *) malloc(10 * sizeof(struct part) );
for( i=0, p=ptr; i< 10; i++, p++)
{
p -> price = 10.0 * i;
sprintf( p -> name, "part %d", i );
}
……
free(ptr);
}
Pointer as Structure Member
a.data = 1;
struct node{
int data; a.next->data = 2;
struct node *next; /* b.data =2 */
};
struct node a,b,c;
a.next->next->data = 3;
a.next = &b; /* c.data = 3 */
b.next = &c; c.next = (struct node *)
c.next = NULL;
malloc(sizeof(struct
node));
……
NULL
a b c
Assignment Operator vs.
memcpy  Equivalently, you can use
 This assign a memcpy
struct to another
#include <string.h>
{ ……
struct part a,b; {
b.price = 39.99; struct part a,b;
b.name = "floppy"; b.price = 39.99;
a = b; b.name = "floppy";
} memcpy(&a,&b,sizeof(part));
}
Array Member vs. Pointer
Member int main()
{
struct book {
struct book a,b;
float price; b.price = 19.99;
char strcpy(b.name, "C handbook");
name[50]; a = b;
strcpy(b.name, "Unix
}; handbook");
}
Array Member vs. Pointer
Memberint main()
{
struct struct book a,b;
book { b.price = 19.99;
float b.name = (char *) malloc(50);
price; strcpy(b.name, "C handbook");
a = b;
char strcpy(b.name, "Unix handbook");
*name;
}; free(b.name);
}
Passing Structures to
Functions
 Structures are passed by value to functions
 The parameter variable is a local variable, which will
be assigned by the value of the argument passed.
 Unlike Java.
 This means that the structure is copied if it is
passed as a parameter.
 This can be inefficient if the structure is big.
 In this case it may be more efficient to pass a pointer to the
struct.
 A struct can also be returned from a function.
Pre-Processing Directives
 Transform your program before actual
compilation
 Preprocessing directives are lines in your
program that start with `#'
 `#' is followed by an identifier that is the
directive name
 Eg `#define' is the directive that defines a
macro
Pre-Processing Directives
Cntd
The C preprocessor provides four separate
facilities

 Inclusion of header files


 Macro expansion
 Conditional compilation
 Line control.
Pre-Processing Directives
Cntd
 You request the use of a header file in your
program with the C preprocessing directive
`#include'.
 To make sure the file is included only once
#ifndef FILE_FOO_SEEN
#define FILE_FOO_SEEN
 #import includes a file, but does so at most
once
 But obsolete, because of bad design
Pre-Processing Directives
Cntd
 Macros with arguments
 #define min(X, Y) ((X) < (Y) ? (X) : (Y))
 System has predefined macros

 Standard Predefined macros


Pre-Processing Directives
Cntd
 Stringification means turning a code fragment
into a string constant
 Macro contents are the text for the code
fragment
 eg
#define WARN_IF(EXP) \
do { if (EXP) \
fprintf (stderr, "Warning: " #EXP "\n"); } \
while (0)
Pre-Processing Directives
Cntd
 To undefine a macro means to cancel its
definition
#define FOO 4
x = FOO;
#undef FOO
x = FOO;

expands into
x = 4;
x = FOO;
Pre-Processing Directives
Cntd
#if expression
controlled text
#endif

#if expression
text-if-true
#else /* Not expression */ t
ext-if-false
#endif
Multiple Definitions with
#include(1)
 If the same .h file is included in several
places in a program, it will cause multiple
definition errors at compile time.
 To circumvent this problem, use #ifndef (if
not defined), #define and #endif macros in
the .h file:

#ifndef PQ_Item_Types
#define PQ_Item_Types
… <type definitions belong here>...
#endif
Multiple Definitions with
#include(2)
 The compiler keeps track of all identifier
names defined in the program so far.
 The first time the compiler scans this
file, it recognizes that PQ_Item_Types
has not been defined, so it will scan all
code between #ifndef and the matching
#endif.
 This causes PQ_Item_Types and any
other identifier found in the block to
become defined.
Multiple Definitions with
#include(3)
 The next time this file is scanned, the
compiler recognizes that PQ_Item_Types
has been defined, and it ignores all code
between #ifndef and the matching #endif.
 Note: use a different, unique identifier with
#ifndef in each .h file. If the identifier has
already been defined elsewhere, code that
should be scanned by the compiler will be
ignored.
Linked List -- list.h, listapi.h
 list.h
struct nodestr {
int data;
struct nodestr *next;
};
typedef nodestr node;

 listapi.h
#include "list.h"
node * search(node * head, int d);
node * add(node * head, int d);
void free(node * head);
Linked List -- listapi.c
#include <stdio.h>
#include "listapi.h"
/* Search for a node by its key d */
node * search(node * head, int d)
{
for(; head != NULL; head = head->next)
if ( head -> data == d) return head;
return NULL;
}
Linked List -- listapi.c
/* insert a node into list */node * insert(node * head, int d) {
node * loc;
loc=search( *head, d );
if (loc != NULL) return head; /* No need to change */
else {
node * newhead;
newhead = malloc( sizeof(node) );
newhead -> data = d;
newhead -> next= head;
return newhead;
}
}
Linked List -- listapi.c
void free_list(node *head)
{
node *p = head;
while (p != NULL) {
head = head ->next;
free(p);
p = head;
}
Linked List -- main.c
#include <stdio.h>
#include "listapi.h"
int main() {
int i;
node * loc, *list = NULL;
for (i=0; i<15; i++)
list = add(list, i);
loc = search(list, 10);
if( loc != NULL) printf("10 is found.\n");
free_list(list);
}
Files
FILE *
 In C, we use a FILE * data type to access files.
 FILE * is defined in stdio.h .
 An example:
#include <stdio.h>
int main()
{
FILE *fp;
fp = fopen("tmp.txt", "w");
fprintf(fp,"This is a test\n");
fclose(fp);
return 0;
}
Opening a File (1)
 You must include <stdio.h>
 Prototype Form:
FILE * fopen (const char * filename, const char * mode)
 FILE is a structure type declared in stdio.h.
 You don't need to worry about the details of the structure.
 In fact it may vary from system to system.
 fopen returns a pointer to the FILE structure type.
 You must declare a pointer of type FILE to receive that value when
it is returned.
 Use the returned pointer in all subsequent references to that file.
 If fopen fails, NULL is returned.
 The argument filename is the name of the file to be opened.
Opening
Values of mode
a File (2)
 Enclose in double quotes or pass as a string variable
 Modes:
r: open the file for reading (NULL if it doesn’t exist)
w: create for writing. destroy old if file exists
a: open for writing. create if not there. start at the end-of-
file
r+: open for update (r/w). create if not there. start at the
beginning.
w+: create for r/w. destroy old if there
a+: open for r/w. create if not there. start at the end-of-file
stdin, stdout, and stderr
 Every C program has three files opened for them at
start-up: stdin, stdout, and stderr
 stdin is opened for reading, while stdout and stderr
are opened for writing
 Examples:
 fprintf(stdout, "Hello there!\n");
 This is the same as printf("Hello there!\n");
 fscanf(stdin, "%d", &int_var);
 This is the same as scanf("%d", &int_var);
 fprintf(stderr, "An error has occurred!\n");
 This is useful to report errors to standard error - it flushes output as
well, so this is really good for debugging!
The exit () Function
 This is used to leave the program at anytime from
anywhere before the “normal” exit location.
 Syntax:

exit (status);
 Example:
#include <stdlib.h>
……
if( (fp=fopen("a.txt","r")) == NULL){
fprintf(stderr, "Cannot open file a.txt!\n");
exit(1);
}
Four Ways to Read and Write
Files

 Formatted file I/O

 Get and put a character

 Get and put a line

 Block read and write


Formatted File I/O
 Formatted File input is done
through fscanf:
 intfscanf (FILE * fp, const char *
fmt, ...) ;
 Formatted File output is done
through fprintf:
 int
fprintf(FILE *fp, const char *fmt,
…);
Formatted File I/O Contd.
{
FILE *fp1, *fp2;
int n;
fp1 = fopen("file1", "r");
fp2 = fopen("file2", "w");
fscanf(fp1, "%d", &n);
fprintf(fp2, "%d", n);
fclose(fp1);
fclose(fp2);
}
Get and Put a Character
#include <stdio.h>
int fgetc(FILE * fp);
int fputc(int c, FILE * fp);
 These two functions read or write a single
byte from or to a file.
 fgetc returns the character that was read,
converted to an integer.
 fputc returns the same value of parameter c
if it succeeds, otherwise, return EOF.
Get and Put a Line
#include <stdio.h>
char *fgets(char *s, int n, FILE * fp);
int fputs(char *s, FILE * fp);
 These two functions read or write a string from or to a file.
 fgets reads an entire line into s, up to n-1 characters in
length (pass the size of the character array s in as n to be
safe!)
 fgets returns the pointer s on success, or NULL if an error
or end-of-file is reached.
 fputs returns the number of characters written if
successful; otherwise, return EOF.
fwrite and fread (1)
 fread and fwrite are binary file reading and writing functions
 Prototypes are found in stdio.h
 Generic Form:
int fwrite (void *buf, int size, int count, FILE *fp) ;
int fread (void *buf, int size, int count, FILE *fp) ;
 buf: is a pointer to the region in memory to be written/read

 It can be a pointer to anything (more on this later)


 size: the size in bytes of each individual data item
 count: the number of data items to be written/read
 For example a 100 element array of integers
 fwrite( buf, sizeof(int), 100, fp);
 The fwrite (fread) returns the number of items actually
written (read).
fwrite and fread (2)
 Testing for errors:
if ((frwrite(buf,size,count,fp)) != count)
fprintf(stderr, "Error writing to file.");
 Writing a single double variable x to a file:
fwrite (&x, sizeof(double), 1, fp) ;
 This writes the double x to the file in raw binary format
 i.e., it simply writes the internal machine format of x
 Writing an array text[50] of 50 characters can be done
by:
 fwrite (text, sizeof(char), 50, fp) ;
 or
 fwrite (text, sizeof(text), 1, fp); /* text must be a local array
name */
 fread and frwrite are more efficient than fscanf and fprintf
Closing and Flushing Files
 Syntax:
int fclose (FILE * fp) ;
 closes fp -- returns 0 if it works -1 if it fails
 You can clear a buffer without closing it
int fflush (FILE * fp) ;
 Essentially this is a force to disk.
 Very useful when debugging.
 Without fclose or fflush, your updates to a file
may not be written to the file on disk. (Operating
systems like Unix usually use memory caches
when accessing files.)
Random Access
 One additional operation gives slightly better control:
int fseek (FILE * fp, long offset, int origin) ;
 offset is the number of bytes to move the position
indicator
 origin is the moves relative starting position
 Three options/constants are defined for origin
 SEEK_SET
 move the indicator offset bytes from the beginning
 SEEK_CUR
 move the indicator offset bytes from its current position
 SEEK_END
 move the indicator offset bytes from the end
Detecting End of File
 Text mode files:
while ( (c = fgetc (fp) ) != EOF )
 Reads characters until it encounters the EOF
 The problem is that the byte of data read may actually be
indistinguishable from EOF.
 Binary mode files:
int feof (FILE * fp) ;
 Note: the feof function realizes the end of file only after a reading
failed (fread, fscanf, fgetc … )
fseek(fp,0,SEEK_END);
printf("%d\n", feof(fp)); /* zero value */
fgetc(fp); /* fgetc returns -1 */
printf("%d\n",feof(fp)); /* nonzero value */
An Example
#define BUFSIZE 100
int main () {
char buf[BUFSIZE];
if ( (fp=fopen("file1", "r"))==NULL) {
fprintf (stderr,"Error opening file.");
exit (1);
}
while (!feof(fp)) {
fgets (buf,BUFSIZE,fp);
printf ("%s",buf);
}
fclose (fp);
return 0;
}
File Management Functions
 Erasing a file:
int remove (const char * filename);
 This is a character string naming the file.
 Returns 0 if deleted; otherwise -1.

 Renaming a file:
int rename (const char * oldname, const char *
newname);
 Returns 0 if successful or -1 if an error occurs.
 error: file oldname does not exist

 error: file newname already exists

 error: try to rename to another disk


Multiple Source Files (1)
 Obviously, large programs are not going to be contained
within single files.
 C provides several techniques to ensure that these multiple
files are managed properly.
 These are not enforced rules but every good C
programmer know how to do this.
 A large program is divided into several modules, perhaps
using abstract data types.
 The header (.h) file contains function prototypes of a module.
 The (.c) file contains the function definitions of a module.
 Each module is compiled separately and they are linked to
generate the executable file.
Multiple Source Files (2)
 C programs are generally broken up into two
types of files.
.c files:
 contain source code (function definitions) and global
variable declarations
 these are compiled once and never included
.h files:
 these are the “interface” files which “describe” the .c files
 type and struct declarations
 const and #define constant declarations
 #includes of other header files that must be included
 prototypes for functions

You might also like