Pointers in The C Programming L - Ninnat Aupala-1

You might also like

Download as pdf or txt
Download as pdf or txt
You are on page 1of 110

Pointers in The C

Programming Language

* & ->

Ninnat Aupala
Pointers in The C
Programming Language
By

Ninnat Aupala

copyright © 2015 Ninnat Aupala


All Rights Reserved
To my family for their love and support
TABLE OF CONTENS

Chapter 1. An introduction to pointers 1

1.1 Introduction 1

1.2 Declaring pointer variables 1

1.3 Rules for using keywords with pointers 4

1.4 How to obtain the address of a variable 4

1.5 How to obtain the content of a variable by using a pointer 5

1.6 lvalue and rvalue 6

1.7 Displaying the address of a variable 7

1.8 Passing pointers as arguments to functions 8

1.9 Returning a pointer from a function 9

1.10 Pointers to pointers 12

1.11 Arrays of pointers 13

1.12 Explicit Type Conversion 15

1.13 Generic pointers 16

1.14 Finding the size of pointers 17


Chapter 2. Dynamic memory allocation 20

2.1 Introduction 20

2.2 Using malloc( ) 20

2.3 Using calloc( ) 21

2.4 Using realloc( ) 21

2.5 Using free( ) 22

2.6 Memory Leaks 23

Chapter 3. Pointers and Arrays 24

3.1 Introduction 24

3.2 Pointers and one-dimensional arrays 25

3.2.1 Passing one-dimensional arrays as arguments to functions 26

3.2.2 Returning a one-dimensional array from a function 27

3.3 Pointers and two-dimensional arrays 28

3.3.1 Passing two-dimensional arrays as arguments to functions 30

3.3.2 Returning a two-dimensional array from a function 32

3.4 Pointers and three-dimensional arrays 34

3.4.1 Passing three-dimensional arrays as arguments to functions 37

3.4.2 Returning a three-dimensional array from a function 39

3.5 Address arithmetic 41


3.5.1 One-dimensional array 41

3.5.2 Two-dimensional array 45

3.5.3 Three-dimensional array 59

Chapter 4. Pointers and Strings 74

4.1 Introduction 74

4.2 Pointers to strings 78

4.3 Pointers and the const qualifier 79

4.4 Passing strings as arguments to functions 83

4.5 Returning a string from a function 84

4.6 An array of pointers to strings 86

Chapter 5. Pointers and Structures 87

5.1 Introduction 87

5.2 Creating structure pointers 87

5.3 Accessing structure members 88

5.4 Passing structure pointers as arguments to functions 89

5.5 Returning a structure pointer from a function 90

5.6 An array of structure pointers 91


Chapter 6. Pointers and Functions 92

6.1 Introduction 92

6.2 Creating and using function pointers 92

6.3 Passing function pointers as arguments to functions 94

6.4 Returning a function pointer from a function 95

6.5 An array of function pointers 96

6.6 Complex declaration 96


1

Chapter 1
An introduction to pointers

1.1 Introduction
When a variable is defined in a C program, the compiler will find and allocate unused
locations in memory to that variable to use. Since the area of memory consist of a lot
of cells, each cell is identified by a unique number called its memory address (or, for
short, that address), to distinguish it from the other cells in memory. Every memory
cell has one byte in size. So to access the desired memory location, the address of
that location is needed.

Like any other data such as 47 (an integer value), 32.91 (a floating-point value), 'D'
(a character value) or etc., an address can be held in a variable called a pointer
variable. A pointer variable, often called shortly that a pointer, is a variable that can
store the memory address of a variable or function defined in a program. With a
pointer, we can refer to every variable or function created in a program.

1.2 Declaring pointer variables


Like any other variables, before using any pointers we must declare them. The
general form of declaration is

data_type * variable_name

The declaration starts with a data type and follows by the * operator and then
a variable’s name. Some white spaces can also be used in the declarations. The
following declarations are all equivalent:

int *p;

int* p;

int * p;

int * p;

int *p;
2

int* p;

or no white spaces

int*p;

Note that the form of pointer declaration is the same as the form of ordinary variable
declaration, just add the * operator before a variable’s name.

char *p1 /* p1 is a pointer to a char or a char pointer */

int *p2 /* p2 is a pointer to an int or an int pointer */

float *p3 /* p3 is a pointer to a float or a float pointer */

double *p4 /* p4 is a pointer to a double or a double pointer */

And we can also use some keywords, shown in the table below, with pointers in the
declaration.

Data Type
Keyword
char int float Double

signed Y Y

unsigned Y Y

short Y

long Y Y Y

long long Y
3

auto Y Y Y Y

register Y Y Y Y

static Y Y Y Y

extern Y Y Y Y

const Y Y Y Y

Table 1-1

The letters 'Y' in the above table indicate what keywords can be used to which
data types. From the table 1-1, some keywords can be grouped together as shown in
the table 1-2 below.

Group Keyword

sign signed, unsigned

size short, long, long long

storage class auto, register, static, extern

Table 1-2
4

1.3 Rules for using keywords with pointers


• Don’t use two or more keywords of the same group with the same variable.

The following declarations are invalid:


short long int *a;

static extern float *b;

unsigned long long short int *c;

• The order of keywords don’t affect with the declaration.

The following declarations are all equivalent:

const unsigned long int *p;

unsigned int const long *p;

int unsigned long const *p;

long int unsigned const *p;

long const int unsigned *p;

1.4 How to obtain the address of a variable


The address of a variable can be obtained by placing the & operator in front of a
variable’s name. The name of the & operator is ampersand. When this symbol is used
as a unary operator with a pointer variable in an expression, we can call it that the
address-of operator.

int n;

int *p;

p = &n;

From the statements above, the expression p = &n assigns the address of the variable
n to the pointer p, and p is said to “point to” n. The address-of operator is only applied
5

to variables or array elements. It cannot be applied to expressions, constants, or


register variables. Initializing a pointer can be also done at the same time of its
declaration as shown below.

int n;

int *p = &n;

char y, *z = &y;

Note that the data type of pointers and that of variables that they are pointing to must
be corresponding. From above, the int pointer p is pointing to the int variable n and
the char pointer z is pointing to the char variable y.

1.5 How to obtain the content of a variable by using a pointer


To obtain the content of the variable which is pointed to by a pointer, we apply the *
operator. The name of the * operator is asterisk. When this symbol is used as a unary
operator with a pointer variable in an expression (not in a declaration expression), we
can call it that the indirection or dereferencing operator.

int x = 1, y;

int *p = &x;

y = *p;

To access the content of the variable which the pointer p points to, the * operator is
applied as shown in the last statement. Accessing what a pointer points to in this
way is called dereferencing the pointer because the pointer is considered to be
referencing the variable.

Since p points to the variable x, then *p can be used in any context where x could,
so the above expression y = *p is equivalent to an expression y = x. As a variable
itself, a pointer can be used without the * or & operator. For example, if z is another
pointer to an int, the expression z = p copies the contents of p into the pointer z, thus
making z point to whatever p pointed to.
6

1.6 lvalue and rvalue

An lvalue (left value) is an expression that can be on the left side of an assignment
operator and a data value can be assigned to it.

An rvalue (right value) is an expression that can be on the right side of an assignment
operator and a data value cannot be assigned to it.

The following table summarizes the use of the & and * operator.

used with a pointer in

Operator expression as
declaration

lvalue rvalue

* Y Y Y

& Y

(no operator) Y Y

Table 1-3

Consider the following code fragment:

int x = 1, y, *p;

p = &y;

*p = 2;

x = *p;
7

In the expression *p = 2, *p serves as an lvalue, so the value 2 is put into the variable
that is being pointed to by the pointer p, the variable y. This expression is the same
as y = 2. For the next expression, x = *p, *p serve as an rvalue, so it gives the
contents of the variable that it is pointing to. Then, the expression x = *p puts the
contents of y, the value 2, into x. This expression is the same as x = y.

1.7 Displaying the address of a variable


To display the memory address of any variables, we can use the printf( ) function.

/* Program 1-1 */

#include <stdio.h>

void main(void)
{
int x = 1;

printf("The address of x = %d \n\n", &x);

printf("x occupies %d bytes \n\n", sizeof(x));

printf("The value of x = %d", *&x);


}

The output is:

The address of x = 100

x occupies 4 bytes

The value of x = 1

When you run this program on your computer, you will have the different result in
that all addresses in this book are arbitrary to help us to have simple numbers that we
can refer to. From the program 1-1, we will represent the memory location of the
variable x by the diagram as shown below:
8

x variable name

1 data value

100 memory address


Figure 1-1

Note in the last printf( ) function, the expression *&x gives the content of x
because when the * and & operator are written consecutively, they cancel each
other. So *&x is the same as x.

/* Program 1-2 */

#include <stdio.h>

void main(void)
{
int *p, x = 3;

p = &x;

printf("The address of p = %d\n\n", &p);

printf("The address of x = %d\n\n", p);

printf("The value of x = %d", *p);


}

The output is:

The address of p = 100

The address of x = 200

The value of x = 1

In the program 1-2, we have declared a pointer named p, and then we have assigned
the address of x to it. The memory diagram of two variables in this program are shown
below.
9

p x

100 1

200 100
Figure 1-2

The arrow from the pointer p to the variable x shows that p is pointing to x, thus the
value stored by p is the address of x. And note that although x contain several bytes
of memory, p will store only the address of the first byte of x.

p
100
200 100 101 102 103

x
1

100

Figure 1-3

1.8 Passing pointers as arguments to functions


If we want to have one function can alter variables in another function, we have to
pass their addresses instead of their values. Look at the following program:

/* Program 1-3 */

#include <stdio.h>

void swap(int *, int *);

void main(void)
{
int x = 1, y = 2;

swap(&x, &y);
10

printf("x = %d, y = %d", x, y);


}

void swap(int *x, int *y)


{
int temp;

temp = *x;

*x = *y;

*y = temp;
}

The output is:

x = 2, y = 1

