PART 13

Functions, Pointers, and Arrays

Pointers

References

Functions

Arrays

Copyright © 1992-1997 Thomas P. Sturm

POINTERS

A variable is a location in the computer which contains certain kinds of data.

A pointer variable is a variable which contains a pointer.

To define a pointer variable which will hold a pointer to a variable which contains a certain type of data, a definition of the form

type *variable_name ;

is used.

For example:

int *b;

declares b as a pointer variable which can hold a pointer to an integer variable.

double *h;

declares h as a pointer variable to a double variable.

The address of a variable (pointer to variable) is obtained with the operator. For example, if j is an integer variable, then &j is its address.

The variable pointed to by the pointer in a pointer variable is obtained by the * operator. For example, if k is a pointer variable which holds a pointer to an integer variable, then *k refers to the integer variable.

The following two program segments have an identical effect:

int x=3, y;int x=3, y;

y = x;int *px;

px = &x;

y = *px;

POINTER INITIALIZATION

To initialize a pointer along with the pointer definition, the following sequence must be used:

char c = 'Q';/* this must come first */

char *char_ptr = &c;

The above definition and initialization (2nd line above) could have been done in two statements as:

char *char_ptr;

char_ptr = &c;/* not *char_ptr = &c; */

While having the same effect, this is not an initialization (as defined by the C language)

ptr01.cpp

/* PRT01.CPP: Illustrates the use of pointers */

#include <iostream.h>

main()

{

int x, count = 10;/* declare x and count as integer

variables, initialize count = 10 */

int *int_ptr;/* define int_ptr as a pointer

variable */

int_ptr = &count;/* assign address of count to

int_ptr */

x = *int_ptr;/* reference the value pointed to

by int_ptr and assign it to x */

cout < x < endl;/* print x */

return 0;

}

ptr02.cpp

/* PRT02.CPP: Further illustrates the use of pointers */

#include <iostream.h>

main()

{

char c;

char *char_ptr;/* define char_ptr as pointer to

a char */

char_ptr = &c; /* put address of c in char_ptr */

c = 'A';

cout < c < " " < *char_ptr < endl;

c = 'Z';

cout < c < " " < *char_ptr < endl;

return 0;

}

ptr03.cpp

/* PRT03.CPP: Illustrates the use of pointer assignment */

#include <iostream.h>

main()

{

inti1, i2,

*p1, *p2;/* p1, p2 declared to be pointers

to integers */

i1 = 6;

p1 = &i1;

i2 = (*p1)/2;/* could be written as *p1/2 */

p2 = p1;

cout < i1 < " " < i2 < " " < *p1 < " "

< *p2 < endl;

return 0;

}

swap0.cpp

/* SWAP0.CPP: Program shows how NOT to write a function to

exchange (swap) the contents of two integer variables

because parameters default to pass by value */

#include <iostream.h>

void swap(int a, int b);

main()

{

int x = 2, y = 4;

cout < "x = " < x < ", y = " < y < endl;

swap(x, y);

cout < "x = " < x < ", y = " < y < endl;

return 0;

}

void swap(int a, int b) /* interchange *px and *py */

{

int temp;

temp = a;

a = b;

b = temp;

}

swap1.cpp

/* SWAP1.CPP: Program shows how a function to exchange

(swap) the contents of two integer variables can be

handled using pointers */

#include <iostream.h>

void swap(int *px, int *py);

main()

{

int x = 2, y = 4;

cout < "x = " < x < ", y = " < y < endl;

swap(&x,&y);

cout < "x = " < x < ", y = " < y < endl;

return 0;

}

void swap(int *px, int *py) /* interchange *px and *py */

{

int temp;

temp = *px;

*px = *py;

*py = temp;

}

swap2.cpp

/* Program shows how a function to exchange (swap) the

contents of two integer variables can be handled in C++. */

#include <iostream.h>

void swap(int & a, int & b);

main()

