int V[10] = { 2, 3, 5, 7, 11, 13, 17, 19, 23, 29 } ; double X[100] = { 1.0e15, 3.7, 20 } ; int squares[] = { 0, 1, 4, 9, 16, 25, 36, 49, 64, 81 } ;

When an array is declared with `const`

, its
elements cannot be changed.

const int DaysOfMonth[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 } ;

int D2[5][3] ; double D3[2][4][6] ;

If `X`

is declared with `int X[5][3] ;`

it is an array of 5 elements that are arrays of 3 elements
that are integers.

`X`

: array of arrays of integers`X[3]`

: array of 3 integers`X[3][2]`

: integer

Consequently, arrays are stored in
*row-major order*.
So, the first seven elements of `X`

, as stored in memory, are
`X[0][0]`

, `X[0][1]`

, `X[0][2]`

,
`X[1][0]`

, `X[1][1]`

, `X[1][2]`

,
and `X[2][0]`

.

int A[3][2] = { { 2, 4}, {3, 9}, {4, 16 } } ; int B[3][2] = { 2, 4, 3, 9, 4, 16 } ;

double M[1000][1000], i, j ; for (i=0; i<1000; ++i) { for (j=0; j<1000; ++j) { if (i==j) { M[i][j] = 1.0 ; } else { M[i][j] = 0.0 ; } } }

The array variable itself is a constant, *but* you
can change its elements.

If `V`

is a one-dimensional array,
assigning to `V`

is *not allowed* but
assigning to `V[5]`

is allowed.

If `X`

is a two-dimensional array,
assigning to `X[5]`

is *not allowed* but
assigning to `X[5][5]`

is allowed.

C treats an array like a *constant* reference to
its 0’th elements; that is,
`V`

and `&V[0]`

are the same.
For a two-dimensional array `X`

,
`X[5]`

and `&X[5][0]`

are the same.

The *postfix* array index has a higher precedence
that the *prefix* address operator.
Therefore,
`&X[5][0]`

is `&((X[5])[0])`

.

void reverse(int V[10]) { int i, j ; for (i=0, j=9; i<j; ++i, --j) { int t ; t = V[i] ; V[i] = V[j] ; V[j] = t ; } }

#define SIZE 209 void reverse(int V[SIZE]) { int i, j ; for (i=0, j=SIZE-1; i<j; ++i, --j) { int t ; t = V[i] ; V[i] = V[j] ; V[j] = t ; } }

void reverse(int V[], int n) { int i, j ; for (i=0, j=n-1; i<j; ++i, --j) { int t ; t = V[i] ; V[i] = V[j] ; V[j] = t ; } }

Let’s try a generic `identity`

function.

void identity(int X[][], int n) ;

This is not allowed! Suppose `identity`

was called with
two different array sizes.

int Y[300][300] ; int Z[400][400] ;

`Y[1][0]`

is 300 integers past the start of `Y`

.
`Z[1][0]`

is 400 integers past the start of `Z`

.
Since the compiler can’t "see" the calls, it doesn’t know what code to
compile.

C99 provides for *variable-length arrays* or *VLAs*.
The dimension of arrays can be set from expressions, though these
dimensions cannot be changed once the array is declared.

int numStudents = 31 ; char namesStudents[numStudents][16] ;

Variable length arrays can also be passed as parameters.

void identity(int n, int X[n][n]) ;

ANSI C allows the safe passing of arrays if all but the first dimension is fixed.

void identity(int X[][100]) ;

`int R[3][5] ;` |
`R` is a 3×5 array |
`R[0][0]` |

`int *S[5] ;` |
`S` is an array of 5 pointers to integers |
`*S[0]` |

`int (*T)[5] ;` |
`T` is a pointer to an array of 5 integers |
`(*T)[0]` |

`int **V ;` |
`V` is a pointer to a pointer to an integer |
`**V` |

#include <stdio.h> #include <stdlib.h> int main(int argc, char **argv) { int R[3][5] ; int *S[5] ; int (*T)[5] ; int **V ; int i, j ; for(i=0; i<3; ++i) { for (j=0; j<5; ++j) { R[i][j] = i*10 + j ; } } for(i=0; i<5; ++i) { S[i] = &R[i%3][i] ; } T = &R[1] ; V = &S[2] ; puts("\nValues of R[i][j]") ; for(i=0; i<3; ++i) { for (j=0; j<5; ++j) { printf(" R[%2d,%2d]: %4d\n", i, j, R[i][j]) ; } } puts("\nValues of S[i]") ; for(i=0; i<5; ++i) { printf(" S[%2d]: %p\n", i, (void *) S[i]) ; } puts("Values of *S[i]") ; for(i=0; i<5; ++i) { printf(" *S[%2d]: %4d\n", i, *S[i]) ; } puts("\nValue of T") ; printf(" T: %p\n", (void *) T) ; puts("Values of (*T)[i]") ; for(i=0; i<5; ++i) { printf(" (*T)[%2d]: %4d\n", i, (*T)[i]) ; } puts("\nValue of V") ; printf(" V: %p\n", (void *) V) ; puts("Value of *V") ; printf(" *V: %p\n", (void *) *V) ; puts("Value of **V") ; printf(" **V: %4d\n", **V) ; return (EXIT_SUCCESS) ; }