In the calling function, main( ) function, the addresses of x and y are passed as
argument to the called function, swap( ) function. Note that the parameter x and y in
the swap function must be pointers to receive the addresses that are passed from the
main function. Because having the addresses of the variable in function main, the
swap function’s parameters can access and change variable in the function that called
it. Although the name of arguments and parameters are the same but they are not the
same variables since they are in the different functions. And we may or may not
declare the name of parameters in a function prototype whether or not. And in
function prototype, parameters’ name can be omitted but not in function definition.

1.9 Returning a pointer from a function


We can not only pass addresses to functions, but also return addresses from functions.

/* Program 1-4 */

#include <stdio.h>

int *func(void);

void main(void)
{
int *x;
11

x = func();

printf("\n----------\n");
printf("In main");
printf("\n----------\n");

printf("&x = %d\n", &x);


printf(" x = %d\n", x);
printf("*x = %d", *x);
}

int *func(void)
{
static int y = 1;

printf("----------\n");
printf("In func");
printf("\n----------\n");

printf("&y = %d\n", &y);


printf(" y = %d\n", y);

return &y;
}

The output is:

----------
In func
----------
&y = 7000
y = 1

----------
In main
----------
&x = 1500
x = 7000
*x = 1

Note that in front of the function’s name must be preceded by the * operator in both
function prototype and function definition.
12

1.10 Pointers to pointers


Since pointers are variables themselves, then they have their own memory addresses.
To hold the address of pointers, C has pointers to pointers.

char **p1; /* p1 is a pointer to a pointer to a char


or a pointer to a char pointer */

int **p2; /* p2 is a pointer to a pointer to an int


or a pointer to an int pointer */

float **p3; /* p3 is a pointer to a pointer to a float


or a pointer to a float pointer */

double **p4; /* p4 is a pointer to a pointer to a double


or a pointer to a double pointer */

All statements from above are the declarations of a pointer to a pointer. Only one
thing added is another * operator.

/* Program 1-5 */

#include <stdio.h>

void main(void)
{
int **pp, *p, n;

n = 1;

p = &n;

pp = &p;

printf(" &pp : : : %d \n", &pp);


printf(" pp : &p : : %d \n", pp);
printf(" *pp : p : &n : %d \n", *pp);
printf("**pp : *p : n : %d \n", **pp);
}
13

The output is:

&pp : : : 1000 /* the address of pp */


pp : &p : : 2000 /* the address of p */
*pp : p : &n : 3000 /* the address of n */
**pp : *p : n : 1 /* the content of n */

Suppose that the address of a pointer to a pointer pp is 1000, a pointer p is 2000 and
a variable n is 3000.

pp p n

2000 3000 1

1000 2000 3000


Figure 1-4

Remember that use a pointer to a pointer when you want to hold the address of a
pointer.

1.11 Arrays of pointers


If we want several pointers that have the same data type, we can create an array of
pointers.

char *q[3]; /* q is an array of pointers to char


or an array of char pointers
or a char pointer array */

int *w[15]; /* w is an array of pointers to ints


or an array of int pointers
or an int pointer array */

float *e[37]; /* e is an array of pointers to floats


or an array of float pointers
or a float pointer array */
14

double *r[100]; /* r is an array of pointers to doubles


or an array of double pointers
or a double pointer array */

The brackets after each variable’s name identify it as an array and the number within
the square brackets indicates the number of elements in the array. Let’s take a look
the way for using array of pointers in the example below.

/* Program 1-7 */

#include <stdio.h>

void main(void)
{
int q = 1, v = 2, u = 3;

int *x[3];

x[0] = &q;
x[1] = &v;
x[2] = &u;

for (int i = 0; i<3; i++)


{
printf("&x[%d] = %d\t", i, &x[i] );
printf(" x[%d] = %d\t", i, x[i] );
printf("*x[%d] = %d\n", i, *x[i] );
}
}

Suppose that the address of x, q, v and u is 100, 500, 600 and 700 respectively.

The output is:

&x[0] = 100 x[0] = 500 *x[0] = 1


&x[1] = 104 x[1] = 600 *x[1] = 2
&x[2] = 108 x[2] = 700 *x[2] = 3

Note that the form of using an array of pointers is similar to ordinary pointers. One
thing added is a subscript enclosed by the brackets.
15

x[0] 100: 500 1


u
500
x[1] 104: 600 2
v
600
x[2] 108: 700 3
700
Figure 1-5

1.12 Explicit Type Conversion


The data type of pointers can be converted from one data type to another data type
like any other ordinary variables. We use a cast operator to convert the data type of
pointers. The general form of a cast operator is

(data_type *)pointer_name

This operator is a unary operator and associate from right to left. It has the same
precedence as other unary operators. In C, type conversion is called casting. Consider
the following code fragment:

int i, *pi;

char *pc;

pc = (char *)&i;

pi = (int *)pc;

int **ppi = (int **)pi;

pc = (char *)ppi;

Note that the way to convert the data type of pointers is similar to ordinary variables
just only one thing that is added is an asterisk.
16

1.13 Generic pointers


A generic pointer (also called a pointer to void or a void pointer) is a pointer which
can store the address of any data types of a variable. The general form for declaring
a generic pointer is

void * pointer_name

Let’s learn how to declare and use a generic pointer in the program 1-8.

/* Program 1-8 */

#include <stdio.h>

void main(void)
{

void *v;

char c = 'Z';

v = &c;

char *p = v;

printf("The address of \n");


printf("===============\n");
printf("&v: %d\n", &v);
printf("&c: %d\n", &c);
printf("&p: %d\n", &p);
printf("---------------\n\n");
printf("The content of \n");
printf("===============\n");
printf("v = %d\n", v);
printf("c = %c\n", c);
printf("p = %d\n", p);
printf("---------------\n\n");

printf("&c : v : p = %d\n\n", &c);

printf(" c : *(char *)v : *p = %c", c);


}
17

The output is:

The address of
===============
&v: 1000
&c: 2000
&p: 3000
---------------

The content of
===============
v = 2000
c = z
p = 2000
---------------

&c : v : p = 2000

c : *(char *)v : *p = Z

From the program 1-8, there are several interesting points about generic pointer.

 We can assign the address of any data types to a generic pointer without an
explicit casting.

 We cannot dereference a generic pointer directly but we can avoid by casting it


before dereferencing as shown in the expression *(char *)v.

 We can assign the content of a generic pointer to any type of pointer without
casting.

1.14 Finding the size of pointers


To find out the size of pointers, we use sizeof operator. This operator can be applied
to the name of variables, constants and data types. This operator gives the size of
things in bytes. When its operand is a data type such as int, char, float *, etc., it must
be enclosed in parentheses. But if its operand is a variable or a constant, we can put
it in the parentheses whether or not.
18

/* Program 1-9 */

#include <stdio.h>

void main(void)
{
char **a;
int **b;
float **c;
double **d;

printf("The size of");


printf("\n==============\n");

printf("char * : %d\n", sizeof(char *));


printf("int * : %d\n", sizeof(int *));
printf("float * : %d\n", sizeof(float *));
printf("double * : %d\n\n", sizeof(double *));

printf("char ** = %d\n", sizeof(a));


printf("int ** = %d\n", sizeof(b));
printf("float ** = %d\n", sizeof c);
printf("double ** = %d\n\n", sizeof d);

printf("void * : %d", sizeof(void *));


}

The output is:

The size of
==============
char * : 4
int * : 4
float * : 4
double * : 4

char ** = 4
int ** = 4
float ** = 4
double ** = 4

void * : 4
19

Note that all pointers are the same size, regardless of what they point to. You may
receive the different output when you run this program since the size of pointers
depend on the machine that you use. In this book, we use 32-bit machine, so all types
of pointers have 4 byte-size.
20

Chapter 2
Dynamic memory allocation

2.1 Introduction

Since once an array has been created, its size is fixed and cannot be changed
during execution of a program. But there are some situations that we are unable to
know in advance that how large an array should be such as when we have to
create an array to receive a message which a user will enter through a keyboard. So
we have to guess the size of an array that we think that large enough to hold data
from a user. If the array's size is more than we need we waste space, but if it is
less than we need we can’t hold total data.

To solve a fix-sized limitation of arrays, C provides dynamic memory allocation


functions which is malloc( ), alloc( ), realloc( ) and free( ) that can be applied to
manipulate area of memory in runtime. These functions are defined in the <stdlib.h>
header file.

2.2 Using malloc( )

We can allocate memory dynamically by using function malloc( ). The function


prototype of this function is

void *malloc(size_t s)

The malloc( ) function takes one parameter, which is the size of the requested area
of memory in bytes. It returns the address of the first byte of allocated memory area
if the allocation is successful. But if the memory allocation fails or the parameter
value is 0, it returns NULL. The allocated memory is contiguous and not cleared.

Note that the data type of a return value is a generic pointer since any kind of pointer
can be returned from the function which make it more flexible. And also note that
the data type of the parameter in this function is size_t. Remember that size_t is a
synonym for an unsigned integer type. The following code fragments shows the use
of this function:
21

long *p;

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

In fact, we can specify the number of bytes that we desire directly but since the size
of the data types vary from system to system, so we use the sizeof operator to get the
correct size and make the code more portable. For the generic pointer that malloc( )
returns, we actually will convert it whether or not but in this case we cast it for clarity.

2.3 Using calloc( )

We can also allocate memory dynamically by using function calloc( ). The function
prototype of this function is

void *calloc(size_t n, size_t s)

The calloc( ) function takes two parameters, which is the number of items and the
size of each item in bytes. It returns the address of the first byte of allocated memory
area if the memory allocation is successful. But if the allocation fails or either
parameters or both parameters is 0, it returns NULL. The allocated memory is
contiguous and cleared (set all bytes to zero.)

The following code fragments shows the use of this function:

int *p = (int *)calloc(5, sizeof(int));

The size of allocated memory results in the product of two function parameters
which is 5 x sizeof(int).

2.4 Using realloc( )

To change the size of memory location that was previously allocated by malloc( )or
calloc( ) we use realloc( ) function. The function prototype of this function is

void *realloc(void *p, size_t s)


22

The realloc( ) function takes two parameters which is the address of original memory
area and the new size that is required. The result that we can obtain from this function
is shown in the table below.