{

int x = 2, y = 4;

cout < "x = " < x < "; y = " < y < "\n";

swap( x , y );

cout < "x = " < x < "; y = " < y < "\n";

return 0;

}

void swap(int & a, int & b) // interchange a and b

{

int temp = a;

a = b;

b = temp;

}

POINTERS / REFERENCES

While passing pointers is the most general and flexible way to alter the values of a variable, C++ provides a simpler mechanism if it is desired to alter the values of the variables or to avoid the overhead of copying a variable onto the calling stack.

C++ supports a special type of identifier called a reference. The reference is like the var in Pascal. It is, in effect, the way all variables are handled in FORTRAN.

The program swap2.cpp shows how much simpler looking the function, and the call to the function, can become in C++. Variable names can be passed by the calling function, but both value and address is available to the called function when the is used.

One must be very cautious in writing a C++ function using references (the in front of the variable name in the function definition). There is the potential for additional side-effects. Namely, C++, by using the , can, in effect, modify the parameters used in the function call.

refern.cpp

/* REFERN.CPP: demonstrate use of functions returning

lvalues */

#include <iostream.h>

#define TRUE 1

#define FALSE 0

int & which (int& x, int& y, int w);

main()

{

int a, b, c;

a = 12;

b = 25;

c = 37;

cout < "a = " < a < " b = " < b < " c = "

< c < "\n";

which (a, b, TRUE) = 8;

cout < "a = " < a < " b = " < b < " c = "

< c < "\n";

which (a, b, FALSE) = c;

cout < "a = " < a < " b = " < b < " c = "

< c < "\n";

return 0;

}

int & which (int& x, int& y, int w)

{

if (w)

return x;

else

return y;

}

USING REFERENCES TO OBTAIN LVALUES

References can also be used as return values from functions. When they are, they become lvalues.

The function which in the program refern.cpp uses the third parameter (a logical variable) to determine which one of the two references passed to the function should be used as the lvalue to accept the result of the assignment.

Reference parameters are efficient and should be used in place of passing large structures to or from functions.

Notice the function prototyping that is required (not optional) in C++. In C++ two functions differing in the data types of their parameter list (or the size of the list) are considered different functions.

Functions with the same name but differing parameter types are kept distinct through a process called name mangling. Name mangling involves prepending a sequence of codes to the function name to distinguish different parameter data types. (This only becomes an issue if you are directly calling a C++ function by name from another programming language.)

ONE-DIMENSIONAL ARRAYS

A one-dimensional array is stored sequentially in C++ as illustrated:

location / reserved
or address / for value of
a / a[0]
a+1c / a[1]
a+2c / a[2]

Where c represents the length of the array element.

Pointers in C++ do not need to worry about the length of the array element if they always use the proper data type. C++ uses the sizeof operator to perform pointer arithmetic that is correct for the data type and its representation on the machine being used.

By analogy with scalar variables, if we define

inti,a[5];

then

iis like a[0] or a[1] or ... or a[4]

& iis like a

ptr05.cpp

/* PRT05.CPP: Illustrates pointer arithmetic */

#include <iostream.h>

main()

{

int i, x[100];

int *p, *p1, *p2;

// initialize array with 100, 103, 106, ... , 397

for (i=0; i<100; ++i)

x[i] = 3*i + 100;

p = &x[10];

p1 = &x[20];

p2 = &x[30];

cout < *(p+5) < " " < (p2-p1) < endl;

return 0;

}

POINTER ARITHMETIC

If p is a pointer to an array element and if *p has the same data type as elements of the array, then

p + n,(n must be an integer)

points to n elements further down in the array and

*(p + n)

is the element n terms further in the array.

If p1 and p2 are pointers to elements of the same array and if p2 points to an element further ahead then p1, then

p2 - p1

is an integer indicating the number of array elements between *p2 and *p1.

ptr06.cpp

/* PRT06.CPP: Illustrates the relation between pointers

and arrays */

#include <iostream.h>

main()

