Search code examples
cglobal-variablesheader-filesdefinitionforward-declaration

No warning with uninitialized C string


I'm currently wondering why I don't get an error from GCC during compilation/linking of a small C program.

I declared in version.h the following string:

const char* const VERSION;

In version.c I have set the initialization of the variable:

const char* const VERSION = "0.8 rev 213";

No problem with that. I can use the string in the rest of the program.

If the c file is missing, no error occurs during compilation/linking but the program fails with SIGSEGV (of course) when it tries to access the variable.

Is my way of setting up the variable VERSION correct or is there a better way? Or is there a chance to get an error during compilation/linking?


Solution

  • You have defined (not just declared) a variable in the header.

    If you ever include this header from more than one source file, the behaviour is undefined. Here's the relevant quote from the standard:

    J.2 Undefined behavior

    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.

    You are relying on a GCC-specific (actually common to many compilers, but still non-standard) behaviour here, which is merging of duplicate tentative data definitions. See help for -fcommon and -fno-common GCC compilation flags. Not all compilers behave this way. Historically this is the common behaviour for linkers, because that's how Fortran worked before there was C.

    Assuming this language extension, one of the definitions (one that has an explicit initializer) initialises the variable to point to your string literal. But if you omit this definition, it will remain zero-initialised. That is, it will be a constant null pointer. Not very useful.

    To make long story short, never ever do that. In order to declare (but not define) a global variable in a header, use extern. If you do, and try to omit a definition elsewhere, you will likely get a linker error (though the standard does not require diagnostic for this violation, all known implementation produce one).