cglobal-variablesdeclarationdefinition

Why can I define a variable twice in C?


I've been testing global variables, defining and declaring, and I stopped at this situation:

main.c:

#include "stdio.h"

void func(void);
int a;

int main(void) {
    a = 20;
    printf("in main: %d\n", a);
    func();
    
    return 0;
}

add.c:

#include <stdio.h>

void func(void);
int a;

void func() {
    printf("in add: %d\n", a);
}

so in C the line

int a;

means both declaration and definition, but we know that defining a variable more than once is not allowed. So why does this code compile if we have two definitions and two declarations of a? I'm working in CLion and when I press "Go to Definition/Declaration" on a in main, it moves the pointer to a in add.c, when I do the same in add.c, it moves back to main.c, so I can't understand what's happening in here.


Solution

  • Outside of any function, int x; is a tentative definition, and some compilers and linkers treat them as a sort of “cooperative definition,” where an identifier can be declared this way in multiple files and will result in defining only one object.

    C’s rules for external declarations (declarations outside of functions) are a bit complicated due to history—C grew with different people developing and experimenting, rather than by design with the knowledge we have today.

    Definitions: int x = 3; is a definition. It both declares the identifier x and reserves memory for an int, and it initializes the int to 3.

    Declarations: extern int x; is a declaration but not a definition. It declares the identifier x but does not reserve memory for it.

    extern int x; gives x external linkage, and so does int x = 3; if it appears outside of a function. External linkage means, when they appear in different source files, the two instances of the identifier will be linked to refer to the same thing in memory.

    The C standard says “there shall be” at most one definition for an identifier with external linkage (C 2018 6.9 5). (If the identifier is used in the program, there must be a definition. If it is not used in an expression, you do not need a definition.)

    Tentative definitions: int x; is a hybrid. Outside of a function, it is a special kind of declaration called a tentative definition. The C standard says that, if there is a tentative definition in a translation unit (the source file being compiled, along with all the files it includes) and there is no regular definition, a regular definition will be created.

    Now, what happens if you violate the rule that “there shall be” at most one definition? Here is the thing: It is not a rule a program has to obey. When the C standard says “shall” it means, if a program obeys this rule, the behavior will be as the C standard says. If a program disobeys this rule, the C standard does not define the behavior (C 2018 4 2). Instead, we let the compiler and the linker define the behavior, if their designers elect to do so.

    A common behavior (but not the only possibility) in compilers and linkers when a program violates the rule about at most one definition was:

    • If there are multiple regular definitions when linking, report an error.
    • If there are multiple definitions coming from tentative definitions but no more than one coming from a regular definition, coalesce them into a single definition.

    This was the defined default behavior in GCC and associated tools prior to GCC version 10 and was explicitly mentioned in the C 2018 standard’s informative section on common extensions, in J.5.11. In current versions of GCC, multiple definitions of any type are treated as an error by default. You can request the old behavior with the command-line switch -fcommon.