{

int i, *p;

static int y[5] = {10, 9, 8, 7, 6};

for (i=0; i<5; ++i)

cout < y[i] < endl;

for (i=0; i<5; ++i)

cout < *(y+i) < endl;

p = y;

for (i=0; i<5; ++i)

cout < *(p++) < endl;

return 0;

}

POINTERS AND ARRAYS

If an array x is defined in a C++ program, e.g.

char x[100];

then:

1.The data type of x is "one dimensional array of char's of length = 100".

2.If x is used in an expression it is immediately converted to a CONSTANT POINTER TO THE FIRST ELEMENT OF THE ARRAY.

This means that we can access the i-th element of the array either by

x[i]

or by

*(x + i)

BOTH WAYS ARE EQUIVALENT.

If x is an array of characters and f is a C++ function that takes x as an argument ( f(x) ) then the argument to f can be declared in either of the following two EQUIVALENT ways.

f(char *z)f(char z[])

{or{

......

}}

wrong1.cpp

/* WRONG1.CPP: This program is dubious - it may not always

execute correctly. Why? */

#include <iostream.h>

main()

{

int *x;

int i;

for (i=0; i<100; ++i)

*(x+i) = i;

for (i=0; i<100; ++i)

cout < *(x+i) < endl;

return 0;

}

wrong2.cpp

/* WRONG2.CPP: This program is incorrect, it will not

compile. Why? */

#include <iostream.h>

main()

{

int i, x[100];

for (i=0; i<100; ++i)

x[i] = i;/* or *(x+i) = i; */

for (i=0; i<100; ++i)

cout < *(x++) < endl;

return 0;

}

LAB 1 for Part 13

1.Write a C++ program that accepts a Fahrenheit temperature from the user at the terminal and outputs the Centigrade equivalent. Give an appropriate user prompt with cout, and then accept the user input with cin. Test the program not only with correct input, but also with some bad data, such as character strings.

2.Save all work in progress. Then compile and attempt to run the program wrong1.c.

Note to PC Users: Beware that if this program is run on a PC, the machine may crash. It may not crash the first time it is run, but rather the second or third time. Even if the machine does not crash, reboot your machine after this exercise.

3.Attempt to compile wrong2.c. It should fail to compile. Relate the error message to the error in the program.

4.Write a program to determine the largest member of a one-dimensional integer array. Use pointers to access the array. How general can this program be written?

array.cpp

/* ARRAY.CPP: Demo of defining a two dimensional array

and passing it to a function. */

#include <iostream.h>

int trace(int [][4]);

main()

{

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

{5, 6, 7, 8},

{9, 10, 11, 12},

{13, 14, 15, 16} };

cout < "Sum of diagonal = " < trace(x) < endl;

return 0;

}

int trace(int z[][4])

// return sum of diagonal elements

{

return( z[0][0] + z[1][1] + z[2][2] + z[3][3] );

}

TWO-DIMENSIONAL ARRAYS

The definition

int x[2][3];

defines x to be a 2 x 3 array of integers. (Actually, it is an array of 2 arrays of size 3.) As in the case of one-dimensional arrays, the indices start from 0. Therefore, the six integer elements of the array x defined above are:

x[0][0] x[0][1] x[0][2]

x[1][0] x[1][1] x[1][2]

A two-dimensional array (as well as any array of higher dimension) is stored sequentially in C++. The subscript combinations are linearized in "numerical order" by allowing the rightmost subscript to run most rapidly, so the array elements would be stored as follows:

location / reserved
or address / for value of
x / x[0][0]
x+1c / x[0][1]
x+2c / x[0][2]
x+3c / x[1][0]
x+4c / x[1][1]
x+5c / x[1][2]

Where c represents the length of an integer array element.

Pointers in C++ do not need to worry about the length of the array element if they always use the proper data type. C++ uses the sizeof operator to perform pointer arithmetic that is correct for the data type and its representation on the machine being used.

array2.cpp

