Multi-Dimensional Arrays

A multi-dimensional array is just an array of arrays

We declare a 2 dimensional array with:

int a[10][20];

int x;

This can be viewed as 10 arrays, each of which contain 20 integer elements

An individual element of this array is retrieved with:

x = a[i][j];

Naturally we can still do things like the following

int* pa;

pa = &a[0][0]; /* or even pa = a; */

x = *(pa+i*20+j); /* x = a[i][j]; */

Similarly we can do things like:

int a[10][20];

int (*pa)[20]; /* see last note below */

pa = &a[0]; /* pa = &a[0][0]; */

Then (*pa)[5] would be the same as a[0][5]

An observation: A variable is declared in the way that it is used

The type in a variable declaration is usually a basic type, the declaration syntax shows how a value of that basic type can be obtained from the variable name

Also note that [] has higher precedence than *, so we need to use parenthesis in the above declaration, otherwise we would have an array of 20 pointers to integers instead of a pointer to an array of 20 integers

Character Arrays and Initialization

An array of pointers to text strings (which is not the same as a 2 dimensional array of characters) is often used in C programming. Such a structure can be initialized in the following way:

char* name[] = {"George", "Mark", "Peter", "Jimmy"};

The last element of the array should be NULL, to serves as a marker. We can detect the end of the array in the following way:

#define INFINITY 32767 /* 2^15 - 1 */

#define NULL 0
 

0 George

1 Mark

2 Peter

3 Jimmy

4 ™?               int i = 0;

5                  while (name[i++] != NULL);

Consider now the assignment statement

name[2] = "Paul"; /* replace "Peter" by "Paul" */

Note that the compiler has built the string constant "Paul" and the assignment causes name[2] to point to it.

The space that "Peter" used is still in existence, but is now inaccessible. Later we will see how to manage this memory loss better.

See King pages 262 and 263

Consider another way of forming this matrix of names:

char name[][7] = {"George", "Mark", "Peter", "Jimmy"};

This is your customary 2-D array formation. Thus in C we have two similar but distinctly different ways of forming multidimensional arrays. The first one above is the more efficient in terms of storage space, but uses pointers. Also the use of char* (string) variables warrants use of the special system functions in <string.h> and you will have to become familiar with these, especially strcpy, strcmp, and strlen

Functions and Procedures

There are two ways of declaring and defining procedures in C, the old way and the ANSI standard way - you may run into both, but use only ANSI form

All procedures in C are really functions, that is they return a value. The special type void indicates that a return value is not used (not wanted). Such void functions are called procedures.

C has both function declarations and function definitions - these are two different concepts

A function declaration contains all the information required to call the function, that is the name the types of the parameters and the type of the return value. These declarations are also called prototypes.

A function definition includes all the information in a function declaration, plus local variables and the statements in the function - It not only describes how the function can be called, but also how it computes its value

Function Declarations (ANSI prototype)

type FunctionName ( ParameterDeclarations );

For example:
float Work (int x, double y, char* s);
float Work (int , double , char* );

The parameters in the prototype are in the order of the function definition (no surprise there).

Function Definitions

The ANSI style of function definition is:

type FunctionName (ParameterDeclarations) {

    local variable declarations;

    statements;

    return (expression);

}

A return statement is used to supply the value of the function and to give control back to the calling program

Formats of the return statement are:

return (Rvalue); or return Rvalue;

and

return ; /* used only with void functions */

Although the return can be omitted in that case.

Parameter Passing

All parameter passing in C is by value, that is, when a function is called the parameter values from the calling function are copied into temporary storage in the called function--all modifications to the parameter values occur in this temporary storage; the original values in the calling function are not changed

This means that you cannot return a result directly through a parameter. The only way to export a result with a parameter is to do so indirectly through a pointer to the location where the result will be stored

Remember arrays are built with pointers, so if you pass an array address (not an array element) to a function, you can modify the elements in the array and these changes will be seen outside of the function

void initialize (int x) {

    we can do anything we like to x inside this function.

    The calling function won't see any of these changes.

    It provides only the initial value of x

    x = 5;

}

but consider ::::::::

void reset (int* y) {

    *y = 5; /* put 5 in cell pointed to by y */

}

    int i = 3, j = 3;

    initialize ( i );

    reset ( &j );

    printf ( "%d, %d", i, j );

This will print 3, 5, since we have passed a pointer to j into the function, the value pointed at is changed by the reset - note that the invocation reset (j) will cause all sorts of problems, since the parameter j isn't a pointer.

When would j be a pointer?

int j[10];

then

reset (j); is the same as reset ( &j[0] );

thus the parameter j is a pointer. HOWEVER, the corresponding formal parameter would still be

void reset ( int* y );

The ReadLine and FetchLine functions, below, are equivalent, but their parameter declarations are appropriate for their intended use

#include <stdio.h>

int ReadLine ( char str[], int n )

{

}

int FetchLine (char* str, int n)

{

}

They are used in the following way:

void main (void) {

}
Function Calls

When you call a function, the actual parameters (arguments) to the function are copied and placed onto the stack. Thus the function manipulates copies of the parameters, not the original arguments themselves. That is, all function calls in C are call by value.
To alter a data object in the calling program you must either return a value from the function, or pass the address of the object to the function.

Just to convince you that arguments are copied:

#include <stdio.h>

void look (int a, int b, char c, char d, double f, double g) {
 

}

/*

The previous is a procedure called "look" that prints 8 lines of output. Each line has a single number that is the address of a different parameter--coerced from an unsigned int to an int and printed in hexadecimal (%x)

*/

int main (void) {

}

produces output like

The address of a is 40b0

The address of b is 40b4

The address of c is 40b8

The address of d is 40b9

The address of f is 40c0                why not 40ba ?

The address of g is 40c8

Run this program and discuss the remaining output with your TA

The address of argument a is effff874       4 bytes

The address of argument b is effff878

The address of argument c is effff827       1 byte

The address of argument d is effff826

The address of argument f is effff818       8 bytes

The address of argument g is effff810

Note the size of the (stack) addresses!!

Why are they not at 4-byte intervals?