Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Bad linkage of `char *array[size]` and `extern char **array`?

Tags:

c

gcc

linkage

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!

like image 793
Shahbaz Avatar asked May 29 '12 16:05

Shahbaz


1 Answers

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 defines names 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 that names refers to a block of memory big enough to hold ONE pointer, and that pointer points at an array of 10 pointers.

like image 140
Chris Dodd Avatar answered Oct 22 '22 16:10

Chris Dodd