/* ARRAY2.CPP: Program shows relation between pointers

and two dimensional arrays. */

#include <iostream.h>

#define P(y) cout < y < endl

main()

{

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

{5, 6, 7, 8},

{9, 10, 11, 12} };

/* x is an array of size 3 whose elements are

arrays of size 4 */

P( **x );

P( *(x[1] + 2) );

P( *(*x + 2) );

P( **(x + 2) );

P( x[2][2] );

P( *( *(x + 1) + 2 ) );

return 0;

}

ACCESSING ARRAY ELEMENTS

Array elements may be obtained by using either a pair of subscripts or a pointer. The following three references are equivalent:

x[1][2]or*((double *)x + 5)

or*(*(b + 1) + 2)

If f is a function returning an integer that takes x as an argument (f(x) ) then f must be declared as:

int f(int z[2][3]); or

int f(int z[][3]); or

int f(int (*z)[3]);

Two-dimensional arrays are initialized in a similar way to one-dimensional arrays.

int x[2][3] = {{1.0, 2.3, 3.6},

{4.2, 5.4, -6.3} };

or

int x[2][3] =

{1.0, 2.3, 3.6, 4.2, 5.4, -6.3};

Both ways give the same result, although the former is preferred from a readability and defensive programming point of view.

Program array.cpp cannot be modified using the techniques it employs to sum the diagonal of a square matrix of arbitrary size.

diagonal.cpp

/* DIAGONAL.CPP: This program uses pointers to access an

arbitary size two-dimensional array from a function */

#include <iostream.h>

#define SIZE 5

double diagsum (double *, int);

main()

{

double a[SIZE][SIZE], r;

int i, j, n=SIZE;

for (i=0; i<n; i++)

for (j=0; j<n; j++)

a[i][j] = (double)i + (double)j/100.;

r = diagsum((double *)a, n);

cout < "sum is " < r < endl;

return 0;

}

double diagsum(double *a, int n)

{

int i;

double sum=0;

for (i=0; i<n; i++)

sum += *(a + i*(n+1));

return (sum);

}

LOCATION OF ARRAY ELEMENTS IN C++

The C++ language allows arrays of any dimension to be defined.

For 1-dimensional arrays:

if array a is defined to be of size a[P], then

loc( a[i] ) = loc(a) + i

For 2-dimensional arrays:

if array b is defined to be of size b[P][Q], then

loc( b[i][j] ) = loc(b) + i*Q + j

For 3-dimensional arrays:

if array c is defined to be of size c[P][Q][R], then

loc( c[i][j][k] ) = loc(c) + i*Q*R + j*R + k

For 4-dimensional arrays:

if array d is defined to be of size d[P][Q][R][S], then

loc( d[i][j][k][l] ) = loc(d) + i*Q*R*S + j*R*S

+ k*S + l

In general, each array subscript is multiplied by the product of the defined array size for the subscripts remaining on its right.

ptr07.cpp

/* PTR07.CPP: Exercise on pointer arrays. What is the

output of this program? */

#include <iostream.h>

#define PRINT(x) cout < x < endl

main()

{

static char *names[3] =

{ "JOE", "BILL", "ALBERT"};

char **p;

p = names;

PRINT( *p );

PRINT( *(p+1) );

PRINT( (*(p+1) + 1) );

PRINT( ((*p) + 1) );

PRINT( *(p+2) );

PRINT( (*(p) + 2) );

return 0;

}

clue.cpp

/* CLUE.CPP: Exercise on pointer arrays.

What is the output of this program? */

#include <iostream.h>

#define PRINT(x) cout < x < endl

main()