Values of R[i][j] R[ 0, 0]: 0 R[ 0, 1]: 1 R[ 0, 2]: 2 R[ 0, 3]: 3 R[ 0, 4]: 4 R[ 1, 0]: 10 R[ 1, 1]: 11 R[ 1, 2]: 12 R[ 1, 3]: 13 R[ 1, 4]: 14 R[ 2, 0]: 20 R[ 2, 1]: 21 R[ 2, 2]: 22 R[ 2, 3]: 23 R[ 2, 4]: 24 Values of S[i] S[ 0]: 0x7ffff47dbae0 S[ 1]: 0x7ffff47dbaf8 S[ 2]: 0x7ffff47dbb10 S[ 3]: 0x7ffff47dbaec S[ 4]: 0x7ffff47dbb04 Values of *S[i] *S[ 0]: 0 *S[ 1]: 11 *S[ 2]: 22 *S[ 3]: 3 *S[ 4]: 14 Value of T T: 0x7ffff47dbaf4 Values of (*T)[i] (*T)[ 0]: 10 (*T)[ 1]: 11 (*T)[ 2]: 12 (*T)[ 3]: 13 (*T)[ 4]: 14 Value of V V: 0x7ffff47dbac0 Value of *V *V: 0x7ffff47dbb10 Value of **V **V: 22

If `p`

is an integer pointer (which could just be the
first element of an array), what is `p[i]`

?

If `p`

is an integer pointer and `i`

is an integer,
`p+i`

is the address of the `i`

’th integer
after the integer `p`

is addressing.

So `p[i]`

is `*(p+i)`

.

void reverse(int *p, int *q) { if (p>q) { int *t ; t = p ; p = q ; q = t ; } while (p<q) { int t ; t = *p ; *p = *q ; *q = t ; ++p ; --q ; } }

It is unlikely that `p++`

adds one to the bits stored in
`p`

. It adds the size of an integer.

int *p ; double *q ;

`q = p ;` |
illegal |

`*q = *p ;` |
allowed, `*p` is made into a `double` |

`q = (double *)p ;` |
allowed, but probably not a good idea |

If a function does *not* modify an array,
this should be clearly indicated in the prototype.

int sum(const int a[], int n) ; double DotProduct(const double *X, const double *Y, int n) ;

`int * * const R ;` |
Prevents assignment to `R` |

`int * const * R ;` |
Prevents assignment to `*R` |

`const int * * R ;` |
Prevents assignment to `**R` |

The following looks like a useful function.
On a multi-processor, the inner loop seems to be
*parallelizable*.
Can it ever be used
to modify its `const`

arguments?

void ArraySum(int *A, const int *B, const int *C, int n) { int i ; for (i=0; i<n; ++i) { A[i] = B[i] + C[i] ; } }

It could, if the arrays overlap.

int X[20] ; X[0] = 1 ; ArraySum(&X[1], X, X, 19) ;

This possibility of aliasing prevents the compiler from
*scheduling* the additions of the loop concurrently.

In C99 (and C++) declaring a pointer with the `restrict`

qualifier informs the compiler that an object is allowed to have
only one name. If `ArraySum`

were declared with
restricted pointers, the result of the aliased execution of
`ArraySum`

would be undefined.

Often array sizes depend on constants.

One file may define the constants.

#define NUMCARS 35

Declarations will use the constants to set aside memory for data.

double mpg[NUMCARS] ; int wheels[NUMCARS] ;

Loops will also use the constants to do the work.

double avgMPG ; avgMPG = 0.0 ; for (i=0; i<NUMCARS; ++i) { avgMPG += mpg[i] ; } avgMPG = avgMPG/N ;

However don't be surprised to see obscure uses of
`sizeof`

to determine the number of elements within an array.

const int elementsInV = sizeof(V)/sizeof(V[0]) ;

// generate a histogram from an array argument void genHistogram(const int V[], int vSize, int H[], int hSize) ; // add the elements of an array int sumElements(const int V[], int vSize) ; // find the largest element in an array int maxElement(const int V[], int vSize) ; // count the number times a value occurs within an array) int countValue(const int V[], int vSize, int e) ;

Let's try to get `identity`

to work in a unsavory manner.
Just pass in the address of the first element of the two-dimensional
array along with the size of its dimensions.

identity(&X[0][0], n) ;

Now do some really ugly pointer manipulation.
Remember that every `n+1`

elements must be set equal to 1.
Also `n+1`

×`n-1`

is
`n`

^{2}`-1`

.

void identity(int *p, int n) { int i, j ; for (i=0; i<n-1; ++i) { *p = 1 ; ++p ; for (j=0; j<n; ++j) { *p = 0 ; ++p ; } } *p = 1 ; }

Or even slightly more obscurely.

void identity(int *p, int n) { int i, j ; for (i=0; i<n-1; ++i) { *(p++) = 1 ; for (j=0; j<n; ++j) *(p++) = 0 ; } *p = 1 ; }

Take a look at that example on pages 383 of the textbook (C Primer Plus by Stephen Prata).

int * p1; const int * p2 ; const int ** pp2 ; p1 = p2 ; /* GCC warning: Assignment discard qualifiers */ p2 = p1 ; pp2 = &p1 ; /* GCC warning: Assignment from incompatiable type */

The first assignment is forbidden because it would allow the
`*p2`

to be modified as `*p1`

.
The second assignment is allowed because the original `p2`

is "lost". Assigning to `*p1`

cannot modify
the original `*p2`

values.

The third assignment is also forbidden. So, why is assigning
a `int *`

to a `const int *`

allowed, but assigning a `int **`

to a
`const int **`

not allowed?

The reason is seen in this modified example.

int * p1; const int ** pp2 ; const int n = 13 ; pp2 = &p1 ; /* GCC warning: Assignment from incompatiable type */ *pp2 = &n ; *p1 = 10 ;

If the assignment were allowed, the constant `n`

could be
modified.
Rather than attempt to understand these complex rules, it might
be better to think carefully before assigning
to anything with a `const`

qualifier anywhere in its declaration.