First, see this example (I made this up for the sake of example, it's not a real program):
whatever.h
#ifndef WHATEVER_H
#define WHATEVER_H
void fill(void);
#endif
main.c
#include <stdio.h>
#include "whatever.h"
char *names[10] = {NULL};
int main()
{
int i;
fill();
for (i = 0; i < 10; ++i)
printf("%s\n", names[i]);
return 0;
}
whatever.c
#include "whatever.h"
extern char **names;
void fill(void)
{
int i;
for (i = 0; i < 10; ++i)
names[i] = "some name";
}
When I make this program using:
gcc -o test main.c whatever.c -Wall -g
I don't get any errors or warnings. However, when I run the program, I see that in fill
, names
is actually NULL
. If in whatever.c
I change
extern char **names;
to
extern char *names[];
then everything is fine.
Can anyone explain why this happens? If gcc couldn't link extern char **names;
with the one in main.c
, shouldn't it have given me an error? If it could link them, how come names
ends up being NULL
in whatever.c
?
Also, how does extern char **names;
differ from extern char *names[];
?
I am using gcc version 4.5.1 under Linux.
Update
To further investigate this, I change the definition of names
in main.c
to:
char *names[10] = {"1", "2", "3", "4", "5", "6", "7", "8", "9", "10"};
(keeping extern char **names;
in whatever.c
) and with gdb
, I can see that names
has a value. If I cast that value to char *
and print it, it gives me "1"
. (Note that, it's not *names
that is "1"
, but (char *)names
)
Basically, what it means is that gcc has somehow managed to link extern char **names;
in whatever.c
with names[0]
in main.c
!
Whenever you use different, incompatible types for the same variable in different compilation units (as you have done here), you get undefined behavior. That means it probably won't work and you may not get any error or warning messages about it.
WHY this happens (and why the spec says this is undefined) is due to the way most linkers work. Most linkers don't understand anything about types; they only understand names and memory. So as far as the linker is concerned, a variable is just a block of memory starting at some address. In your (original) program
main.c
definesnames
as referring to the start of a block of memory big enough to hold 10 pointers (probably 40 or 80 bytes, depending on whether this is a 32-bit or 64-bit system), all of which are NULL.whaterver.c
, on the other hand, assumes thatnames
refers to a block of memory big enough to hold ONE pointer, and that pointer points at an array of 10 pointers.