Skip to content

Link error for identifier FirstTime #2

@bzinberg

Description

@bzinberg

In CI here, clang complained of a double definition.

This looks like a valid complaint to me, though as we saw here, gcc successfully compiles the same code. Presumably this means the code is incorrect (i.e. compiler behavior is unspecified) but gcc is being lenient in some way.

Quoting @giordano:

maybe the issue is that the global variable in the header file is being defined twice in two different object files. It's just a completely bad idea to define variables in header files (without being extern at least).

I've personally never seen an issue like this before, for (I think) two reasons: (1) I've never seen a non-static, non-extern global variable in a header file; (2) all the codebases I've worked in have had include guards on the header files, so re-declaration was not happening.

What we need to do

We should fix the link error in a way that does not change program behavior.

As far as I can see, the only sane way this program would compile is if the offending line is being treated as an extern declaration. However I don't have iron-clad proof of this, and I think it's a statement about the behavior of GCC that is not mandated by the C standard.

Of course, we could also start reasoning about the program itself and what it's supposed to do, and/or contact the author 🙂

Attempting to get a full explanation

According to Wikipedia:

If neither the extern keyword nor an initialization value are present, the statement can be either a declaration or a definition. It is up to the compiler to analyse the modules of the program and decide.

According to cppreference

For objects, a declaration that allocates storage (automatic or static, but not extern) is a definition, while a declaration that does not allocate storage (external declaration) is not.

extern int n; // declaration
int n = 10; // definition

Sadly, the above quote does not explicitly say whether the statement

int n;

with no storage-class specifier and no initialization must be considered a definition, or must be considered only a declaration, or whether it's up to the toolchain. (Wikipedia apparently says it's up to the toolchain.)

As far as I can tell, this line is getting inlined twice by the preprocessor (since there is no include guard), and the clang error above seems to indicate that clang is considering at least two of the copies of that line to be definitions (since the only other use of that name in the codebase is definitely neither a definition nor a declaration). Perhaps gcc chooses not to consider more than one of those copies a definition.

On the other hand, if all copies of that line are considered to be declarations and not definitions, then the following excerpt from cppreference:

If no storage-class specifier is provided, the defaults are:

  • extern for all functions
  • extern for objects at file scope
  • auto for objects at block scope

implies that the line is equivalent to

extern long FirstTime;

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions