Why can non-extern be in .h files in C/C++?

2019-08-03 03:45发布

问题:

Take this file as example,there are many non-extern structures like:

struct list_head source_list;

How can it work when this header file is included by more than one compile units?

There should be error reporting that the same symbol is defined twice,right?

回答1:

Technically there should, but that usage has been around for years and is impossible to eradicate (it's been tried; every so often some vendor decides to make it an error, and reverts after the first hundred or so bug reports). Pedantically, the .h file should declare it extern and one .c/.cpp file should define it.

Briefly, when you don't specify the linkage (static, extern, etc.) of a top level variable, it's declared as "common". At link time, if all references to that variable are the same size (and type, when available) then it is allocated once and all references are made to point to it. If the linker finds different sizes / types / linkages for the same variable, it throws an error.

EDIT: this is clearly confounding people. Here:

jinx:1714 Z$ cat foo.h
int foo;
extern void bar();
jinx:1715 Z$ cat foo.c
#include "foo.h"

int
main(int argc, char **argv)
{
  bar();
  return 0;
}
jinx:1716 Z$ cat bar.c
#include "foo.h"

void
bar(void)
{
  return;
}
jinx:1717 Z$ gcc -Wall foo.c bar.c -o foo
jinx:1718 Z$ ./foo
jinx:1719 Z$ _

Note the complete lack of errors about int foo being multiply defined. This is what I've been trying to say.



回答2:

The term for this is "tentative definition":

A declaration of an identifier for an object that has file scope without an initializer, and without a storage-class specifier or with the storage-class specifier static, constitutes a tentative definition. If a translation unit contains one or more tentative definitions for an identifier, and the translation unit contains no external definition for that identifier, then the behavior is exactly as if the translation unit contains a file scope declaration of that identifier, with the composite type as of the end of the translation unit, with an initializer equal to 0.

So this is well defined in C (but often frowned upon).



回答3:

This struct list_head source_list; fields are declared inside other structures so they are not symbols.

Declarations of other (top level) structures have distinct names so it's ok too.

edit

Note that all variables it this header are really marked with extern.



回答4:

There should be an extern indeed. However, there's no explicit definition of that variable, so the compiler marks it as extern for you.

You would get a linker error if you had

struct list_head source_list = { 0 };

...since this does define the symbol once per translation unit (and hence the linker complains).