5

Suppose the following code:

File a.c

#include <stdio.h>

int a;
int func();

int main(int argc, char **argv) {
    a = 7;
    int a2 = func();
    printf("a is %d, a2 is %d\n", a, a2);
    return 0;
}

File b.c

int a;

int func()
{
    a = 9;
    return a;
}

When compiled with g++ a.c b.c -Wall -O0, it produces a linking error, as expected. However, when invoking gcc a.c b.c -Wall -O0 it doesn't produce any warning or error!

The output is a is 9, a2 is 9 by the way.

gcc version 5.4.0 20160609 (Ubuntu 5.4.0-6ubuntu1~16.04.4)

Why does GCC allow this?

I was surprised by this behaviour. If you initialize the variables at declaration, then linking will also fail with GCC.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
user765269
  • 311
  • 1
  • 4
  • 12
  • Try defining it twice in the *same* file. You will be surprised. http://en.cppreference.com/w/c/language/extern – Eugene Sh. Feb 15 '17 at 23:12
  • The duplication wouldn't show up until the linker and since nothing references an *external* instance of 'a' then the duplicate definitions go unnoticed and ignored. – Mike Feb 15 '17 at 23:12
  • This was asked earlier this week by someone else ([Why is there no error in this program?](https://stackoverflow.com/questions/42215669/)). That was closed as a duplicate of [Tentative definitions in C99 and linking](https://stackoverflow.com/questions/1490693/). It has been addressed in [How do I use `extern` to share variables between source files in C?](https://stackoverflow.com/questions/1433204/) – Jonathan Leffler Feb 15 '17 at 23:37
  • In case it's not clear from the duplicate: in C and C++, the code is undefined behaviour with no diagnostic required. In some C compilers (but not C++ compilers) there is a compiler extension to link the two `a` together. – M.M Feb 15 '17 at 23:50

1 Answers1

5

When compiled with g++ a.c b.c -Wall -O0 it produces a linking error, as expected. However, when invoking gcc a.c b.c -Wall -O0 it produces no warning and no error!

In your code, a has a tentative definition (which becomes a full definition at the end of the current translation unit) which is valid in C.

However, there's another such tentative definition which becomes a full definition in b.c from another translation unit—both of which provide an external definition for a in your program. In other words, a.c and b.c are fine on their own, but it isn't valid when they are combined either compiling together (either directly you do or by compiling them into separate modules and then making an executable by linking them). This is undefined behaviour:

C11, 6.9/5:

An external definition is an external declaration that is also a definition of a function (other than an inline definition) or an object. If an identifier declared with external linkage is used in an expression (other than as part of the operand of a sizeof or _Alignof operator whose result is an integer constant), somewhere in the entire program there shall be exactly one external definition for the identifier; otherwise, there shall be no more than one.161

However, this is often supported as an extension by GCC. That's why when you invoke gcc, it compiles fine as C code. Strictly (standard-wise), this is invalid code in C.

When you invoke g++ to compile it as C++ code, it'd fails because C++ has no tentative definitions. It's not valid in C++. Hence, g++ errors out. See One Definition Rule in C++.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
P.P
  • 117,907
  • 20
  • 175
  • 238