// in file2.c
extern int *b; // b is declared as a pointer to an integer
// in file1.c
int b[2] = {100, 101}; // b is defined and initialized as an array of 2 ints
The linker links them both to same memory address, however since the symbol b
has different types in file1.c
and file2.c
, the same memory location is interpreted differently.
// in file2.c
int x2; // assuming sizeof(int) == 4
x2 = b[1]; // b[1] == *(b+1) == *(100 + 1) == *(104) --> segfault
b[1]
is evaluated first as *(b+1)
. This means get the value at the memory location b
is bound to, add 1
to it (pointer arithmetic) to get a new address, load that value into the CPU register, store that value at the location x2
is bound to. So, the value at the location b
is bound to is 100
, add 1
to it to get 104
(pointer arithmetic; sizeof *b
is 4) and get the value at the address 104
! This is wrong and undefined behaviour and most likely will cause program crash.
There is a difference in how the elements of an array are accessed and how the values pointed to by a pointer are accessed. Let's take an example.
int a[] = {100, 800};
int *b = a;
a
is an array of 2
integers and b
is a pointer to an integer initialized to the address of the first element of a
. Now when a[1]
is accessed, it means get whatever is there at offset 1
from the address of a[0]
, the address (and the next block) to which the symbol a
is bound. That's one assembly instruction. It's as if some information is embedded into the array symbol so that the CPU can fetch an element at an offset from the base address of the array in one instruction. When you access *b
or b[0]
or b[1]
, you first get the content of b
which is an address, then do the pointer arithmetic to get a new address and then get whatever is there at that address. So the CPU has to first load the content of b
, evaluate b+1
(for b[1]
) and then load the content at address b+1
. That's two assembly instructions.
For an extern array, you don't need to specify its size.The only requirement is that it must match with its external definition. Therefore both the following statements are equivalent:
extern int a[2]; // equivalent to the below statement
extern int a[];
You must match the type of the variable in its declaration with its external definition. The linker doesn't check for types of variables when resolving references of symbols. Only functions have the types of the function encoded into the function name. Therefore you won't get any warning or error and it would compile just fine.
Technically, the linker or some compiler component could track what type the symbol represents, and then give an error or warning. But there is no requirement from the standard to do so. You are required to do the right thing.