Professional Documents
Culture Documents
Nspace CTraining
Nspace CTraining
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);
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
/* 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.
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 ;
};
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
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