C - From The Top (part 6)

The answers from last time's puzzles are again too long to list here, so they are both on the cover disc again this month. As before though, an SAE with a floppy will get you them from myself or Archive.

More on printf

You will have already seen that, by using the % sign preceded by a letter or number, you can affect how items are displayed on the screen (a list was given in the second of these articles). However, you will have only seen following the \ character one of two letters, either n or t.

You will have guessed that n is for a new line to be printed and t tabs along the text. There are still more of these, and they are listed below.

LetterMeaning
\\backslash
\'single quote
\"double quote
\?question mark
\abell
\bbackspace
\fformfeed
\nnewline
\0NULL
\rcarriage return
\ttab
\vvertical tab
\xhhhinsert ASCII code hhh

One addition to the % list given in the second article is %0. This causes all the leading zeros to be printed out.

That's all I need to add on the printf command, so on with arrays.

Arrays

An array can be thought of as a set of boxes in memory, into which data can be stored. As with Basic, there is no physical limit to the number of dimensions an array has, other than your machine's memory.

There is no DIM statement in C; arrays are defined by using the variable type, variable name and the number of components held within square brackets [].

char string[30];
int fred[35];
double big[10];

These define a character array of 30 elements, an integer of 35 and a double of 10.

An important fact to remember about arrays is that they go from 0 to n-1 (that is, if the array is [10], the array goes from 0 to 9).

If in Basic, you wanted to define a DIM statement to contain some fixed data or other (say, birth dates), you would use something like this

DIM birthdays(8)
FOR a%=1 TO 8
READ birthdays(a%)
NEXT
DATA ........

In C, this is done away with. To define a set of data elements for an array, you simply encase the data within a set of curly brackets.

int birthdays [8] = { date1, date2, date3 .... };

A two-dimensional array works slightly differently as it has to be thought of as a block of x rows.

The above diagram should make it clearer. A 3 × 3 array contains nine elements all told. The addressing of this array is as follows.

Say you want the second element of the third row assigned to variable x, you would use

x = array [2] [1];

And remember, arrays go from 0, not from 1.

Defining a 2D array is the same as a 1D array, except that the second element needs to be held in another set of brackets. So,

int two_d [10] [10];

defines the int array two_d 10 elements by 10 elements (giving a total of 100 elements).

As before, the contents can be initialised by the use of curly brackets.

The 3D array is the same as a 2D, but with another set of numbers in square brackets.

int three_d [5] [10] [4];

This would give 5 down, by 10 across by 4 'diagonally', giving a total of 5 × 10 × 4 = 200 elements.

Accessing a 3D array into a different variable (say you want point 3 - 6 - 2 - remembering that the array goes from 0 first though!)

var = array [2] [5] [1];

There are a couple of nice little tricks when dealing with arrays under C.

The first is when defining a 1D array and using a set of curly brackets, you don't need to define the number of elements within the square brackets - the compiler does this for you. This is known as an unsized array and is useful as you can always resize it should you need to. This can also be applied to a multi-dimension array. You don't need to define the first element, but do need to define the rest.

Strings need to be handled slightly differently. So, say we had

char names [3] [6] [80];

This would mean we have three lists of strings (say names, birthdays and religion) of six elements of strings, each of a maximum of 80 characters.

Accessing something from this array would be done by saying which list and which element

printf("%s",names[2] [2]);

would display the contents of the third person's religion.

Data can be entered into the array using the standard gets() function, thus:

gets (name [list][element]);

Pointers

As you can by now appreciate, arrays can be very large (I have one program that creates an array of 1000 × 1000 × 1000, a total of 97.6Mb) and passing an array of this size across a number of functions really does slow even my RiscPC down (and worries the boots off the P3 it also runs on!).

A far simpler method (and much faster) of passing an array across multiple functions is to use a pointer.

This is defined in the same way as you would any other pointer:

int *array [x][y][z];
int array [x][y][z];
int *pointer;
pointer=array;

These would define (a) a pointer array of int values, (b) a normal array, (c) a pointer and (d) then makes the pointer equal to the base of the array in memory.

Now, instead of passing over a huge array, you're simply passing over a pointer containing the base of the array in memory. Far quicker!

There are a couple of drawbacks to using pointers with arrays. Firstly, look at the following snippet

int x [10] = { 10 values };
int *pointer;
pointer=x;
printf("%d %d %d\n",*pointer,*(pointer+1),*(pointer+2));
printf("%d %d %d\n",x[0],x[1],x[2]);

You'll see that pointer+1 is held within brackets. This is because the pointer is a higher priority than the +. The brackets are a higher priority than the pointer, so this is processed first. Other than that, the two printf lines do exactly the same.

The second drawback is when accessing a multi-dimensioned array.

Say we've defined

float numbers [5][6];

Normally, we would access this by saying

x = numbers [3][4];

However, we're using a pointer, so the fragment would look like this:

float *pointer;
pointer = (float *) numbers;
printf ("value at x [3][4] = %d\n",*(pointer + (3 * 6) + 4));

In other words, to access point [3][4], you first have to multiply the number of rows by the number of elements in that row (in this case, row 3 × 6 elements) then add the actual element you're after.

It is a lot of messing around and, unless you're going to be passing this across a function, you're going to be better off with the normal x = array [3][4]; command.

The (float*) is known as a cast and was needed in this example, as the array had to be indexed manually, so the pointer arithmetic had to be kept relative to a float pointer.

Character and numeric arrays can also be treated the same as normal numbers and arrays within an if else or switch case condition.

That's enough for this time. Next time I'll be revisiting functions and looking at prototypes and recursion within a function. Until then, here's this time's teasers...

1. Adjust the functions written in the simple maths program you should have done for this time so that the numbers can be floating point or integer and also that the sum and average function(s) use a dimension to store the numbers in (to a maximum of 10).

2. Write a phone directory program which stores the name, phone number (with the STD code separate), four lines for the address and one line for the postcode. Make sure this program has a lookup and display function so that you can find a name and number. The name and phone number arrays should be pointers. Make the program able to store 100 names.

This second program is far more important than the first and should be done as it will be built on later. I would also suggest upgrading your Archive subscription now to include the monthly disc (that's a subtle hint as to how much code there will be soon!).


Source: Archive Magazine 13.6
Publication: Archive Magazine
Contributor: Paul Johnson