# ECE 209 Arrays and Pointers

## Initialization

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

## Constant arrays

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

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

## Multi-dimensional arrays

### Declaration

```int D2 ;
double D3 ;
```

### Meaning

If `X` is declared with `int X ;` it is an array of 5 elements that are arrays of 3 elements that are integers.

• `X`: array of arrays of integers
• `X`: array of 3 integers
• `X`: integer

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

### Initialization on declaration

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

### Initialization of identity matrix by for-loop

```double M, 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 ;
}
}
}
```

## 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` is allowed.

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

### Arrays as pointers

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

### Precedence and associativity

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

## Attempts at generalization

### Only good for an array of size 10

```void reverse(int V) {
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 ;
int Z ;
```

`Y` is 300 integers past the start of `Y`. `Z` 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

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

Variable length arrays can also be passed as parameters.

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

### ANSI C non-solution

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

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

### The complications of array/pointer declarations

 `int R ;` `R` is a 3×5 array `R` `int *S ;` `S` is an array of 5 pointers to integers `*S` `int (*T) ;` `T` is a pointer to an array of 5 integers `(*T)` `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 ;
int *S ;
int (*T) ;
int **V ;

for(int i=0; i<3; ++i) {
for (int j=0; j<5; ++j) {
R[i][j] = i*10 + j ;
}
}

for(int i=0; i<5; ++i) {
S[i] = &R[i%3][i] ;
}

T = &R ;

V = &S ;

puts("\nValues of R[i][j]") ;
for(int i=0; i<3; ++i) {
for (int j=0; j<5; ++j) {
printf("  R[%2d,%2d]:  %4d\n", i, j, R[i][j]) ;
}
}

puts("\nValues of S[i]") ;
for(int i=0; i<5; ++i) {
printf("  S[%2d]:  %08x\n", i, (unsigned int)S[i]) ;
}
puts("Values of *S[i]") ;
for(int i=0; i<5; ++i) {
printf("  *S[%2d]:  %4d\n", i, *S[i]) ;
}

puts("\nValue of T") ;
printf("  T:  %08x\n", (unsigned int)T) ;
puts("Values of (*T)[i]") ;
for(int i=0; i<5; ++i) {
printf("  (*T)[%2d]:  %4d\n", i, (*T)[i]) ;
}

puts("\nValue of V") ;
printf("  V:  %08x\n", (unsigned int)V) ;
puts("Value of *V") ;
printf("  *V:  %08x\n", (unsigned int)*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]:  bf8ebcc8
S[ 1]:  bf8ebce0
S[ 2]:  bf8ebcf8
S[ 3]:  bf8ebcd4
S[ 4]:  bf8ebcec
Values of *S[i]
*S[ 0]:     0
*S[ 1]:    11
*S[ 2]:    22
*S[ 3]:     3
*S[ 4]:    14

Value of T
T:  bf8ebcdc
Values of (*T)[i]
(*T)[ 0]:    10
(*T)[ 1]:    11
(*T)[ 2]:    12
(*T)[ 3]:    13
(*T)[ 4]:    14

Value of V
V:  bf8ebcbc
Value of *V
*V:  bf8ebcf8
Value of **V
**V:    22
```

## 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, 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. 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 ;
X = 1 ;
ArraySum(&X, X, X, 19) ;
```

This possibility of aliasing prevents the compiler from performing several 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.

## Determining the size of an array

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

### Examples -- if time permits

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