|
|||
Department of Engineering | |
University of Cambridge > Engineering Department > computing help |
reverse (str) { char *str; ... }rather than
reverse (str) char *str; { ... }the compiler might not warn the programmer that the formal parameter str has the default type int and a local variable str is created which isn't initialised.
int* ptr1, ptr2;
int mean(num1, num2) int num1, num2; { ... } int i, answer; double f; /* deliberate mistake! */ answer = mean(f,i); printf("The mean of %f and %d is %d\n", f, i, answer);
C functions usually get given arguments via the stack. Calling functions put values on the stack then peel the same number of bytes off when returned to, so it doesn't matter to K&R C if the subfunction doesn't use or declare all the arguments that it is given. It doesn't even matter if it declares more arguments than given by the calling function as long as it doesn't write to these values. Were it to do so, it might well overwrite the address that the called function should return to. Such a problem might not be recognised for quite a while, and isn't easy to track down. This is where `lint' (see 16.1) becomes useful
int array[100]
and you want to use this
array from another source file, you mustn't declare as
extern int *array
but as extern int array[]
. An
explanation of why this is so comes from Chris Volpe (volpecr@crd.ge.com)
When you declare int array[100];
the compiler sets aside storage for
100 ints, at say, address 500. The compiler knows that array is an
array, and
when it tries to generate code for an expression like array[3]
, it
does the following: It takes the starting address of the array (500), and
adds to that an offset equal to the index (3) times the size of an int
(typically 4) to get an address of 500+3*4=512
. It looks at the int
stored at address 512 and there's the int.
When you give an external declaration in another file like
extern int *array;
,
the compiler takes your word for it that array
is a pointer. The linker
resolves the symbol for you as an object that resides at address 500. But
since you lied to the compiler, the compiler thinks there's a pointer variable
stored at address 500. So now, when the compiler sees an expression like
array[3]
, it generates code for it like this: It takes the address of
the
pointer (500) and, assuming there's a pointer there, reads the value of the
pointer stored there. The pointer will typically reside at address 500
through 503. What's actually in there is indeterminate. There could be a
garbage value stored there, say 1687. When executed the code gets this value,
1687, as
the address of the first int to which the pointer points. It then adds the
scaled index offset (12) to this value, to get 1699, and tries to read the
integer stored at address 1699, which will likely result in a bus error or
segmentation violation.
The thing to remember about all this is that even though
array[index]
and pointer[index]
can be used interchangeably in
your source code, the compiler generates very different object code depending
on whether you are indexing off an array identifier or a pointer identifier.