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 integersX[3]
: array of 3 integersX[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.