# ECE 209 Arrays and Pointers

## Initialization

```int V[10] = { 2, 3, 5, 7, 11, 13, 17, 19, 23, 29 } ;
double X[100] = { 1.0e15, 3.7, 20 } ;
```

## Constant arrays

When an array is declared with `const`, its elements cannot be changed.

```const DaysOfMonth = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 } ;
```

## Multi-dimensional arrays

### Declaration

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

### Meaning

If `X` is declared with `int X[5][3] ;` it is an array of 5 elements that are arrays of three 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]`.

### Initialization on declaration

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

### Initialization of identity matrix by for-loop

```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 ;
}
}
```

### Other examples -- if time permits

• multiplication
• relaxation

## Arrays as arguments and pointer

### Arrays as constants

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 alloweed.

If `X` is a two-dimensional array, assigning to `X[5]` is not allowed but assigning to `X[5][5]` is allowed.

### Arrays as pointers

C treats are array like a constant reference to its 0'th elements; that is, `V` and `&V[0]` are the same. For a two-dimensional `X`, `X[5]` and `&X[5][0]` are the same.

### Precedence and associativity

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

## Attempts at generalization

### Only good for an array of size 10

```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 ;
}
}
```

### Only good for an array size fixed at compile time

```#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 ;
}
}
```

### Good for an array of any size

```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 ;
}
}
```

## Problems with generalization

Let's try a generic `identity` function.

```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 solution

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

### ANSI C non-solution

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

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

### The complications of array/pointer declarations

 `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 U[][5] ;` `U` is a pointer to an array of 5 integers `U[0][0]` `int **V ;` `V` is a pointer to a pointer to an integer `**V`

## Pointer arithmetic

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 one `p` is addressing.

So `p[i]` is `*(p+i)`.

### C code once thought to be fast

```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.

### Take care with assignments

```  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

## You didn't see this here...

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`.

```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.

```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 ;
}
```

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) ;
```

### constant vigilence

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

### constant warnings

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 never to assign to anything with a `const` qualifier anywhere in its declaration.

### lack of constancy

The following looks like a useful function, but 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) ;
```