{

static char *names[3] = { "JOE", "MARY", "albert"};

char **p;

p = names;

PRINT( *p );

PRINT( (*p + 1) );

PRINT( (*p + 2) );

PRINT( *(p+1) );

PRINT( (*(p+1) + 1) );

PRINT( (*(p+1) + 2) );

PRINT( (*(p+1) + 3) );

PRINT( *(p+2) );

PRINT( (*(p+2) + 1) );

PRINT( (*(p+2) + 2) );

PRINT( (*(p+2) + 3) );

PRINT( (*(p+2) + 4) );

PRINT( **p );

PRINT( *(*p + 1) );

PRINT( *(*p + 2) );

PRINT( **(p+1) );

PRINT( *(*(p+1) + 1) );

PRINT( *(*(p+1) + 2) );

PRINT( *(*(p+1) + 3) );

PRINT( **(p+2) );

PRINT( *(*(p+2) + 1) );

PRINT( *(*(p+2) + 2) );

PRINT( *(*(p+2) + 3) );

PRINT( *(*(p+2) + 4) );

return 0;

}

wordy.cpp

/* WORDY.CPP: Demonstrate use of fixed length arrays of

strings */

#include <iostream.h>

#include <string.h>

main()

{

char wordz[4][20];/* array of 4 strings, each one

up to 19 characters in length */

int i;

strcpy(wordz[0],"Greetings");/* copy a string into

first element */

strcpy(wordz[1],"Hi there");/* copy a string into

second element */

strcpy(wordz[2],"Hello");/* this string will be

destroyed */

wordz[3][0] = 'H';/* build this string */

wordz[3][1] = 'I';/* a character at a time */

wordz[3][2] = '\0';/* terminate with a null */

i = 0;

strcpy (wordz[2], wordz[1]);

for (i=0; i < 4; i++)

cout < wordz[i] < endl;/* print out the

strings */

return 0;

}

LAB 2 for Part 13

1.Determine the output of the program ptr07.cpp without actually running it. Run program clue.cpp, then use the output from clue.cpp as an aid to organizing your conclusions about the behavior of ptr07.cpp.

2.Write a C++ function to copy one string to another. Use pointers to access the string array. (Remember that all legal strings are terminated by the null character which is guaranteed to be equivalent to an integer zero, which in turn is treated as false by any logical comparison.) Test the function.

3.Rewrite or add to the program size.cpp to determine the size of an element in a two-dimensional array, and the size of the entire two-dimensional array. Next define a pointer variable and set it to point to the array. What result does the sizeof function produce with this pointer variable?

4.Write a function that determines the largest element in a two-dimensional array. How general can this function be written (in terms of the size of the array)?

ptr08.cpp

/* PRT08.CPP: The function "main" can take arguments.

Compile and run this program with arguments on the

command line. */

#include <iostream.h>

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

{

int i;

cout < "The number of arguments on the command line"

< " = " < (argc-1) < endl;

cout < "\nThe arguments are:\n";

for (i=0; i < argc; i++)

cout < argv[i] < endl;

return 0;

}

USING ARGUMENTS IN VMS

To be able to use command line arguments in C++, you must first "install the program as a foreign command." This is done in the following way:

$ cxx ptr08

$ link ptr08

$ tryme :== $sys$login:ptr08

$ tryme 8 "Hi"

ptr08a.cpp

/* PTR08A.CPP: The function "main" can take arguments.

Compile and run this program with arguments on the

command line. */

#include <iostream.h>

int main(int argc, char **argv)

{

cout < "The total number of strings (arguments) on "

< "the command line = " < argc < endl;

cout < "\nThe arguments are:\n";

while ( argc-- > 0 )

cout < *(argv++) < endl;

return 0;

}

puzzle.cpp

/* PUZZLE.CPP: What is the output of the following

program? */

#include <iostream.h>

#define P(x) cout < x < endl

main()

{

static char *msg1[] = {"HELLO", "BYE"};

static char *msg2[] = {"YES", "NO", "MAYBE"};

char **index[2];

index[0] = msg1;

index[1] = msg2;

P( **index );

P( **(index+1) );

P( (**index + 1) );

P( *(*index + 1) );

P( *(*(index+1) + 1) );

P( *(*(index+1) + 2) );

return 0;

}