If the first parameter is NULL, the function works identically to malloc( ).

If the second parameter is 0, the allocated memory area is freed and the function
returns NULL.

If the new size is smaller, the function returns the original address.

If the new size is larger and the function can extend the original memory area, it
returns the original address.

If the new size is larger and the function cannot extend the original area of memory,
it will allocate the new one and copy the original content to it. The old memory area
is freed and the address of the new one is returned. But if the function cannot
allocate new memory area, it returns NULL and the original one is unchanged.

Table 2-1

The following code fragments shows the use of this function:

short *p = (short *)calloc(5, sizeof(short));

p = (short *)realloc(p, 20 * sizeof(short));

2.5 Using free( )

The area of memory which we obtain from malloc( ), calloc( ) and realloc( ) come
from the heap segment and it is not disappeared when a function that it is resident
terminate. Thus, it is our responsibility to deallocate (or release or free) this memory
23

area to the system when we don’t need it by calling the free( ) function. The function
prototype of free( ) function is

void free(void *p)

The free( ) function deallocates memory location which is allocated by calloc( ),


malloc( ) or realloc( ). It does nothing if its parameter is NULL.

The following code fragments shows the use of this function:

char *p = malloc(70);

free(p);

p = NULL;

Remember that the allocated memory should not be accessed again after it has been
released but there is possibility that this situation will happen incidentally since its
address is still left in the pointer p. So we have to assign NULL to p in the code
above.

2.6 Memory Leaks

When dynamically allocated memory is no longer needed, it should be freed so that


it can be reused again later. If the allocated memory is not freed, it causes a memory
leak. Sometimes, a memory leak occur if we’ve lost the address of allocated memory.
For example,

float f;

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

p = &f;

From the code above, note that the dynamically allocated memory's address has been
lost since we’ve reassigned the new address to the pointer p before we will free the
old one.
24

Chapter 3
Pointers and Arrays

3.1 Introduction

In C, pointer and array are closely related to each other because they can be used
interchangeably in almost context but not all. Several operations that can be done by
using arrays can also be done with pointers. Consider the following program.

/* Program 3-1 */

#include <stdio.h>

void main(void)
{
int z[3] = { 1, 2, 3 };

printf("%d ", z[1] );


printf("%d ", *(z + 1) );
printf("%d ", *(1 + z) );
printf("%d", 1[z] );
}

The output is:

2 2 2 2

Firstly, consider the expression *(z+1) in the second printf( ) function. Actually, this
expression is equivalent to the expression z[1] in the first printf( ) function. Since
when a C program is compiled, the expression z[1] is translated into *(z+1) by a
compiler. That is, the name of an array is treated as the address of the first element
of the array (or base address) and its subscript as an offset. So we can use either of
these expressions to get the content of z[1]. Next, consider the expression *(1+z) in
the third printf( ) function and the expression 1[z] in the fourth printf( ) function.
Like the previous consideration, the expression *(1+z) is equivalent to the expression
1[z] because when the program is complied, 1[z] is translated into *(1+z). In addition,
from commutative property for addition, the order does not matter. Hence, *(1+z) is
equivalent to *(z+1). All in all the whole expressions in the program are the same.
So all printf( ) function print the same results, 2.
25

3.2 Pointers and one-dimensional arrays

In addition to using array notation to refer to an array element, we can also refer to
an array element by using a pointer. Before we can refer to a desired element of an
array by a pointer, we have to assign the address of that array to a pointer. Look at
the following code fragment:

int *p, z[5];

p = z; /* method 1 */

Another way to assign the address of an array to the pointer p is shown below.

p = &z[0]; /* method 2 */

Note that the address-of operator, &, is needed for the second method. The both ways
yield the same result. Now, the pointer p points to the array z (p contains the address
of 0th element). Then, any elements in the array z can be accessed through p. And
when a pointer contains the address of an array, it can refer to any array elements
with an array form too.

/* Program 3-2 */

#include <stdio.h>

void main(void)
{
int z[] = { 1, 2, 3, 4, 5 };

int *p = z;

printf("%d %d %d\n", *(p + 0), p[0], *p);

printf("%d %d", *(p + 1), p[1]);


}

The output is:

1 1 1
2 2
26

There are three interesting things in the program 3-2:

1) We don’t specify a subscript to the array z while initializing it since a compiler


can automatically count the number of elements in the list when finding empty
brackets

2) The square brackets are applied with the pointer p to refer to the desired array
element. Then, there is no need to use the * operator because the expression p[0]
and p[1] will be translated into *(p+0) and *(p+1) in turn.

3) When we see a [0] to the right of a pointer, we can replace it with a * to the left of
the pointer.

3.2.1 Passing one-dimensional arrays as arguments to functions

When we want to pass an array to function, we have two optional forms of function
parameter definition as shown below.

/* Program 3-3 */

#include <stdio.h>

void func1(int p[]); /* method 1 */

void func2(int *p); /* method 2 */


void main(void)
{
int z[] = { 1, 2, 3, 4, 5 };

func1(z);

func2(&z[0]);
}

void func1(int p[])


