Pointers

Oracle Certification Program Candidate Guide

Consider a variable v of particular data type. During the execution, v is allocated memory cells to store the data values. The number of memory locations required to store a variable will depend on the type of the variable. Thus a single character may be stored in one(1) byte, whereas a floating point number may take 4 contiguous bytes. The address of v's memory location is denoted by an expression &v. This is sometimes called left value of v (l_value v). The actual value of v is called the right value of v (r_value v).

A variable p is called a pointer to v, if it points to the location where v is stored.

The syntax for declaring a pointer variable is,

*;

Example 9.1 :

int v;

int *p; /* pointer to an integer*/

The following statement, assigns the address location of the variable v to p, and p is a pointer to v.

p=&v;

Since a pointer variable points to a location, the content of that location is obtained by prefixing the pointer variable by the unary operator * (also called the indirection or dereferencing operator) like, *.

Example 9.2 :

int u,v=9;

int *pi;

pi=&v; /* pi points to the location of v */

u=*pi; /*assigns the value 9 to u, the content of the location pointed to by pi i.e. the r_value of v */

A pointer may be assigned (or initialised) a ‘null’ value, when it does not point to any other data item.

The unary operators & and * have same precedence as the other unary operators -, --, +, ++, !, sizeof and .

REMARK :

The address operator & can be applied to an operand which has unique address e.g. to variables or single array elements. It cannot be applied to arithmetic expressions.

The indirection operator * can be applied to operands that are pointers e.g. pointer variables. However, if pi points to v, that is, pi=&v, then in an expression *pi and v can be used interchangeably.

Example 9.3 :

/* to show indirection operator usage */

int v=3;

int *pi;

pi=&v;

*pv=0; /* resets v indirectly */

9.1 OPERATIONS ON POINTERS

A pointer can be made to point different locations in the memory by performing operations such as adding or subtracting integer quantity to or from a pointer variable. The pointer shifts (due to such operation) to a new location. The new location is determined by adding or subtracting (depending on the operator used in the operation) the product of the offset (integer quantity indicating the extent of shift) and the scaling factor associated with the datatype of that pointer. Consider the following example :

Example 9.4 :

#include

main()

{

static int j[5]={1,3,5,7,9};

int *k; /* defines a pointer to an integer*/

k=j; /* now k points to same location address stored in j i.e the address of the first element in the array (&j[0] which holds the value 1)*/

k=k + 3;

:

}

The statement k=k+3; will increment the pointer k so that the pointer k would point to a new location which can be computed using the following calculation.

new position of k =(old position of k) + (offset * scaling factor)

Since k is a pointer to an integer (which typically occupies 4 bytes in memory), the scaling factor becomes 4. The offset or the integer quantity is added to k in the operation is 3. So by substituting the values in places we finally get,

k=k + (3 * 4);

means k is incremented by 12 bytes or pointing to the new location address &j[3] which stores the value 7.

The following operations on pointers are not allowed.

1. Addition of two pointers.

2. Multiplying a pointer with a number

3. Dividing a pointer with a number.

9.2 MORE ABOUT POINTER ARITHMETIC

Here, we illustrate the usage of increment/decrement (++/--) and dereferencing operator (*) in implementing pointer arithmetic. The ++, -- and * operators fall in the same precedence group and their associativity is right to left. Consider the following array declaration for evaluating the results of the expressions that involve pointer arithmetic,

int arr[4]={1,2,3,4};

int *ip;

ip=arr; /*ip points to the first element in the array */

All the expressions listed below are assumed to be the statement immediately following the previous statement (i.e. ip=arr;).

Expression

Evaluation

++ip

ip points to the second element in the array

--ip

ip points back to the element 1 again

++*ip

since operators are associated right to left, the * ip operation is considered first and then ++ operator is applied, so the value of the element that ip is pointing to will be accessed first (*) and then the value is incremented by 1. Since ip was pointing the first element[arr[0]), its new value becomes 2 (1 +1).

(*ip)++

same as previous

*++ip

in this case the ++ will be evaluated first followed by the indirection operator *. Thus it accesses the content of the second element (i.e. arr[1]) from the array i.e. 2.

*(ip++)

same as previous

