Search code examples
cstaticforward-declarationlinkage

How do I reference a static data in C that appears after the function that needs it?


#include <stdio.h>
typedef unsigned char uint8_t;
// I want this at the end of the file
//static const uint8_t hello[] = { 'H','e','l','l','o',' ','W','o','r','l','d','!','\0'};
int main()
{   // how do I declare a forward reference to 'hello' here?
    printf("%s\n", hello);
    return;
}
// but down here, the linker can't resolve it
static const uint8_t hello[] = { 'H','e','l','l','o',' ','W','o','r','l','d','!','\0'};

Error C2065 'hello': undeclared identifier

For cosmetic reasons, I would like to put my large, static data tables at the end of my c source file. But how do I reference it? I have used a forward reference in a function to resolve functions that appear later in the file, but static variables are giving me a headache. I tried extern (as a last hope,) but the linker looks outside of the module (which makes sense,) but won't resolve to the darn variable just a few lines past the function that needs it. Is this a C limitation (I've tried this on two compilers,) or am I just missing something incredibly obvious?


Solution

  • I would like to put my large, static data tables at the end of my c source file. But how do I reference it?

    Use a tentative definition.

    static const uint8_t hello[13 /* more */];
    
    int main(void) {
      ...
    }
    
    static const uint8_t hello[] = { 
        'H','e','l','l','o',' ','W','o','r','l','d','!','\0' };
    

    Cites follow. There is reasonable disagreement as to if static const uint8_t hello[]; should work. Compilers I use prohibit it which matches my reading of the spec.

    6.9.2 External object definitions A declaration of an identifier for an object that has file scope without an initializer, and without a storage-class specifier or with the storage-class specifier static, constitutes a tentative definition. If a translation unit contains one or more tentative definitions for an identifier, and the translation unit contains no external definition for that identifier, then the behavior is exactly as if the translation unit contains a file scope declaration of that identifier, with the composite type as of the end of the translation unit, with an initializer equal to 0.

    If the declaration of an identifier for an object is a tentative definition and has internal linkage, the declared type shall not be an incomplete type. C17 § 6.9.2 2&3

    J.2 Undefined behavior An identifier for an object with internal linkage and an incomplete type is declared with a tentative definition (6.9.2).