{
for(int i = 0; i<5; i++)
printf("%d ", p[i]);

printf("\n");
27

void func2(int *p)


{
for(int i = 0; i<5; i++)
printf("%d ", *(p + i));

printf("\n");
}

The output is:

1 2 3 4 5
1 2 3 4 5

Both forms are the same. However, the first form may have a bit more advantage
because it will remind us that what is passed is the address of an array.

3.2.2 Returning a one-dimensional array from a function

When we want to return an array from a function, we have just one way that is
returning its address. The form of function definition is shown below in a program
2-4.

/* Program 3-4 */

#include <stdio.h>

void *func(void)
{
static int a[5] = { 0, 1, 2, 3, 4 };

return a;
}

void main(void)
{
int *p;

p = func();
28

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


printf("%d ", p[i]);
}

The output is:

0 1 2 3 4

3.3 Pointers and two-dimensional arrays

A two-dimensional array (2D array) is an array of arrays. That is, each element of an
array is another array. Consider the following array definition:

int z[3][3] =
{
{ 1, 2, 3 },
{ 4, 5, 6 },
{ 7, 8, 9 }
};

This is an array of 3 arrays of 3 ints. That is, z is an array with three elements. Each
of the three elements of z is an array of 3 ints.
an array
z

an array of 3 arrays
z[0] z[1] z[2]

an array of 3 arrays of 3 ints


z[0][0] z[0][1] z[0][2] z[1][0] z[1][1] z[1][2] z[2][0] z[2][1] z[2][2]

Figure 3-1
29

We can also visualize this array as a table consisting of three rows and three columns.
The first subscript, next to the array name, is a row number and the second one is a
column number. All arrays, no matter how many dimensions they have, are stored
contiguously in memory, as shown in a figure below.

0th 1st 2nd


z[0][0] z[0][1] z[0][2] z[1][0] z[1][1] z[1][2] z[2][0] z[2][1] z[2][2]

1 2 3 4 5 6 7 8 9

First row Second row Third row

1 2 3

4 5 6

7 8 9

0th 2nd
1st
First column Third column

Second column

Figure 3-2

To hold the address of this array, we have to create a pointer to a 2D array as shown
below.

int(*p)[3];

p = z;

or

int(*p)[3] = z;
30

The general form of declaration is

data_type ( * pointer_name )[index]

Keep in mind that the parentheses enclosing an asterisk and the pointer name are
important. If we don’t have the parentheses, p will become an array of pointers in
that the square brackets have higher precedence than the asterisk. The below table
lists the order of operator precedence.

+------------------------------+---------------+
| Operator | Associativity |
+------------------------------+---------------+
| | |
| ( ) [ ] . -> | left to right |
| | |
| * & ++ -- (data_type) sizeof | right to left |
| | |
| + - | left to right |
| | |
| = += -= | right to left |
| | |
+------------------------------+---------------+
| Note: |
| |
| - operators on the same line have the same |
| precedence |
| |
| - operators on the lower line have the lower |
| precedence |
| |
+----------------------------------------------+
Table 3-1

3.3.1 Passing two-dimensional arrays as arguments to function

When we want to pass two-dimensional arrays to functions by reference (pass their


addresses), we have two methods in declaring function parameters to receive the
address of arrays as shown in the program 3-5.
31

/* Program 3-5 */

#include <stdio.h>

void func1(int p[][3]); /* method 1 */

void func2(int(*p)[3]); /* method 2 */


void main(void)
{
int z[3][3] =
{
{ 1, 2, 3 },
{ 4, 5, 6 },
{ 7, 8, 9 }
};

func1(z);

func2(&z[0]);
}

void func1(int p[][3])


{
printf("In func1:\n");
printf("---------\n");

for(int i = 0; i<3; i++)


{
for(int j = 0; j<3; j++)
printf("%d ", p[i][j]);

printf("\n");
}

printf("\n");
}

void func2(int(*p)[3])
{
int(*p2)[3];

printf("In func2:\n");
printf("---------\n");
32

for(int i = 0; i<3; i++)


{
p2 = p + i;

for (int j = 0; j<3; j++)


printf("%d ", (*p2)[j]);

printf("\n");
}
}

The output is:

In func1:
---------
1 2 3
4 5 6
7 8 9

In func2:
---------
1 2 3
4 5 6
7 8 9

As we can see from the program 3-5, there are two forms to declare function
parameters to receive the address of 2D arrays. In the first method, the leftmost
subscript can be omitted.

3.3.2 Returning a two-dimensional array from a function

Consider the following program:

/* Program 3-6 */

#include <stdio.h>

int(*func(void))[3];

void main(void)
{
int(*p)[3] = func();
33

for(int i = 0; i<3; i++)


{
for(int j = 0; j<3; j++)
printf("%d ", p[i][j]);

printf("\n");
}
}

int(*func(void))[3]
{
static int a[3][3] =
{
{ 1, 2, 3 },
{ 4, 5, 6 },
{ 7, 8, 9 }
};

return a;
}

The output is:

1 2 3
4 5 6
7 8 9

The general form of function declaration is

data_type ( * function_name ( ) )[index]

Note that it is similar to the general form of declaring a pointer to 2D array just
replace a pointer name with a function name and add the parentheses at the end of
the function name.
34

3.4 Pointers and three-dimensional arrays

A Three-dimensional array is an array of arrays of arrays. We can visualize a


one-dimensional array as a row, a two-dimensional array as a table and a
three-dimensional array as a set of tables.

int z[3][2][2] =
{
{
{ 0, 2 },
{ 4, 6 }
},

{
{ 1, 3 },
{ 5, 7 }
},
{
{ 11, 22 },
{ 33, 44 }
}
};

This is an array of 3 arrays of 2 arrays of 2 ints. That is, z is an array with three
elements. Each of the three elements is an array of 2 arrays. Each of the two elements
is an array of 2 ints.
35

an array

an array of 3 arrays

z[0] z[1] z[2]

an array of 3 arrays of 2 arrays

z[0][0] z[0][1] z[1][0] z[1][1] z[2][0] z[2][1]

an array of 3 arrays of 2 arrays of 2 ints

z[0][0][0] z[0][1][0] z[1][0][0] z[1][1][0] z[2][0][0] z[2][1][0]

z[0][0][1] z[0][1][1] z[1][0][1] z[1][1][1] z[2][0][1] z[2][1][1]

Figure 3-3

We can also visualize this array as a nested table (or a stack of table). Each table
consists of two rows and two columns. The first subscript specifies a desired table.
The second and the third subscript specify a row and a column respectively.
36

0th 1st 2nd


0th 1st 0th 1st 0th 1st
0 2 4 6 1 3 5 7 11 22 33 44

First row Second row First row Second row

0 2 1 3 11 22
4 6 5 7 33 44
First table Second table Third table

11 22
1 333 44
0 52 7
4 6

Figure 3-4
37

3.4.1 Passing three-dimensional arrays as arguments to functions

Consider the following program:

/* Program 3-7 */

#include <stdio.h>

void func1(int p[][2][2]); /* method 1 */

void func2(int(*p)[2][2]); /* method 2 */


void main(void)
{
int z[3][2][2] =
{
{ { 0, 2 }, { 4, 6 } },
{ { 1, 3 }, { 5, 7 } },
{ { 11, 22 }, { 33, 44 } }
};

func1(&z[0]);

func2(z);
}

void func1(int p[][2][2])


{
printf("In func1:\n");
printf("---------\n");

for(int i = 0; i<3; i++)


{
for(int j = 0; j<2; j++)
{
for(int k = 0; k<2; k++)
printf("%d ", p[i][j][k]);

printf("\n");
}

printf("\n");
}
38

printf("\n");
}

void func2(int(*p)[2][2])
{
int(*p2)[2][2];

printf("In func2:\n");
printf("---------\n");

for (int i = 0; i<3; i++)


{
p2 = p + i;

for(int j = 0; j<2; j++)


{
for(int k = 0; k<2; k++)
printf("%d ", (*p2)[j][k]);

printf("\n");
}

printf("\n");
}
}

The output is:

In func1:
---------
0 2
4 6

1 3
5 7

11 22
33 44

In func2:
---------
0 2
4 6
39

1 3
5 7

11 22
33 44

Note that the forms of declaring function parameter to receive the address of 3D
array is similar to 2D array just one thing that is increased is another subscript.

3.4.2 Returning a three-dimensional array from a function

The form of function declaration is

data_type ( * function_name ( ) )[index][index]

Note that it is similar to the form of 2D array just add another square bracket.

/* Program 3-8 */

#include <stdio.h>

int(*func(void))[2][2];

void main(void)
{
int(*p)[2][2] = func();

for(int i = 0; i<3; i++)


{
for(int j = 0; j<2; j++)
{
for(int k = 0; k<2; k++)
printf("%d ", p[i][j][k]);

printf("\n");
}

printf("\n");
}
}
40

int(*func(void))[2][2]
{
static int a[][2][2] =
{
{ { 0, 2 }, { 4, 6 } },
{ { 1, 3 }, { 5, 7 } },
{ { 11, 22 }, { 33, 44 } }
};

return a;
}

The output is:

0 2
4 6

1 3
5 7

11 22
33 44
41

3.5 Address arithmetic


Some arithmetic operations can be performed on an address.

 Adding an integer to an address.


 Subtracting an integer from an address.
 Subtracting one address from another address.

As summarized in the table 2-1 below.

Operation Result

address + integer address


address – integer address
address – address integer

Table 3-2

3.5.1 One-dimensional array


The formula for calculating the address of 1D arrays is

address = base address + offset

and

content = *( base address + offset )

base address is the address of the first element of an array.

For example, to find out the address of z[i]:

address = z + i

and to find out the content of z[i]:

content = *( z + i )
42

Let us consider the following code fragment:

float z[5], *p;

p = &z[1];

Assume that the address of the array z is 100.

p = &z[1]

= &(z[1])

= &(*(z + 1))

= &*(z + 1)

= z + 1

= 100 + (1 * sizeof(float))

= 100 + (1 * 4)

= 104

The address that we get is 104 not 101 since z is an array of floats, each element of
which consists of 4 bytes in size. So the offset has to be multiplied by the size of
element before it will be added to the base address. The expression z+1 gives the
address of the next element of the array z which is the address of the second element.

The same thing happens to subtraction when we subtract an integer from an address.

short z[5];

short *p = z + 3;

p = p – 1;

Assume that the address of z is 100.

p = z + 3

= 100 + (3 * sizeof(short))
43

= 100 + ( 3 * 2 )

= 100 + 6

= 106

The expression z+3 gives the address of the next 3 element of the array z which is
the address of the fourth element. Since z now is an array of shorts, each element of
which consists of 2 bytes in size.

p = p – 1

= 106 - (1 * sizeof(short))

= 106 - ( 1 * 2 )

= 104

Note that the offset has to be multiplied by the size of element before it will be
subtracted from the address as addition. Now, the pointer p points to the second
element of the array z.
For subtracting two addresses from each other, the result of subtraction will be an
offset value which is an integer. Once again, assume that the address of z is the same
as before.

int z[5], *x, *y, n;

x = z + 4;

y = &z[1];

n = x - y;

Let’s find out the value of the variable n.

n = x – y

= ( z + 4 ) - ( z + 1 )

= ( z + (4 * sizeof(int)) ) - (z + (1 * sizeof(int)) )

= ( 100 + ( 4 * 4 ) ) - ( 100 + ( 1 * 4 ) )
44

= ( 100 + 16 ) - ( 100 + 4 )

= 116 - 104

= 12

The value 12 is the number of bytes but we want an offset value, so we have to divide
this value with the data type.

n = 12 / sizeof(int)

= 12 / 4

= 3

An easier way is:

n = x - y

= ( z + 4 ) - ( z + 1 )

= z + 4 - z – 1

= 3

Because x and y is pointing to the same array, x is essentially the same as z + 4 and
y is the same as z + 1.
45

3.5.2 Two-dimensional array

The formula for calculating the address of 2D arrays is:

address = *( base address + offset2 ) + offset1

and

content = *(*(base address+offset2)+offset1)

+--------------------------+
| offset2 = row * COLSIZE |
| offset1 = column |
+--------------------------+
| row : row number |
| column : column number |
| COLSIZE : column size |
+--------------------------+

For example, to find out the address of a[i]:

address = *(base address + offset2)

= *( a + ( i * COLSIZE ) )

and to find out the address of a[i][j]:

address = *( base address + offset2 ) + offset1

= *( a + ( i * COLSIZE ) ) + j

and to find out the content of a[i][j]:

content = *( *( base address + offset2 ) + offset1 )

= *( *( a + ( i * COLSIZE ) ) + j )
46

Consider the following program:

/* Program 3-9 */

#include <stdio.h>

void main(void)
{

int z[3][3] =
{
{ 1, 2, 3 },
{ 4, 5, 6 },
{ 7, 8, 9 }
};

int(*p)[3] = z;

for(int i = 0; i<3; i++)


{
for(int j = 0; j<3; j++)
printf("&z[%d][%d]:%d ", i, j, &z[i][j]);

printf("\n");
}

printf("\n");
printf("p = %d\n", p);
printf("p+1 = %d\n", p + 1);
printf("p+2 = %d\n\n", p + 2);

printf("\n");
printf("p[0] = %d p[0][0] = %d\n", p[0], p[0][0]);
printf("p[1] = %d p[1][0] = %d\n", p[1], p[1][0]);
printf("p[2] = %d p[2][0] = %d\n", p[2], p[2][0]);

printf("\n");
printf("p[0]+1 = %d p[0][1] = %d\n", p[0] + 1, p[0][1]);
printf("p[1]+1 = %d p[1][1] = %d\n", p[1] + 1, p[1][1]);
printf("p[2]+1 = %d p[2][1] = %d\n", p[2] + 1, p[2][1]);
printf("\n");
printf("p[0]+2 = %d p[0][2] = %d\n", p[0] + 2, p[0][2]);
printf("p[1]+2 = %d p[1][2] = %d\n", p[1] + 2, p[1][2]);
printf("p[2]+2 = %d p[2][2] = %d\n", p[2] + 2, p[2][2]);
}
47

The output is:

&z[0][0]:100 &z[0][1]:104 &z[0][2]:108


&z[1][0]:112 &z[1][1]:116 &z[1][2]:120
&z[2][0]:124 &z[2][1]:128 &z[2][2]:132

p = 100
p+1 = 112
p+2 = 124

p[0] = 100 p[0][0] = 1


p[1] = 112 p[1][0] = 4
p[2] = 124 p[2][0] = 7

p[0]+1 = 104 p[0][1] = 2


p[1]+1 = 116 p[1][1] = 5
p[2]+1 = 128 p[2][1] = 8
p[0]+2 = 108 p[0][2] = 3
p[1]+2 = 120 p[1][2] = 6
p[2]+2 = 132 p[2][2] = 9

The detail for calculating the address of each element of the array z is explained
below.

&z[0][0]:100 &z[0][1]:104 &z[0][2]:108


&z[1][0]:112 &z[1][1]:116 &z[1][2]:120
&z[2][0]:124 &z[2][1]:128 &z[2][2]:132
48

z z+1 z+2
100 112 124

z[0] z[0]+1 z[0]+2 z[1] z[1]+1 z[1]+2 z[2] z[2]+1 z[2]+2

100 104 108 112 116 120 124 128 132

z[0][0] z[0][1] z[0][2] z[1][0] z[1][1] z[1][2] z[2][0] z[2][1] z[2][2]

1 2 3 4 5 6 7 8 9
100 104 108 112 116 120 124 128 132

1 2 3

4 5 6

7 8 9

Figure 3-5
49

Let’s use what we’ve just learned to find the address of 2D array.

p = z = 100 /* the address of the first row of the table */

p
100

z z+1 z+2

100 112 124

100 104 108

z+1

z+2

Figure 3-6

Since p is a pointer to an array (an array of 3 ints), the thing that p is pointing to is
the entire subarray (or row) not single element in a row.

p + 1 = z + 1

= 100 + ( 1 * COLSIZE )

= 100 + ( 1 * ( 3 * sizeof(int)) )

= 100 + ( 1 * ( 3 * 4 ) )
50

= 100 + ( 1 * 12 )

= 100 + 12

= 112 /* the address of the second row of the table */

p+1
112

z z+1 z+2

100 112 124

112 116 120

z+1

z+2

Figure 3-7

When p is added by 1, it will point to the next row of the array (or table). So the
address that we obtain is 112 not 104 because in each row consists of 3 elements (or
column) and each element in column is an int.

p + 2 = z + 2

= 100 + ( 2 * COLSIZE )

= 100 + ( 2 * ( 3 * sizeof(int) ) )
51

= 100 + ( 2 * ( 3 * 4 ) )

= 100 + ( 2 * 12 )

= 100 + 24

= 124 /* the address of the third row of the table */

p+2
124

z z+1 z+2

100 112 124

124 128 132

z+1

z+2

Figure 3-8

p[0] = *(p+0) = *(100) = 100

/* the address of the first column in the first row of the table */
52

p[1] = *(p+1) = *(112) = 112

/* the address of the first column in the second row of the table */

p[2] = *(p+2) = *(124) = 124

/* the address of the first column in the third row of the table */

p p[1] p[2]

100 112 124

z[0] z[0]+1 z[0]+2 z[1] z[1]+1 z[1]+2 z[2] z[2]+1 z[2]+2

100 104 108 112 116 120 124 128 132

z[0] z[0]+1 z[0]+2

z[1] z[1]+1 z[1]+2

z[2] z[2]+1 z[2]+2

Figure 3-9

If we want to access to a single element in a subarray, we have to dereference p or


subscript it with only one set of the square brackets. Then, p will point to the first
element of subarray and now p decays into the pointer to an int. However, note that
the addresses that we obtain are the same as before.
53

p[0][0] = *(p[0]+0) = *(100) = 1

p[1][0] = *(P[1]+0) = *(112) = 4

p[2][0] = *(p[2]+0) = *(124) = 7

p[0][0] p[1][0] p[2][0]

1 4 7

1 2 3 4 5 6 7 8 9
100 104 108 112 116 120 124 128 132

1 2 3

4 5 6

7 8 9

Figure 3-10

p[0]+1 = 100 + 1

= 100 + ( 1 * sizeof(int) )

= 100 + ( 1 * 4 )

= 100 + 4

= 104

/* the address of the second column in the first row of the table*/
54

p[1]+1 = 112 + 1

= 112 + ( 1 * sizeof(int) )

= 112 + ( 1 * 4 )

= 112 + 4

= 116

/* the address of the second column in the second row of the table */

p[2]+1 = 124 + 1

= 124 + ( 1 * sizeof(int) )

= 124 + ( 1 * 4 )

= 124 + 4

= 128

/* the address of the second column in the third row of the table */
55

p+1 p[1]+1 p[2]+1

100 112 124

z[0] z[0]+1 z[0]+2 z[1] z[1]+1 z[1]+2 z[2] z[2]+1 z[2]+2

100 104 108 112 116 120 124 128 132

z[0] z[0]+1 z[0]+2

z[1] z[1]+1 z[1]+2

z[2] z[2]+1 z[2]+2

Figure 3-11

Once p is dereferenced or subscripted with only one square brackets, it will decay to
a pointer to an int. After that, if we add 1 to it, it will point to the next single element
in subarray.

p[0][1] = *(p[0]+1) = *(104) = 2

p[1][1] = *(P[1]+1) = *(116) = 5

p[2][1] = *(p[2]+1) = *(128) = 8


56

p[0][1] p[1][1] p[2][1]

1 4 7

1 2 3 4 5 6 7 8 9
100 104 108 112 116 120 124 128 132

1 2 3

4 5 6

7 8 9

Figure 3-12

p[0]+2 = 100 + 2

= 100 + ( 2 * sizeof(int) )

= 100 + ( 2 * 4 )

= 100 + 8

= 108

/* the address of the third column in the first row of the table */

p[1]+2 = 112 + 2

= 112 + ( 2 * sizeof(int) )

= 112 + ( 2 * 4 )

= 112 + 8
57

= 120

/* the address of the third column in the second row of the table */

p[2]+2 = 124 + 2

= 124 + ( 2 * sizeof(int) )

= 124 + ( 2 * 4 )

= 124 + 8

= 132

/* the address of the third column in the third row of the table */

p+2 p[1]+2 p[2]+2

100 112 124

z[0] z[0]+1 z[0]+2 z[1] z[1]+1 z[1]+2 z[2] z[2]+1 z[2]+2

100 104 108 112 116 120 124 128 132

z[0] z[0]+1 z[0]+2

z[1] z[1]+1 z[1]+2

z[2] z[2]+1 z[2]+2

Figure 3-13
58

p[0][2] = *(p[0]+2) = *(108) = 3

p[1][2] = *(P[1]+2) = *(120) = 6

p[2][2] = *(p[2]+2) = *(132) = 9

p[0][2] p[1][2] p[2][2]

1 4 7

1 2 3 4 5 6 7 8 9
100 104 108 112 116 120 124 128 132

1 2 3

4 5 6

7 8 9

Figure 3-14
59

3.5.3 Three-dimensional array


The formula for calculating the address of 3D arrays is
address = *( *( base address + offset3 ) + offset2 ) + offset1

and
content = *(*(*(base address + offset3) + offset2) + offset1)

+-------------------------------------+
| offset3 = table * ROWSIZE * COLSIZE |
| offset2 = row * COLSIZE |
| offset1 = column |
+-------------------------------------+
| table : table number |
| row : row number |
| column : column number |
| ROWSIZE : row size |
| COLSIZE : column size |
+-------------------------------------+
For example, to find out the address of a[i]:
address = *( base address + offset3 )

= *( a + ( i * ROWSIZE * COLSIZE ) )

and to find out the address of a[i][j]:


address = *( *( base address + offset3 ) + offset2 )

= *( *( a + ( i*ROWSIZE*COLSIZE ) ) + ( j*COLSIZE ) )

and to find out the address of a[i][j][k]:


address = *(*(base address + offset3) + offset2) + offset1

= *(*(a+(i*ROWSIZE*COLSIZE)) + (j*COLSIZE)) + k

and to find out the content of a[i][j][k]:


content = *(*(*(base address+offset3)+offset2)+offset1)

= *(*(*(a+(i*ROWSIZE*COLSIZE))+(j*COLSIZE))+k)
60

Consider the following program:


/* Program 3-10 */

#include <stdio.h>

void main(void)
{
short z[3][2][2] =
{
{ { 0, 2 }, { 4, 6 } },
{ { 1, 3 }, { 5, 7 } },
{ { 11, 22 }, { 33, 44 } }
};

short(*p)[2][2] = z;

for(int i = 0; i<3; i++)


{
for(int j = 0; j<2; j++)
{
for(int k = 0; k<2; k++)
{
printf("&z[%d][%d][%d]:", i, j, k);
printf("%d ", &z[i][j][k]);
}
printf("\n");
}
printf("\n");
}

printf("--------------------------------\n");

printf("p = %d\n", p);


printf("p+1 = %d\n", p + 1);
printf("p+2 = %d\n", p + 2);

printf("\n------------------------------\n");

printf("p[0] = %d p[0]+1 = %d\n", p[0], p[0] + 1);


printf("p[1] = %d p[1]+1 = %d\n", p[1], p[1] + 1);
printf("p[2] = %d p[2]+1 = %d\n", p[2], p[2] + 1);

printf("\n------------------------------\n");
61

printf("p[0][0] = %d ", p[0][0]);


printf("p[0][0][0] = %d\n", p[0][0][0]);

printf("p[0][1] = %d ", p[0][1]);


printf("p[0][1][0] = %d\n", p[0][1][0]);

printf("p[1][0] = %d ", p[1][0]);


printf("p[1][0][0] = %d\n", p[1][0][0]);

printf("p[1][1] = %d ", p[1][1]);


printf("p[1][1][0] = %d\n", p[1][1][0]);

printf("p[2][0] = %d ", p[2][0]);


printf("p[2][0][0] = %d\n", p[2][0][0]);

printf("p[2][1] = %d ", p[2][1]);


printf("p[2][1][0] = %d\n", p[2][1][0]);

printf("\n------------------------------\n");

printf("p[0][0]+1 = %d ", p[0][0] + 1);


printf("p[0][0][1] = %d\n", p[0][0][1]);

printf("p[0][1]+1 = %d ", p[0][1] + 1);


printf("p[0][1][1] = %d\n", p[0][1][1]);

printf("p[1][0]+1 = %d ", p[1][0] + 1);


printf("p[1][0][1] = %d\n", p[1][0][1]);

printf("p[1][1]+1 = %d ", p[1][1] + 1);


printf("p[1][1][1] = %d\n", p[1][1][0]);

printf("p[2][0]+1 = %d ", p[2][0] + 1);


printf("p[2][0][1] = %d\n", p[2][0][1]);

printf("p[2][1]+1 = %d ", p[2][1] + 1);


printf("p[2][1][1] = %d\n", p[2][1][1]);
}
62

The output is:


&z[0][0][0]:100 &z[0][0][1]:102
&z[0][1][0]:104 &z[0][1][1]:106

&z[1][0][0]:108 &z[1][0][1]:110
&z[1][0][1]:112 &z[1][1][1]:114

&z[2][0][0]:116 &z[2][0][1]:118
&z[2][1][0]:122 &z[2][1][1]:124

----------------------------------
p = 100
p+1 = 108
p+2 = 116

----------------------------------
p[0] = 100 p[0]+1 = 104
p[1] = 108 p[1]+1 = 112
p[2] = 116 p[2]+1 = 120

----------------------------------
p[0][0] = 100 p[0][0][0] = 0
p[0][1] = 104 p[0][1][0] = 4
p[1][0] = 108 p[1][0][0] = 1
p[1][1] = 112 p[1][1][0] = 5
p[2][0] = 116 p[2][0][0] = 11
p[2][1] = 120 p[2][1][0] = 33

----------------------------------
p[0][0]+1 = 102 p[0][0][1] = 2
p[0][1]+1 = 106 p[0][1][1] = 6
p[1][0]+1 = 110 p[1][0][1] = 3
p[1][1]+1 = 114 p[1][1][1] = 7
p[2][0]+1 = 118 p[2][0][1] = 22
p[2][1]+1 = 122 p[2][1][1] = 44

The detail for calculating the address of each element of the array z is explained
below.
&z[0][0][0]:100 &z[0][0][1]:102
&z[0][1][0]:104 &z[0][1][1]:106

&z[1][0][0]:108 &z[1][0][1]:110
&z[1][0][1]:112 &z[1][1][1]:114
63

&z[2][0][0]:116 &z[2][0][1]:118
&z[2][1][0]:120 &z[2][1][1]:122

z[0] z[1] z[2]

100 108 116

z[0][0] z[0][1] z[1][0] z[1][1] z[2][0] z[2][1]

100 104 108 112 116 120

z[0][0][0] z[0][1][0] z[1][0][0] z[1][1][0] z[2][0][0] z[2][1][0]

z[0][0][1] z[0][1][1] z[1][0][1] z[1][1][1] z[2][0][1] z[2][1][1]

11 22
1 333 44
0 52 7
4 6

Figure 3-15
64

Let’s use what we’ve just learned to find the address of 3D array.

p = z = 100 /* the address of the first table */

100

a[0] a[1] a[2]

100 108 116

a[2]

a[1]

a[0]

Figure 3-16
65

p+1 = z + 1

= 100 + ( 1 * ROWSIZE * COLSIZE )

= 100 + ( 1 * ( 2 * 2 * sizeof(short) ) )

= 100 + ( 1 * ( 2 * 2 * 2 ) )

= 100 + ( 1 * 8 )

= 100 + 8

= 108 /* the address of the second table */

p+1

108

a[0] a[1] a[2]

100 108 116

a[2]

a[1]

a[0]

Figure 3-17
66

Since p is a pointer to a 2D array ( an array of 2 arrays ), when it is added by 1, the


address that we obtain is 108 because in an array consists of 2 subarrays and each
subarray consists of 2 shorts.

p+2 = z + 2

= 100 + ( 2 * ROWSIZE * COLSIZE )

= 100 + ( 2 * ( 2 * 2 * sizeof(short) ) )

= 100 + ( 2 * ( 2 * 2 * 2 ) )

= 100 + ( 2 * 8 )

= 100 + 16

= 116 /* the address of the third table */

p+2

116

a[0] a[1] a[2]

100 108 116

a[2]

a[1]

a[0]

Figure 3-18
67

p[0] = *(p+0) = *(100) = 100

/* the address of the first row of the first table */

p[1] = *(p+1) = *(108) = 108

/* the address of the first row of the second table */

p[2] = *(p+2) = *(116) = 116

/* the address of the first row of the third table */

p[0] p[1] p[2]


100 108 116

z[0] z[0]+1 z[1] z[1]+1 z[2] z[2]+1


100 104 108 112 116 120

100 102 108 110 116 118

z[2]
z[1] z[2]+1
z[0]
z[1]+1
z[0]+1

Figure 3-19

After p is dereferenced or subscripted with one set of the square brackets, it will
decay into a pointer to a 1D array ( an array of 2 shorts ).
68

p[0]+1 = 100 + 1

= 100 + ( 1 * COLSIZE )

= 100 + ( 1 * ( 2 * sizeof(short) ) )

= 100 + ( 1 * ( 2 * 2 ) )

= 100 + ( 1 * 4 )

= 100 + 4

= 104 /* the address of the second row of the first table */

p[1]+1 = 108 + 1

= 108 + ( 1 * COLSIZE )

= 108 + ( 1 * ( 2 * sizeof(short) ) )

= 108 + ( 1 * ( 2 * 2 ) )

= 108 + ( 1 * 4 )

= 108 + 4

= 112 /* the address of the second row of the second table */

p[2]+1 = 116 + 1

= 116 + ( 1 * COLSIZE )

= 116 + ( 1 * ( 2 * sizeof(short) ) )

= 116 + ( 1 * ( 2 * 2 ) )

= 116 + ( 1 * 4 )

= 116 + 4

= 120 /* the address of the second row of the third table */


69

p[0]+1 p[1] +1 p[2]+1


104 112 120

z[0] z[0]+1 z[1] z[1]+1 z[2] z[2]+1


100 104 108 112 116 120

104 106 112 114 120 122

z[2]
z[1] z[2]+1
z[0]
z[1]+1
z[0]+1

Figure 3-20

Note that if the expression such as *p, p[0], p[1] or p[2] is added by 1, it will give
the address of the second row of the table.

p[0][0] = *(p[0]+0) = *(100) = 100

/* the address of the first column in the first row of the first table */

p[0][1] = *(p[0]+1) = *(104) = 104

/* the address of the first column in the second row of the first table */

p[1][0] = *(p[1]+0) = *(108) = 108

/* the address of the first column in the first row of the second table */

p[1][1] = *(p[1]+1) = *(112) = 112

/* the address of the first column in the second row of the second table */
70

p[2][0] = *(p[2]+0) = *(116) = 116

/* the address of the first column in the first row of the third table */

p[2][1] = *(p[2]+1) = *(120) = 120

/* the address of the first column in the second row of the third table */

After p is double dereferenced or subscripted with two sets of the square brackets, it
will decays into a pointer to a short.

p[0][0][0] = *(p[0][0]+0) = *(100) = 0

p[0][1][0] = *(p[0][1]+0) = *(102) = 4

p[1][0][0] = *(p[1][0]+0) = *(108) = 1

p[1][1][0] = *(p[1][1]+0) = *(112) = 5

p[2][0][0] = *(p[2][0]+0) = *(116) = 11

p[2][1][0] = *(p[2][1]+0) = *(120) = 33


71

p[0][0] p[0][1] p[1][0] p[1][1] p[2][0] p[2][1]

100 104 108 112 116 120

0 2 4 6 1 3 5 7 11 22 33 44
100 102 104 106 108 110 112 114 116 118 120 122

p[0][0][0] 0 2 p[1][0][0] 1 3 p[2][0][0] 11 22


p[0][1][0] 4 6 p[1][1][0] 5 7 p[2][1][0] 33 44

11 22
1 333 44
0 52 7
4 6

Figure 3-21

p[0][0]+1 = 100 + 1 = 100 + ( 1 * sizeof(short) ) = 102

/* the address of the second column in the first row of the first table */

p[0][1]+1 = 104 + 1 = 104 + ( 1 * sizeof(short) ) = 106

/* the address of the second column in the second row of the first table */

p[1][0]+1 = 108 + 1 = 108 + ( 1 * sizeof(short) ) = 110

/* the address of the second column in the first row of the second table */

p[1][1]+1 = 112 + 1 = 112 + ( 1 * sizeof(short) ) = 114

/* the address of the second column in the second row of the second table */
72

p[2][0]+1 = 116 + 1 = 116 + ( 1 * sizeof(short) ) = 118

/* the address of the second column in the first row of the third table */

p[2][1]+1 = 120 + 1 = 120 + ( 1 * sizeof(short) ) = 122

/* the address of the second column in the second row of the third table */

Note that if the expression such as **p, p[0][1], p[1][1] or p[2][1] is added by 1, it
will give the address of the second column in second row of the table.

p[0][0][1] = *(p[0][0]+1) = *(102) = 2

p[0][1][1] = *(p[0][1]+1) = *(106) = 6

p[1][0][1] = *(p[1][0]+1) = *(110) = 3

p[1][1][1] = *(p[1][1]+1) = *(114) = 7

p[2][0][1] = *(p[2][0]+1) = *(118) = 22

p[2][1][1] = *(p[2][1]+1) = *(122) = 44


73

p[0][0]+1 p[0][1]+1 p[1][0]+1 p[1][1]+1 p[2][0]+1 p[2][1]+1

102 106 110 114 118 122

0 2 4 6 1 3 5 7 11 22 33 44
100 102 104 106 108 110 112 114 116 118 120 122

0 2 p[0][0][1] 1 3 p[1][0][1] 11 22 p[2][0][1]

4 6 p[0][1][1] 5 7 p[1][1][1] 33 44 p[2][1][1]

11 22
1 333 44
0 52 7
4 6

Figure 3-22
74

Chapter 4
Pointers and Strings

4.1 Introduction

A string is a group of characters terminated by a null character (or null byte). The null
character is represented as '\0', a backslash followed a by a zero.

/* Program 4-1 */

#include <stdio.h>

void main(void)
{
printf("'\\0' \n");

printf("%%c = %c \n", '\0');

printf("%%d = %d", '\0');

printf("\n\n");

printf("'0' \n");

printf("%%c = %c \n", '0');

printf("%%d = %d", '0');


}

The output is:

'\0'
%c =
%d = 0

'0'
%c = 0
%d = 48
75

Note that to display a percent sign ( % ) and a backslash by the printf( ) function, we
have to double itself. But for the double quote ( " ), we have to precede it by a
backslash.

The above output shows us that the null character is the nonprinting character whose
value is 0. Although consisting of two characters, ‘\’ and ‘0’, the null character is just
regarded as one character because it is an escape sequence like ‘\n’, ‘\t’ or etc.

Since C has no particular data type for storing strings, it uses an array of chars to
store a string instead. Consider the following code fragment:

char s1[5] = { 'T', 'E', 'X', 'T', '\0' }; /* method 1 */

char s2[5] = "TEXT"; /* method 2 */

If the size of an array is specified by ourselves, we must allocate enough number of


array elements to hold all characters in the string including the null character. That
is, the array is one element longer than the number of characters in the string to
accommodate the null character. By the way, the easier way and result in the same is
not specify a size of an array as show below.

char s1[] = { 'T', 'E', 'X', 'T', '\0' }; /* method 1 */

char s2[] = "TEXT"; /* method 2 */

From the statements above, all in all the second one is more simple and preferred. In
the first statement, the null character must be included at the end but not in the second
because it is automatically done when a group of characters is surrounded by the
double quotation marks (double quotes). And also remember that the double
quotation marks are not part of the string. They are used to inform that they enclose
a string, just as the single quotation marks (single quotes) identify a character. And
we don’t have to enclose each character in the single quotes when it is part of a string
(enclosed in double quotes).

Both methods as shown above are defining and initializing an array at the same time.
But if we don’t initialize an array at the same time as it is defined we must assign
an individual character in a string at a time as shown below.
76

char s[5];

s[0] = 'T';
s[1] = 'E';
s[2] = 'X';
s[3] = 'T';
s[4] = '\0';

And remember that we can’t do like this.

char s1[5], s2[5];

s1 = { 'T', 'E', 'X', 'T', '\0' }; /* NO */

s2 = "TEXT"; /* NO */

But initializing string character by character is a hard and boring work. The better
way is to apply strcpy( ) function as shown in the program 3-2 below.

/* Program 4-2 */

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

void main(void)
{
char s[5];

strcpy(s, "text");

printf("The length of string: %d", strlen(s));

printf("\n\n");

printf("The size of string: %d", sizeof(s));

printf("\n\n");

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


printf("%c", s);

printf("\n\n");

printf("%s", s);
77

The output is:

The length of string: 4

The size of string: 5

TEXT

TEXT

Don’t forget to include string header file when strcpy( ) function or other string
functions are needed. And don’t forget to add another byte for an array to store a null
character. In the program, the strlen( ) function can be used to find the length of a
string but it does not count the null character at the end of the string. On the other
hand, the sizeof operator returns a number that one larger than the number returned
from strlen( ) because it also counts the invisible null character used to end the string.

To display a string on screen, we use two method in this program. The first method
is using a for loop and function printf( ) with %c format specification to display each
character in a string. The second one is using the %s format specification to display
all characters in the string in one time. Note that the second method is easier and
preferred.

Actually, any characters within strings can be letters, numbers, symbols (such as
escape sequences, double quotes, semicolon, etc.)

/* Program 4-3 */

#include <stdio.h>

void main(void)
{
char s1[] = "%AB!_-#?&\t";
char s2[] = "b9c: '(}]*\\";
char s3[] = "\n\"17Fe ;><";

printf("%s", s1);
printf("%s", s2);
printf("%s", s3);
}
78

The output is:

%AB!_-#?& b9c: '(}]*\


"17Fe ;><

Note that when we want to keep a backslash and a double quote in a string, we must
precede them by a backslash.

4.2 Pointers to strings

In fact, we have been always using strings so far such as in using it as an argument
in function printf( ). Consider the form of the function prototype of printf( ).

int printf( const char *format, … )

Note that the first argument of the function accept memory address. So we can call
the printf( ) function like this:

printf("Hello, world");

The string "Hello, world" can be passed as an argument to the function printf( )
because it evaluates to the address of the first character in the string. So we can use a
char pointer to store its address. For example

char *p = "Hello, world";

"Hello, world" will be stored in read-only memory. Consequently, we cannot


modify this string. In other word, we can dereference p to read; however, we cannot
dereference p to write.

Let’s find out the different between the following string declarations
char c = 'Z';

char *p = "AAA";

char z[] = "BBB";


79

p = "CCC"; /* OK */
*p = 'L'; /* NO - can’t write */
c = *p; /* OK - can read */

z = "CCC"; /* NO */
*z = 'L'; /* OK - can read */
c = *z; /* OK - can write */

strcpy(p, "TTT"); /* NO */
strcpy(z, "TTT"); /* OK */

p = z /* OK */
*p = 'N' /* OK */

Note that the quoted string "CCC" can be assigned to p because p is a pointer but
we cannot modify this string since it is stored in read-only memory. In the other hand,
we cannot assign "CCC" to z since z is an array of chars so it cannot hold an address
but z can modify its string as shown in the expression *z = 'L', which this expression
is equivalent to z[0] = 'L', because z itself has its memory location for storing string.
Moreover, p can also point to z and then, we can modify the string in z through p as
shown in the expression *p = 'N' which is equivalent to z[0] = 'N'.

4.3 Pointers and the const qualifier

The keyword const can be applied with a pointer as we have seen in the previous
topic in the function prototype of printf( ) function. When this keyword is used with
a pointer, its position affect what a pointer can do. Consider the following code
fragments:

/* 1 */

char c1, c2;

const char cc = 'Z';

char *p;

const char *pc;


80

p = &c1; /* OK */
*p = 'T'; /* OK */
c2 = *p; /* OK */
p = &cc; /* NO */

pc = &c1; /* OK */
*pc = 'W'; /* NO */
c2 = *pc; /* OK */
pc = &c2; /* OK */

pc = &cc; /* OK */
*pc = 'L'; /* NO */
c1 = *pc; /* OK */
c1 = 'A'; /* OK */
c2 = 'B'; /* OK */
cc = 'U'; /* NO */

From the above code fragment, pc is a pointer to a const char which mean it cannot
modify the char variable that it’s pointing to. That is, we cannot dereference pc to
change what it points to as shown in the expression *pc = 'W' and *pc = 'L'. In the
other word, we cannot dereference pc to write but we can dereference it to read as
shown in the expression c2 = *pc and c1 = *pc. Anyway, the pointer pc itself is not
constant, so we can change it to point to other char variables as in the expression
pc = &cc. In addition, note that pc can hold the address of c1, c2 and cc but p cannot
store cc’s address.

/* 2 */

char c1, c2;

char * const p = &c1;

*p = 'T'; /* OK */
c2 = *p; /* OK */
p = &c2; /* NO */

p is a const pointer to an int which mean after we have created this pointer, it cannot
be changed to point to any other char variables. So we have to initialize it at the same
time that we declare it. We can dereference this pointer to read or write.
81

/* 3 */

char c1, c2;

const char cc1 = 'A';


const char cc2 = 'B';

const char * const p1 = &c1;


const char * const p2 = &cc1;

*p1 = 'T'; /* NO */
p1 = &c2; /* NO */
p1 = &cc1; /* NO */
p1 = &cc2; /* NO */
c2 = *p1; /* OK */

*p2 = 'T'; /* NO */
p2 = &c1; /* NO */
p2 = &c2; /* NO */
p2 = &cc2; /* NO */
c2 = *p; /* OK */

p1 and p2 is a const pointer to a const int which mean they cannot be changed to
point to any other char variables. We have to initialize them at the same time that we
declare them. We can only dereference these pointers to read. The below table
summarizes all things that are discussed in this section.
82

can store
can modify can can the address of
declaration read write

pointer variable
char const char
pointed
to

const char *p Y Y Y Y

char * const p Y Y Y Y

const char * const p Y Y Y

The order of a data type and keyword isn’t important. So the following declarations
are equivalent:

const char *p == char const *p

const char * const p == char const * const p

Table 3-1
83

4.4 Passing strings as arguments to functions

When we pass a string to a function, we pass its address.

/* program 4-4 */

#include <stdio.h>

int len(const char *);

void main(void)
{
char msg[] = "TEST";

printf("%s: %d", msg, len(msg));


}

int len(const char *p)


{
int length = 0;

while (*p != '\0')


{
length++;
p++;
}

return length;
}

The output is:

TEST: 4

The function len( ) in this program emulate the function strlen( ) of C standard
library.
84

4.5 Returning a string from a function

When we return a string from a function, we also return its address.

/* program 4-5 */

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

int len(const char *);

char *link(const char *, const char *);

void main(void)
{
char msg1[] = "Hello, ";
char msg2[] = "world";

char *message = link(msg1, msg2);

printf("%s", message);

free(message);
}

int len(const char *p)


{
int length = 0;

while (*p != '\0')


{
length++;
p++;
}

return length;
}

char *link(const char *p1, const char *p2)


{
int n = len(p1) + len(p2);

char *s = (char *)malloc(n);


85

while (*p1 != '\0')


{
*s = *p1;
s++;
p1++;
}

while (*p2 != '\0')


{
*s = *p2;
s++;
p2++;
}

*s = '\0';

return(s - n);
}

The output is:

Hello, world

Note that we can also hold strings in the heap area by using malloc( ) function.
86

4.6 An array of pointers to strings

When we want to hold several strings, we can create an array of pointers to keep
them.

char *p[] =
{
"Sophia",
"Peter",
"Bob",
"Alexandra"
};

This array will be stored in memory as shown below.

Sophy Pete Bob Alexandra


100 106 111 115

p[0] 100 S o p h y \0
p[1] 106 P e t e \0
p[2] 111 B o b \0
p[3] 115 A l e x a n d r a \0
Figure 4-1
87

Chapter 5
Pointers and Structures

5.1 Introduction

Like the way we can have any other kinds of pointer variables, C also provide a kind
of pointer that can point to a structure variable. Such a pointer is called a structure
pointer or a pointer to a structure.

5.2 Creating structure pointers

We can create pointers to structures in very similar way as we create ordinary


structure variables. Just add the * operator in front of a variable name. Consider the
following code fragment:

/* 1 */

struct book
{
char title[10];
float price;
};

struct book *p;

/* 2 */

struct book
{
char title[10];
float price;
}*p;

Both ways as shown above give the same result.


88

5.3 Accessing structure members

There are two ways to access any members in a structure with a structure pointer.

(*pointer_name).member_name /* 1 */

pointer_name->member_name /* 2 */

In the first method, the parentheses are necessary because the precedence of the .
operator is higher than the * operator. For the second method, the -> operator, which
is the minus sign ( - ) followed by greater than operator ( > ), is a shorthand
of the first method. When these operators are applied with structures, we can call the
. operator that structure member-of operator and the -> operator that structure
member-access operator. Both . and -> associate from left to right.

/* Program 5-1 */

#include <stdio.h>

void main(void)
{
struct book
{
char *title;
float price;
};

struct book comic, *p;

p = &comic;

(*p).title = "The Walking Dead";

p->price = 14.99;

printf("%s\n", p->title);

printf("price: $%0.2f\n\n", (*p).price);

printf("The size of p: %d\n", sizeof(p));


}
89

The output is:

The Walking Dead


$14.99

The size of p: 4

To obtain the address of the structure variable comic, we have to place the
address-of operator, &, in front of it. So, from the expression p = &comic, the
address of comic is assigned to the structure pointer p. And also note that the size of
p is 4 bytes like all other kinds of pointers.

5.4 Passing structure pointers as arguments to functions

The form of declaring structure pointers as parameters of functions are the same as
they are declared as variables in functions.

/* Program 5-2 */

#include <stdio.h>

struct book
{
char *title;
float price;
};

void display(struct book *);

void main(void)
{
struct book comic;

comic.title = "The Walking Dead";

comic.price = 14.99;

display(&comic);
}
90

void display(struct book *pf)


{
printf("%s\n", pf->title);

printf("price: $%0.2f\n", (*pf).price);


}

5.5 Returning a structure pointer from a function

The general form of declaring function that return a structure pointer is

struct tag_name * function_name(parameter_list)

Consider the following program:

/* Program 5-3 */

#include <stdio.h>

struct book
{
char *title;
float price;
};

struct book *func(void);

void main(void)
{
struct book *p = func();

printf("%s\n", p->title);

printf("price: $%0.2f\n", (*p).price);


}

struct book *func(void)


{
static struct book comic;

comic.title = "The Walking Dead";

comic.price = 14.99;
91

return &comic;
}

5.6 An array of structure pointers

To declare an array of structure pointers, just put the square brackets and a subscript
after a structure pointer’s name.

/* 1 */

struct book
{
char title[10];
float price;
};

struct book *p[5];

/* 2 */

struct book
{
char title[10];
float price;
}*p[5];
92

Chapter 6
Pointers and Functions

6.1 Introduction

C provides another kind of pointer — a function pointer (or a pointer to a function)


to hold the address of a function. When a pointer has the address of a function, the
function can be called through that pointer.

6.2 Creating and using function pointers

The general form of declaring function pointers is

data_type (*pointer_name)(parameter_list)

The parentheses around a pointer’s name are necessary without them it would be a
function declaration since the * operator has the lower precedence than the ( )
operator. The data type of a return’s value and parameters of a function pointer must
correspond with a function that it point to. Let's look at the program 6-1 below to see
how to declare and use this kind of pointer.

/* Program 6-1 */

#include <stdio.h>

void func1(void);
void func2(void);

void main(void)
{
/* declaring function pointers */
void(*p1)(void);
void(*p2)(void);
93

/* assigning the address of functions to pointers */


p1 = func1;
p2 = &func2;

/* calling functions through pointers */


p1();
(*p1)();

p2();
(*p2)();
}

void func1(void)
{
printf("TEST1:");
}

void func2(void)
{
printf("test2:");
}

The output is:

TEST1:TEST1:test2:test2:

Note that there are two methods to obtain the address of a function. The function’s
address can be obtained by using a function’s name without the parentheses. There
can be the address-of operator in front of a function pointer’s name whether or not.

And there are also two methods to call a function through a function pointer. We are
well familiar with the first method because it has the same form as a function call.
For the second method, there may be an advantage is it remind us that a function is
called through a pointer and the parentheses are required here too.
94

6.3 Passing function pointers to functions

The form of declaring function pointers as parameters of functions are the same as
they are declared as variables in functions.

/* Program 6-2 */

#include <stdio.h>

int add(int, int);

void display(int(*)());

void main(void)
{
display(add);
}

int add(int num1, int num2)


{
return(num1 + num2);
}

void display(int(*p)(int, int))


{
int num1, num2;

printf("Enter num1: ");


scanf("%d", &num1);

printf("Enter num2: ");


scanf("%d", &num2);

printf("%d", (*p)(num1, num2));


}

The output is:

Enter num1: 1
Enter num2: 2

3
95

6.4 Returning a function pointer from a function

The general form of declaring function that return a pointer to function is

data_type (*function_name(parameter_list))(parameter_list)

Note that a function’s name and parameters will be inside the first parentheses.

/* Program 6-3 */

#include <stdio.h>

int add(int, int);


int sub(int, int);

int(*compute(void))();

void main(void)
{
int(*p)(int, int) = compute();

int num1, num2;

printf("\nEnter number1: ");


scanf("%d", &num1);

printf("Enter number2: ");


scanf("%d", &num2);

printf("\n%d", (*p)(num1, num2));


}

int add(int num1, int num2)


{
return num1 + num2;
}

int sub(int num1, int num2)


{
return num1 - num2;
}
96

int(*compute(void))(int, int)
{
printf("Select operation\n");
printf("================\n");
printf(" + : Addition\n");
printf(" - : Subtraction\n\n");
printf("Enter character: ");

char c = getchar();

switch (c)
{
case '+': return add;
case '-': return sub;
}
}

The output is:

Select operation
================
+ : Addition
- : Subtraction

Enter character: -

Enter number1: 1
Enter number2: 2
-1

6.5 An array of function pointers

Of course, we can create an array of function pointers like any other pointers. The
general form of an array of function pointers is

data_type (*pointer_name[index])(parameter_list)

To declare an array of function pointers, the square brackets and a subscript are put
after the name of function pointer.
97

/* Program 6-4 */

#include <stdio.h>

int add(int, int);


int sub(int, int);
int mul(int, int);

void main(void)
{
int(*p[3])(int, int) = { add, sub, mul };

int n, num1, num2;

printf("Select operation\n");
printf("==================\n");
printf("0 : Addition\n");
printf("1 : Subtraction\n");
printf("2 : Multiplication\n\n");

printf("choose: ");
scanf("%d", &n);

printf("\nEnter number1: ");


scanf("%d", &num1);

printf("Enter number2: ");


scanf("%d", &num2);

printf("\n%d", (*p[n])(num1, num2));


}

int add(int num1, int num2)


{
return num1+num2;
}

int sub(int num1, int num2)


{
return num1-num2;
}

int mul(int num1, int num2)


{
return num1*num2;
}
98

The output is:

Select operation
==================
0 : Addition
1 : Subtraction
2 : Multiplication

choose: 2

Enter number1: 2
Enter number2: 2

6.6 Complex declaration

Now, it’s time to learn how to interpret complex declarations. Let’s take a look at
the process for interpreting complex declaration.

step 1 Start at the name of a variable or function.

Look to the right. If the square brackets are found, continue reading to the
step 2 right until there are no the square brackets left. Or if the opening parenthesis
is found, read up to the nearest closing parenthesis.

step 3 Look to the left. If the opening parenthesis is found, read up to the balancing
parenthesis and go back to step 2

step 4 If const or * is found, keep reading to the left, until it's not one of these and
go back to step 3.

step 5 Read the remaining things to the left.


99

Suppose we have the following declarations.


1. unsigned char *p[3][5];

Declaration Step Match Interpretation

unsigned char *p[3][5] 1 Y p is

unsigned char * [3][5] 2 Y an array of 3 arrays of 5

3 N -
unsigned char *

4 Y pointers to

3,4 N -
unsigned char

5 Y an unsigned char

p is an array of 3 arrays of 5 pointers to an unsigned char.

unsigned char * p [3][5];


5 5 4 1 2 2

Note that the part of the declaration that is printed in bold is what we are dealing
with in each step.
100

2. float (*p)[2][4];

Declaration Step Match Interpretation

float (*p)[2][4] 1 Y p is

float (*)[2][4] 2,3 N -

4 Y a pointer to

3 N -
float ( )[2][4]

4 Y

float [2][4] 2 Y an array of 2 arrays of 4

float 3,4 N -

5 Y floats

p is a pointer to an array of 2 arrays of 4 floats.

float ( * p )[2][4];
5 3 4 1 2 2
101

3. double(*p(int))[4][4];

Declaration Step Match Interpretation

double (*p(int))[4][4] 1 Y p is

double (* (int))[4][4] 2 Y a function

3 N -
double (*)[4][4]

4 Y that return a pointer to

double ( )[4][4] 3 Y

double [4][4] 2 Y an array of 4 array of 4

3,4 N -
double

5 Y doubles

p is a function that return a pointer to an array of 4 arrays of 4 doubles.


double ( * p (int) )[4][4];
5 3 4 1 2 2 2
102

4. int(*p(void))(short,long);

Declaration Step Match Interpretation

int (*p(void))(short,long) 1 Y p is

int (*(void))(short,long) 2 Y a function

int (*)(short,long) 3 N -

4 Y that return a
pointer to

int ( )(short,long) 3 Y

int (short,long) 2 Y Function

3,4 N -
int

5 Y which return an int

p is a function that return a pointer to function which return an int.


int ( * p (void) )(short,long);
5 3 4 1 2 2
103

5. const char * const *(*p[5])();

Declaration Step Match Interpretation

const char * const *(*p[5])( ) 1 Y p is

const char * const *(* [5])( ) 2 Y an array of 5

3 N -
const char * const *(*)( )

4 Y pointers to

const char * const *( )( ) 3 Y

const char * const * ( ) 2 Y a function

3 N -

const char * const *


that return
4 Y a pointer to
a const pointer to

const char 5 Y a const char

p is an array of 5 pointers to a function that return a const pointer to a const char.


const char * const * ( * p [5] )( );
5 4 3 4 1 2 2

You might also like