*ip++

same as previous.

9.3 DYNAMIC MEMORY ALLOCATION AND DEALLOCATION- malloc & free functions

A pointer can point to data objects of any type, not merely integer or float. Thus one can have a pointer to arrays, structures or even to functions. At this point, it is necessary to emphasise that, when a pointer variable is declared such as in

data *p;

where data is previously defined data type, space will not be automatically allocated for an object of type data. C supports a facility to allocate such space whenever it is required. Such dynamic allocation of memory space helps in reducing overall memory requirement for the program. Also as we will see later, such a facility enables us to create dynamic data structures such as lists, trees, etc., efficiently.

In order to allocate space for the data object pointed to by p, C provides a library function malloc(). The space thus created can be freed using the function free(). The procedure for using malloc function is illustrated below,

p=(data *) malloc(sizeof(data));

If data is int, then sizeof(int) will determine space required to store an integer data e.g. 4 bytes. Similarly if data is double then malloc will reserve a space of 8 bytes and will assign the address of this space to p. The sizeof function helps the program to become machine independent. This function returns a pointer to char type. Hence we need to use the type casting operator (data *) to match the type of p.

9.4 POINTERS AND ARRAYS

It can be recalled that a one dimensional array name essentially indicates (points) to the location where the array is stored.

Thus consider the array declaration,

int x[20];

Here x is treated as an address to the array. Incidentally, it stores the address of the first element (i.e. &x[0]) of the array. The location of any element say ith element of the array x can be determined in either of the two ways, &x[i] or (x + i). In (x + i), the integer quantity i (position of a specific (ith) element in the array) is added to the address of the first element to extract the address of the ith element. This is possible because all the elements of the array x, occupies a contiguous memory block.

The following example illustrates the usage of pointers in one dimensional array manipulations.

Example 9.5 :

#include

main()

{

static int x[5]={1,2,3,4,5};

int *p;

int i=2;

p=&x[i]; /* assigns p to the address of the third element */

p=(x + i); /* this statement is equivalent in all respect with the previous statement */

x[3]=*p; /*will put the value of the element 3 to the 4th element in the array*/

/* an alternate way to write the previous statement will be

*(x + 3)=*p;

*/

}

9.5 CREATION OF AN ARRAY USING POINTERS

We have already observed that an array name is actually a pointer to the first element in the array. However a conventional array definition results in a fixed block of memory being reserved at the beginning of the program which may lead to wastage of memory space during program execution. This problem can be avoided if an array is implemented using a pointer variable. The use of a pointer variable to represent an array requires some sort of initial memory allocation before the array elements are processed. This is done by using the malloc library function. Compare the following two blocks of statements to understand the difference between conventional array declaration and array implementation using pointers by dynamic memory allocation.

block 1 :

# include

# define size 10

int arr[size]; /* Conventional static memory allocation */

block 2:

# include

# define size 10

int *arr; /* defined as a pointer to an integer variable */

main()

{

:

arr=(int *)malloc(size * sizeof(int));

}

Example 9.6 :

This example illustrates the usage of a pointer in creating a one dimensional array of integers and sorting them.

#include

#include

main()

{

int i,n, *vector;

void bubble ();

printf (“Size of array ?”);

scanf (“%d”, &n);

printf (“%d”,n);

/* Dynamic memory allocation*/

/* sizeof function gives the size of any datatype in bytes*/

vector = (int*) malloc(n*sizeof (int));

printf (“\nEnter elements separated by blanks \n”);

for (i = 0; i

scanf(“%d”, vector+i); /* reading elements */

for (i = 0; i

printf (“%5d”, *(vector+i)); /* displaying elements*/

printf(“\n”);

/* invoking function*/

bubble (vector,n);

printf(“\n Sorted array \n”);

for (i = 0; i

printf(“%d\t”,vector[i]);

printf (“\n”);

} /*end of main() */

/*Bubble sort function*/

void bubble (x,m)

int *x,m;

{

int pass,i,temp;

for (pass = 0; pass

{

for (i = 0; i <>

{

if (*(x+i)>*(x+i+1))

{

temp = *(x+i);

*(x+i) = *(x+i+1);

*(x+i+1) = temp;

}

}

}

}

One dimensional character string

Similar to a one dimensional integer array, the character array or string name holds the address of the starting element. However, unlike the integer array, a character array is always terminated by a null (\0) terminator. Thus a character pointer can directly be assigned with a string constant. For example, consider the following initialisation,

char * city_string = “Calcutta - the City of Joy”;

such a statement however, can only appear outside main. To incorporate the same inside the main, the storage class static should be used as a prefix to the previous statement.

static char *city_string = “Calcutta-the City of Joy”;

Since, city_string is a pointer to a character, it can be incremented or decremented to traverse along the string. But it is always a better practice to retain the starting address of the string, because losing that may create difficulty in accessing the complete string.

A string variable defined using a character type pointer which will point to the first character of the string.

char *name=“Rabindra Nath Tagore”;

In the above statement, name is a pointer variable of character type. The pointer variable name is capable of pointing to any location in the memory. The initialisation of the pointer name with the string constant assigns a valid address for the variable.

The following programs illustrate usage of pointers for string manipulation.

Example 9.7 :

/* Compute length of a string */

#include

main()

{

char string [80],*ptr;

ptr = string;

printf (“Enter the string to find it’s length\n”);

/* reads in string*/

while ((*ptr++ = getchar ()) ! = ‘\n’);

* - - ptr = ‘\0’;

printf (“\nString is : %s\n”, string);

printf (“nIt’s length is : %d\n”, (ptr - string));

}

Example 9.8 :

/*String concatenation */

#include

#define length 40

main()

{

/*s1, s2 and s3 are pointers to characters */

char *s1, *s2, *s3,c;

int i,j,k;

/* allocate memory */

s1 = (char*) malloc (length*sizeof(char));

s2 = (char*) malloc(length *sizeof(char));

s3 = (char *) malloc(2 *length*sizeof(char));

printf (“Enter string one\n”);

scanf (“%s”,s1);

printf(“%s\n”,s1);

prinft (“\nEnter string two\n”);

scanf (“%s”,s2);

i=0;

while ((c = *(s1 + i)) ! = ‘\0’)

{

s3[i] = c;

i++;

}

k = 0;

while ((c = *(s2 + k)) ! = ‘\0’)

{

s3[i+k] = c;

k ++;

}

s3[k]=‘\0’;

printf (“\nConcatenated string is :\n”)

printf (“%s\n”, s3);

}

1. State what the following C code is doing :

void foo(char *s, char *t)

{

while((*s++ = *t++) != ‘\0’);

}


9.6 TWO DIMENSIONAL ARRAYS

As mentioned earlier, a two dimensional array can be treated as a collection of rows where each row can be interpreted as a one dimensional array. Thus, a two dimensional array can be visualised as a pointer to a group of contiguous one dimensional arrays. Such declarations can be done as follows,

(*) []

Example 9.9 :

The array declaration,

int x [10] [20];

can be written as,

int (*x) [20];

The above declaration defines a two dimensional array of unknown number of rows, where each row or a one dimensional array consists of 20 contiguous elements. In the previous declaration x is a pointer to a pointer, that typically stores the address of the pointer pointing the first row or the first string in the group of strings.

Let us consider the following example,

int x[3][3] = {

{1,2,3},

{4,5,6},

{7,8,9}

};

A schematic representation of the array x is shown in the following figure,


x[0]

1

2

3

x[1]

4

5

6

x[2]

7

8

9

Fig. 9.1 : A schematic representation of two-dimensional array

To print the value of an element say x[1][1] of the array x using the conventional subscript implementation, one can use the following printf statement,

printf (“%d”, x[1][1]);

Accessing each element of a two dimensional array can also be done using a pointer instead of using subscripts. In order to refer to the 2nd element in the 2nd row using the pointer x, first we have to access the row two, which is done by adding 1 to the address value of x i.e., (x + 1). But (x + 1) is the address location of the pointer that points to the row two of the array x. Hence prefixing (x + 1) by indirection operator (*) will extract the address of the first element of the row two i.e., *(x + 1), is equal to &x[1][0]. The address of the next element is calculated by adding one to the previous elements address i.e., *(x+1)+1. To find out the content of that location we again prefix the address by the indirection operator *. So *(*(x+1)+1) will correspond to x[1][1] and the following printf statement can be used to print it.

printf (“%d”, *(*(x+1)+1));

Thus, *(*(x + row) + col) actually corresponds to the element x[row][col].

As observed above, (*x)[5] is a pointer to an array with undefined number of rows where each row consists of a contiguous block of 5 elements. To create the two dimensional array memory space has to be allocated for the rows of the array. This can be done by the malloc function which would allocate space for the specified number of rows (nrows) and columns(ncols).

*x=(int *)malloc(nrows * ncols * sizeof(int));

9.7 ARRAY OF POINTERS

A two dimensional array can be treated as a one dimensional array of pointers (addresses), where each of them can point to an array of elements that can be dynamically allocated and assigned to each such pointer. The syntax used for declaring an array of pointers is,

*[];

The advantage associated with this approach is that a fixed block of memory need not be reserved in advance as is done with a conventional array. Dynamic allocation of memory is carried out using the malloc function.

Consider the following declaration,

int *x[5];

which says x is an array of five pointers. Each of these pointers can be used to create an one dimensional array as discussed earlier. The difference in the declaration between pointer to an array and array of pointers can be seen from the missing braces “()” around *x.

To allocate adequate memory for the array, one can use a for repetitive construct so that in each pass a space for one dimensional array having a given number of elements (nclos) is created using the malloc function.

Example 9.10 :

/* Demonstration of memory allocation*/

{

:

int i;

for(i=0;i <>

x[i]=(int *)malloc(nclos * sizeof(int));

:

}

Example 9.11 :

/*usage of array of pointers for creating a two dimensional matrices and their multiplication */

# include

main()

{

int *a[10],*b[10],*c[10];

int i,j,n,r1,r2,c1,c2;

void get_data();

void disp_matrix();

void mult_matrix();

printf(“Size of Matrix A :”);

scanf(“%d %d”,&r1,&c1);

fflush(stdin);

printf(“Size of Matrix B :”);

scanf(“%d %d”,&r2,&c2);

fflush(stdin);

if(c1 != r2)

{

printf(“\n Multiplication is not possible \n\n”);

printf(“\n Error! Number of columns in A is not equal to the number of rows in B\n”);

exit();

}

/*Memory allocation */

for(i=0;i < style="">

{

a[i]=(int *)malloc(c1 * sizeof(int));

c[i]=(int *)malloc(c2 * sizeof(int));

}

for(j=0;j < style="">

b[j]=(int *)malloc(c2 * sizeof(int));

printf(“Matrix A - elements ?\n”);

get_data(a,r1,c1);

disp_matrix(a,r1,c1);

printf(“\nMatrix B - elements ?\n”);

get_data(b,r2,c2);

disp_matrix(b,r2,c2);

mult_matrix(a,b,c,r1,c1,c2);

printf(“\nResultant matrix C - (%d X %d)\n”,r1,c2);

disp_matrix(c,r1,c2);

}

void get_data(int *x[10],int r,int c)

{

int i,j;

for(i=0;i <>

{

for(j=0;j <>

(

scanf(“%d”,(*(x + i) + j));

fflush(stdin);

printf(“\t”);

}

printf(“\n”);

}

}

void disp_matrix(int *x[10],int r,int c)

{

int i,j;

for(i=0;i <>

{

for(j=0;j <>

printf(“%d\t”,*(*(x + i) + j));

printf(“\n”);

}

}

void mult_matrix(int *a[10],int *b[10],int *c[10],int rw,int m,int cl)

{

int i,j,k;

for(i=0;i <>

{

for(j=0;j <>

{

*(*(c + i) + j)=0;

for(k=0;k <>

*(*(c + i) + j) += (*(*(a + i) + k)) * (*(*(b + k) + j));

}

}

}

2. Distinguish between the following :

i. int *a[10];

ii. int (*a)[10];


9.8 POINTER TO A FUNCTION

A pointer can point to a C function (pointer to function), since C functions have addresses. The function address can be assigned to a pointer to function type and the function can indirectly be invoked using that pointer.

The syntax for declaring a function pointer is as given below,

(*function_pointer)();

where is the type of the data returned by the function pointed to by the function­_pointer.

Let us see how this can be done.

Example 9.12 :

/* program displays the address of a function */

# include

main()

{

void warning();

printf(“\n Address of function warning() is : %u\n”,warning);

warning(); /* this statement invokes the function */

}

void warning()

{

printf(“Don’t go out in the rain!!\n”);

return;

}

A typical output of this program would be:

Address of function warning() is : 450234

Don’t go out in the rain!!

In the above program, the function declaration warning, that appears within main, is actually a pointer to that function. So trapping the function address in a pointer will help in invoking the function indirectly. The following example illustrates the technique,

Example 9.13 :

# include

main()

{

void warning();

void (*fptr)(); /*defining a pointer to a function returning void or no data */

fptr=warning; /*assigning the address of the function warning to fptr*/

printf(“\n Address of function warning() is : %u\n”,fptr);

(*fptr)(); /* this statement indirectly invokes the function warning()*/

}

void warning()

{

printf(“Don’t go out in the rain!!\n”);

return;

}

Let us examine the following statement,

void (*fptr)();

which indicates that fptr is a pointer to a function returning void or no data.

Activation of the function warning, pointed to by fptr is performed using the following statement,

(*fptr)();

3. Give a suitable declaration for each of the following cases:

(i) Declare two pointer variables which point to the integers variables i and j.

(ii) Declare a function that accepts two integer arguments and returns a long integer. Each argument will be a pointer to an integer quantity.

(iii) Declare a one dimensional, floating point array of long integer using a pointer.

(iv) Declare a two dimensional floating point array having 10 rows and 20 columns, using pointers.

(v) Using pointers, declare an array of strings whose initial values are “black”, “yellow”,and “red”.


9.9 PASSING POINTERS TO A FUNCTION

Since C follows "Call by value" technique for parameter transfer, when a pointer is passed as a parameter, it effectively implements "Call by reference". It has been mentioned before in Chapter 6 that in the call by value scheme, the data items passed as actual parameters get copied to the corresponding formal parameters of the called function. Thus any changes made to the content of those variables are not carried back to the calling routine. But if an argument is passed by reference (address of the variable), that is a pointer is passed, the content of that address can be accessed freely in either of the calling and called program to the extent that changes made in any program gets reflected in the other.

For example, a variable x in the calling function my_function is declared as:

my_function()

(

vector *x;

your_function(x);

:

}

and suppose that the corresponding formal parameter in the called function your_function is declared likewise.

your_function(v)

vector *v;

{

:

}

Regardless of whether v is declared as transmitted by value or by reference, the effect of transmitting x as the actual parameter is to allow direct access of the vector to which x points to. If transmission is by value, then your_function has its own copy of the l-value that x contains, so both x and v point to the same vector. In case of call by reference transmission, v would contain a pointer to x that contains a pointer to the vector. In general, whenever the actual-parameter data object contains a pointer or pointer components, the data objects designated by these pointers will be directly accessible from the called function, regardless of the method of parameter transmission. It is because of this property of pointer variables and the arithmetic operations allowed on pointer variables, C does not need an explicit call-by-reference mechanism. The call by value scheme of parameter transmission is sufficient.

Example 9.14a :

# include

main()

{

int u=1,v=3;

void func1(int *pu,int *pv);

:

:

func1(&u,&v);

}

void func1(int *pu,int *pv)

{

*pu=0;

*pv=0;

return;

}

The function func1 will reset the value of u and v.

It may be recalled that the standard library function scanf requires the address of a variable for inputting data into the variable.

Example 9.14b :

void func1(int *pu,int *pv)

{

:

int *pu,*pv;

pu=&u;

pv=&v;

scanf("%d %d ",pu,pv); /* Note that no & symbol appears before pu and pv in the scanf statement */

:

}

Components of data structures.

Consider a function change given below. Instead of simple variables and constants, we pass components of data structure as parameters to change.

/*called function*/

void change(int i,int *j)

{

i=i + 5;

*j=*j + 5;

printf(“i=%d; j= %d\n”,i,*j);

return;

}

Let us also consider another function my_function defined below which calls change.

/* calling function */

void my_function()

{

int c[4];

int m;

c[0] = 5;

c[1] = 6;

c[2] = 7;

c[3]=8;

change(c[1], &c[2]);

for (m=1;m <= 3; m++)

printf(“%d\t”,c[m]);

printf(“\n”);

}

When my_function is executed, the values printed are

i=11; j=12

6 12 8

Here c[1] is transmitted by value. Hence the expression c[1] is evaluated by referencing c and then selecting its second component. The result is the r-value of that component. Formal parameter i of change is initialised to this r-value. Similarly, &c[2] is evaluated and a pointer to the component is transmitted. Assignments within change then directly changes this component of c via the pointer stored in its formal parameter j.

Within change, the code that is executed to manipulate i and j is the same, regardless of whether the call for change is change(a,&b) or change(c[1],&c[2]).

Array components with computed subscripts.

Consider a function new_change having parameters which are pointers to integers,

void new_change(int *i, int *j)

{

*i = *i + 1;

*j = *j + 1;

printf (“%d %d\n”, *i, *j);

}

Suppose my_function is as above, but new_change is substituted for the call to change:

void my_function()

{

int c[4];

int m;

c[0] = 5;

c[1] = 6;

c[2] = 8;

c[3]=9;

m=1;

new_change(&m, &c[m]);

for (m=1;m <= 3; m++)

printf(“%d\t”,c[m]);

printf(“\n”);

}

What values would be printed when my_function is executed now? Note that m has the value 1 initially, but because we are passing a pointer to m (i.e. reference parameter), its value is changed to 2 in new_change before c[m] is incremented via the pointer in j. The statement *j = *j + 1 is executed next followed by addition of one to an element of c. Which element of c gets incremented? Here it must be c[1] that is incremented, not c[2], because the actual-parameter expression c[m] is evaluated at the time of call of new_change to get a pointer to a component of c. When new_change is called, m has the value 1, so it is a pointer to c[1] that is transmitted to new_change. The function new_change only knows the existence of c[1], since within new_change the pointer to c[1] appears the same as a pointer to any other integer data object. Thus the values printed are: 2 7 7 8 9.

9.10 FUNCTION RETURNING POINTER

A function can also return a pointer to a calling program as follows.

Example 9.15 :

# include

main()

{

char name_string[80],*out,*squeeze(char s[]);

printf("\nEnter a string : ");

gets(string);

fflush(stdin);

out=squeeze(name_string);

printf("\nOutput string is : %s\n",out);

}

char *squeeze(char s[]) /* function returns a pointer of type character */

{

char *q,*r;

r=s;

q=s;

while(*r)

{

for(;*q == *r;r++);

q++;

*q=*r;

}

return s;

}

To summarise the followings are the list of pointer declarations with increasing complexities :

int *p;

/*p is an pointer to an integer data */

int *p[15];

/* p is an array of 15 pointers to integer data */

int (*p)[15];

/* p is a pointer to an 15-element integer array */

int *p(int)

/* p is a function with integer argument and returns an pointer to an integer */

int *p(int *x)

/* p is a function with an argument which is a pointer to an integer data and p returns a pointer to an integer */

int (*p)(float *x);

/* p is a pointer to a function which has an argument that is a pointer to a floating point data and the function returns an integer data */

in t*(*p[10])(float a);

/* p is an array of pointers to functions that accept floating point data as argument and return pointers to integer data */

4. Consider the C program segments as shown below.

i.

main ( )

{

int i;

static int a[7] = {1, 2, 3, 4, 5,6,7};

int f1 (int *p);

...

i = f1(a + 2);

}

int f1(int *p)

{

int i, sum = 0;

for (i = 2; i <>

sum += * (p + i);

printf(“sum=%d”, sum);

return (sum);

}

(a) What is the type of the actual parameter which is passed to f1.

(b) What does f1 return and what is the type of the result ?

(c) What is the type of the parameter that f1 expects ?

(d) What value would be displayed by the printf statement within f1?

ii. main ( )

{

int *p1, *p2;

int f1(int *px, int *py);

int f2(int *px, int *py);

int *f3(int (*pt) (int *px,int *py));

...

p1 =f3(f1);

printf (“%\n”, *p1)

...

p2 =f3(f2);

printf (“%\n”, *p2)

...

}

int f1 (int *px, int *py)

{

int x;

x = *px + *py;.

return(x);

}

int f2 (int *p1 int *p2)

{

int result;

result =*p1 - *p2;.

return(result);

}

int *f3(int (*pf) (int *px, int *py))

{

int x, y, z;

x = 2; y = 1

. . .

z = (*pf) (&x, &y);

. . .

return (&z);

}

a. What are the types of the arguments of f1, f2 and f3?

b. What does f3 do with f1 and f2?

c. In what sequence the functions are called?

d. What values will be printed in the main function ?


9.11 COMMAND LINE ARGUMENTS - PASSING PARAMETERS TO FUNCTION main

The controlling function main, which gets executed first on invocation of a C program, collects command line arguments typically in two variables argc and argv. The argc is essentially the argument count, an integer quantity that identifies the number of command line arguments the program is to be invoked with. The argv is an array of pointers to characters (i.e. an array of strings) that hold the arguments (one per string).

Let us consider the C program in example 9.16 that expects 3 command line arguments as described by the command line statement prototype,

extract

Here argc in main gets value 4. The array of pointers to characters i.e., argument vector (argv) gets initialised with the parameters passed as strings. When the program extract is invoked as,

$ extract “Sound of Music” 10 5

argv receives the arguments “Sound of Music”,10,5. The program extract is supposed to extract number of characters determined by the number2 from the string (string1) starting from the position (determined by number1). Hence with the given command line input at the end of the program execution the display will be “Music” on the screen.

Example 9.16 :

#include

main (int argc, char * argv[])

{

int i,len, no1, no2;

if (argc != 4 )

{

printf (“\nUsage: extract \n”);

exit();

}

if (( no1=atoi (argv [2])) == 0)

{

printf(“\nThe second argument needs to be a number\n\n”);

exit();

}

if (( no1=atoi (argv [3])) == 0)

{

printf(“\nThe third argument needs to be a number\n\n”);

exit();

}

len=strlen (argv[1]);

if (no1 > len)

{

printf(“\n The starting position is greater than string width\n\n”);

exit();

}

printf(“\nThe string extracted is : \n”);

for (i = 0; argv [no1] != ‘\0’ && i < no2; no1 ++, i ++)

printf (“%c” ,argv[no1]);

printf(“\n”);

}

5. The outline of a C program is shown below.

main (int argc, char *argv [ ])

{

...

}

a) Suppose the compiled object program is stored in a file called demo.exe, and the following commands are isssued to initiate the execution of the program:

demo debug fast

Determine the value of argc and the nonempty elements of argv.

(b) Suppose the command line is written as

demo “debug fast”

How will this change affect the values of argc and argv ?


9.12 USAGE OF POINTERS TO PASS FUNCTION AS PARAMETERS TO OTHER FUNCTIONS

We have already seen how a function pointer say fp can be used to point to a function. If such a pointer is used as a parameter to another function say f, then f can use fp to call different functions. The following example illustrates this feature, for sorting an array of elements, where an array element can be either an integer or a character string. Note that one has to use different operators (or functions) to compare character strings or integers. These functions are invoked within the sort routine with the help of a function pointer compare, which is made to point to the function str_cmp for comparing character strings and to num_cmp for integer numbers. This feature helps in creating polymorphic functions.

Example 9.17 :

# include

# include

# define NLINES 100

main (argc,argv)

int argc;

char *argv[ ];

{

char *linptr [NLINES];

int nlines;

int str_cmp(char *x, char *y), num_cmp(char *s1, char *s2);

void swapping(char *px[], char *py[]);

void readlines(char *lineptr[],int no_lines);

void writelines(char *lineptr[],int no_lines);

void sort(char *linptr[],int (*compare)(),int (*swapping)());

int numeric = 0;

if (argc > 1 && argv[1][0] == ‘-’ && argv[1][1] == ‘n’)

numeric = 1;

if ((nlines = readlines (linptr , NLINES ))>0)

{

if (numeric)

/*call sort with function pointer compare pointing to num_cmp*/

sort (linptr,nlines,num_cmp, swapping);

else

/*call sort with function pointer compare pointing to str_cmp */

sort (linptr, nlines, str_cmp, swapping);

writelines (linptr,nlines);

}

else

printf(“Input too big to sort\n”);

}

# define MAXLEN 1000

void readlines (lineptr, maxlines)

char *lineptr[ ];

int maxlines;

{

int len, nlines;

char *p, line [MAXLEN];

nlines = 0;

while ((len = getline (line, MAXLEN))>0)

{

if (nlines >= maxlines)

return (-1);

else if ((p=(char *) malloc (len)) == NULL)

return (-1);

else

{

line [len] = ‘\0’;

strcpy(p, line);

lineptr [nlines++] = p;

}

}

return(nlines);

}

int getline(ln,maxl)

char *ln;

int maxl;

{

int l=0;

while((ln[l++]=getchar()) != ‘\n’);

return(l - 1);

}

/* numeric comparison function */

int num_cmp (s1, s2)

char *s1, *s2 ;

{

double atof( ), r1,r2;

r1 = atof(s1);

r2 = atof(s2);

if (r1 <>2)

return (-1) ;

else

if (r1 > r2)

return (1);

else

return (0);

}

/* string comparison function */

int str_cmp (x , y)

char *x,*y;

{

for ( ; *x == *y; x++,y++)

if (*x == ‘\0’)

return (0);

return (*x - *y);

}

void swapping (px , py)

char *px[ ] , *py[ ];

{

char *tcmp;

temp = *px;

*px = *py;

*py = temp;

}

void writelines (lineptr, nlines)

char *lineptr[];

int nlines;

{

while (--nlines >=0)

printf(“%s \n”, *lineptr++);

}

/* sorting routine with function pointers compare and exch */

void sort ( v , n , compare , exch)

char *v[ ] ;

int n;

int (*compare)(), (*exch)();

{

int i, j,not_sorted=1;

j=n;

while(not_sorted)

{

not_sorted=0;

for (i = 0; i < ( j - 1) ; ++i)

{

if (( *compare) ( v[ i ] , v[ i+1] )> 0)

{

(*exch ) ( & v[i] , & v[ i+1 ] );

not_sorted=1;

}

}

j--;

}

return;

}

6. Give an appropriate declaration for each of the following cases using pointers:

(a) Declare a function that accepts a pointer to an integer as its argument and returns a pointer to an integer.

(b) Declare a function that accepts an argument which is a pointer to a two dimensional array of integers and returns an integer.

(c) Declare a function having a pointer to integers as its argument and returns a data of type double.

(d) Declare a function that accepts a pointer to an integer array and returns a pointer to an array of characters.

(e) Declare a pointer to a function f that expects as its argument a pointer to a function having two arguments each of type integer and f returns floating point data.

(f) Declare a two dimensional array of pointers to functions having 10 rows and 5 columns. Each of these functions will accept two arguments of type float and will return a double precision quantity.

(g)Declare a function f that accepts another function as an argument and returns a pointer to an integer. The function which is passed as an argument to f can accept a floating point number as its argument and will return an integer quantity.

(h) Declare a pointer to a function f that accepts three arguments two of which are integer data and the third is an one dimensional array of integers.

(i) Declare a pointer to a function that accepts two pointers, one of which is a pointer to a function and the second one is a pointer to an integer data as arguments and returns a pointer to an integer data.

(j) Declare a function f that accepts an argument which is a pointer to a function from float to float, and returns a float.

7. What will be printed by the following program?

# include

main(int argc, char *argv[])

{

printf(“%s\n”,*argv);

}

8. How would you interpret the following declarations ?

(a) float *px;

(b) int (*p) [10] ;

(c) char *p[15];

(d) double (*p)(void);

(e) int (*p) (char *a,int *b);

(f) char (*(*x[4]) () )[5];

(g) int *(*p)(char *a[]);

(h) int *(*p) (char (*a)[]);

(i) float (*f) (int *p);

(j) int (*f(int *p)) [10];

(k)double *g(int (*p) []);

(l) long int (*f) (int (*p) []);

(m) char *(*p[10]) (char *c);

0 comments: