3

0.c

extern int num;

int main(){
 return num;
}

1.c

int num;

2.c

int num = 5;

Above compiles fine with gcc 0.c 1.c 2.c. Why don't I get a multiple definition error during linking?

Isn't int num; under 1.c a full definition?:

tentative definition becomes a full definition if the end of the translation unit is reached and no definition has appeared with an initializer for the identifier.

Or is this undefined behaviour?

Dan
  • 2,694
  • 1
  • 6
  • 19
  • Is `num` being used in `1.c`? – Eugene Sh. May 17 '21 at 17:59
  • It's because `int num;` defines a _common_ symbol. See my recent answer: https://stackoverflow.com/questions/64626917/global-variables-and-the-data-section/64627070#64627070 – Craig Estey May 17 '21 at 18:08
  • That won't work with GCC 10.x or later (without non-default compiler options). By default, GCC 9.x and earlier effectively uses the flag `-fcommon`; GCC 10.x and later uses `-fno-common` (see [GCC 10 changes](https://gcc.gnu.org/gcc-10/changes.html)). You're relying on the behaviour arising from `-fcommon` — see also [How do I use `extern` to share variables between source files?](https://stackoverflow.com/q/1433204/15168). The revised behaviour is in line with the letter of the C standard (in all versions of the standard); the older behaviour is documented as a "common extension" in Annex J. – Jonathan Leffler May 17 '21 at 18:59
  • @JonathanLeffler is it safe to then say, it's undefined behaviour? The first link above says `If a program disobeys this rule, the C standard does not define the behavior (C 2018 4 2)`. – Dan May 17 '21 at 19:48
  • The standard in [J.5.11 Multiple external definitions](http://port70.net/~nsz/c/c11/n1570.html#J.5.11) says: _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 code doesn't satisfy the conditions in the 'if' clause, so it is not undefined behaviour because of that. The introduction to J.5.11 says _The following extensions are widely used in many systems, but are not portable to all implementations._ – Jonathan Leffler May 17 '21 at 20:06
  • I'm having difficulty finding exactly where the standard prohibits it — the following sections are at least semi-relevant: [§6.9.2 External object definitions](http://port70.net/~nsz/c/c11/n1570.html#6.9.2); [§5.1.1.1 Program structure](http://port70.net/~nsz/c/c11/n1570.html#5.1.1.1); [§6.2.2 Linkages of identifiers](http://port70.net/~nsz/c/c11/n1570.html#6.2.2). ISTR having problems with this before; it's somewhere subtle and non-obvious. Most of the standard discusses translating a single TU (translation unit). This is a property of linking multiple TUs. – Jonathan Leffler May 17 '21 at 20:11
  • I see, because in my case `int num;` under `1.c` is a common symbol and technically not defined and I only have 1 definition under `2.c`? – Dan May 17 '21 at 20:12
  • See also [§J.2 Undefined behavior]() which says (in one part): _An identifier with external linkage is used, but in the program there does not exist exactly one external definition for the identifier, or the identifier is not used and there exist multiple external definitions for the identifier (6.9)._ [§6.9.2 ¶2](http://port70.net/~nsz/c/c11/n1570.html#6.9.2p2) is relevant with tentative definitions. `int num;` is a tentative definition; it becomes a full zero-initialized definition at the end of the TU. – Jonathan Leffler May 17 '21 at 20:20
  • @JonathanLeffler I guess `int num;` in both 0.c and 1.c is considered a full definition BUT that are not initialled explicitly, which according to the following link makes it ok (i.e not UB) am I correct? http://port70.net/~nsz/c/c11/n1570.html#J.5.11 – Dan May 17 '21 at 23:03
  • It isn't standard, but it is a common extension. The standard says "only one definition". But under the rules of J.5.11, it is OK — when J.5.11 works at all. In GCC 10.x and later, it no longer works by default, even though it does with earlier versions of GCC. – Jonathan Leffler May 18 '21 at 01:34
  • Ok so I like to confirm, it is officially undefined behaviour to have multiple external definitions, what I have above is UB even if it works in earlier versions of gcc: https://stackoverflow.com/questions/1433204/how-do-i-use-extern-to-share-variables-between-source-files – Dan May 18 '21 at 03:12
  • @JonathanLeffler another post saying its UB: https://stackoverflow.com/questions/67578056/is-having-multiple-tentative-definitions-in-separate-files-undefined-behaviour – Dan May 18 '21 at 03:14

0 Answers0