fcnptr.cpp

/* FCNPTR.CPP: Demonstrates the use of pointers to

functions */

#include <iostream.h>

#include <math.h>

main()

{

double a = 6.0, b;

double (*f)(double);

b = sqrt(a);

cout < "Direct use of sqrt gives " < b < endl;

f = sqrt; /* just the name is the pointer */

b = (*f)(a);

cout < "Access via pointer gives " < b < endl;

a++;

b = (*f)(a);

cout < "For the non-believers " < b < endl;

return 0;

}

POINTERS TO FUNCTIONS

Pointers to functions can be defined in the C++ language.

The declaration

int (*f)();

defines f to be a pointer to a function that returns an integer. The parentheses around *f are required, for the definition

int *f();

defines f to be a function which returns a pointer to an integer .

The pointer f can be assigned a specific function merely by writing

f = g;

where g is a function name that has been previously defined or declared.

To call the function pointed to by f we write

(*f)(arg1,...,argn);

The returned result of the above call (if any) can be used in the usual way. For example, the statement

z = (*f) (arg1,...,argn);

assigns to the variable z the result of the function call

integral.cpp

/* INTEGRAL.CPP: Program illustrates the use of pointers

to functions to do numerical integration. */

#include <iostream.h>

double f1 (double), f2 (double);

double ingr(double, double, double(*)(double), int);

double f1(double x)/* defines f1(x) = 1.0/x */

{

return(1.0/x);

}

double f2(double x)/* defines f2(x) = x */

{

return(x);

}

/* Simpson's Rule for numerical integration. */

double ingr(double a, double b, double (*f)(double), int m)

/* double a, b; limits of integration */

/* double (*f)(); function to be integrated */

/* int m;region subdivided into 0,1, ... ,2m */

{

doubleh,/* width of interval */

sum1 = 0.0, sum2 = 0.0, sum3;

inti;

h = (b-a)/(2.0*m);

for( i=1; i <= 2*m - 1; i += 2)

sum1 = sum1 + (*f)(a + i*h);

for( i=2; i <= 2*m - 2; i += 2)

sum2 = sum2 + (*f)(a + i*h);

sum3 = (*f)(a) + (*f)(b);

return( (h/3.0)*(4.0*sum1 + 2.0*sum2 + 1.0*sum3) );

}

main()

{

cout < ingr(1.0, 2.0, f1, 50) < endl;

cout < ingr(0.0, 2.0, f2, 50) < endl;

return 0;

}

sort.cpp

/* SORT.CPP: Demonstration of a sort function taking a

comparison function as one of its arguments. The qsort

library function is an implementation of the quicker-sort

algorithm. The first argument is a pointer to the base of

the data; the second is the number of elements; the third

is the width of an element in bytes; the last is the name

of the comparison routine to be called with two arguments

which are pointers to the elements being compared. The

routine must return an integer less than, equal to, or

greater than 0 according as the 1st argument is to be

considered less than, equal to, or greater than the 2nd.*/

#include <iostream.h>

#include <stdlib.h>

int cmp (const void *, const void *);

main()

{

int i;

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

for( i=0; i<8; ++i)

cout < " " < x[i];

cout < endl;

qsort((void *)x, 8, sizeof(x[0]), cmp);

for( i=0; i<8; ++i)

cout < " " < x[i];

cout < endl;

return 0;

}

int cmp(const void *x, const void *y) /* return -1 if x,y

are in correct order, return 1 if x,y are

out of order, return 0 if x,y are equal */

{

if( *(const int *)x < *(const int *)y )

return(-1);

if( *(const int *)x == *(const int *)y )

return(0);

return(1);

}

Summary of Pointers

To pass a pointer to a variable to a function and have that function alter the value of the variable:

main()

{

void routine (int *);

int a;

routine (&a);

...

}

void routine (int *pa)

{

*pa = ...

...

}

To pass a pointer to an array to a function and have that function alter the values contained in the array: