2

I was trying out programs based on extern and as I understand, this is helpful when accessing variables across multiple files having only one definition.

But I tried a simple program as below without extern and thing seem to work when I expected it would fail during linking process

file5.c:

#include <stdio.h>
#include "var.h"

int a = 20;

int main() {
  printf("\n File5.c a = %d", a);
  test();
  return 0;
}

file6.c:

#include <stdio.h>
#include "var.h"

int test() {
  printf("\n File6.c a = %d",a);
}

var.h

int a;

As I have included var.h in all header files without extern, int a would be included in both the .c file and during linking, compiler should have thrown a warning or error message but it compiles file without any issue.

Shouldn't var.h have the following extern int a?

alk
  • 69,737
  • 10
  • 105
  • 255
pkumarn
  • 1,383
  • 4
  • 22
  • 29
  • @JonathanLeffler I knew the translation unit rules weren't identical in C and C++, but never considered they may differ with *this*. Is this in the standard as implementation-dependent? It would seem somewhat odd if it were. – WhozCraig Sep 07 '13 at 20:05
  • @JonathanLeffler yeah I think I get it already. The differences in C and C++ between declarations, definitions, and when something is one, the other, or both. Ex. In C I can throw `int a;` into *all* my source files, with *one* (or none) having an initializer, and it all links. Add an initializer to a second one and kerboom, multi-symbol link error. In C++ it will link-error regardless unless all-but-one is `extern`. – WhozCraig Sep 07 '13 at 20:12
  • 1
    @WhozCraig: more or less. The multiple declarations with at most one initialized is identified as a 'common extension' in §J.5.11 of the 2011 standard. – Jonathan Leffler Sep 07 '13 at 20:14

1 Answers1

2

It is generally best if the header uses extern int a;. See also How do I share a variable between source files in C?

The standard says:

ISO/IEC 9899:2011 §6.9.2 External object definitions

Semantics
¶1 If the declaration of an identifier for an object has file scope and an initializer, the declaration is an external definition for the identifier.

¶2 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.

Thus, what's in the header is a tentative definition of the variable. At the end of the translation unit (TU) for file5.c, you have no longer got a tentative definition; the 'external definition' specified by int a = 20; has specified that. At the end of the TU for file6.c, you have a definition equivalent to int a = 0;.

When you try to link file5.c and file6.c, you should run into multiple definitions of a. However, there is a common extension, documented in the standard in Annex J:

J.5.11 Multiple external definitions

¶1 There may be more than one external definition for the identifier of an object, with or without the explicit use of the keyword extern; if the definitions disagree, or more than one is initialized, the behavior is undefined (6.9.2).

Your compiler is providing the extension identified by §J.5.11, and therefore (legitimately) not complaining.

580

Community
  • 1
  • 1
Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
  • +1 Helluva nice citation, btw. Can't get more applicable than that. Thanks. – WhozCraig Sep 07 '13 at 20:15
  • +1 for this "The multiple declarations with at most one initialized is identified as a 'common extension'" .thanks – Gangadhar Sep 08 '13 at 05:23
  • Thanks for the explanation. This definitely seems to be gcc specific thing and might not be applicable on different compiler. Quite a tricky thing. – pkumarn Sep 08 '13 at 18:21
  • Actually, just for once, this isn't so much GCC-specific as Unix-specific. The C standard officially supports a strict ODR (One Definition Rule) like C++ does, but the legacy Unix behaviour was more usually the §J.5.11 behaviour. The C standard recognizes this by including it as a documented 'Common Extension' behaviour. GCC is merely following the native compilers on Unix systems (where GCC was not originally the native compiler), and provides it on Linux for compatibility with other Unix systems. – Jonathan Leffler Sep 08 '13 at 18:27