extern const linkage specification being seemingly

2019-07-27 07:11发布

问题:

I have a situation building a C codebase with a C++ compiler that parallels this:

lib.h

extern int const values[2] = {1, 2};

lib.c

#include "lib.h"

main.c

#include <iostream>

extern int const values[2];

int main() {
    std::cout << values[0] << ":" << values[1] << std::endl;
}

I had to add the extern because of something pointed out in the C++03 Standard Annex C Compatibility C.1.2 Clause 3. (Compiling with -fpermissive will sweep this under the rug.)

Incidentally the difference it makes in how values appears in objdump is like this before the extern:

$ objdump -t lib.o | grep values
0000000000000000 l     O .rodata    0000000000000008 _ZL6values

$ objdump -t main.o | grep values
0000000000000000         *UND*  0000000000000000 values

...and then it's like this after it is added:

$ objdump -t lib.o | grep values
0000000000000000 g     O .rodata    0000000000000008 values

$ objdump -t main.o | grep values
0000000000000000         *UND*  0000000000000000 values

So the name mangling was dropped, we see an "L" turn into a "G", and the linker does not complain about values being undefined.


Now imagine same situation with two very similar files, modified in the same way:

tmp-exttypes.h

extern const REBYTE Reb_To_RXT[REB_MAX] = { /* bunch of stuff */ };

a-lib.c

extern const REBYTE Reb_To_RXT[REB_MAX];

These are the only two Reb_To_RXT definitions in the project, built clean. But it's not linking and when I objdump the only two files that mention it I get:

$ objdump -t a-lib.o | grep Reb_To_RXT
00000000         *UND*  00000000 Reb_To_RXT

$ objdump -t f-extension.o | grep Reb_To_RXT
00000080 l     O .rodata    00000038 _ZL10Reb_To_RXT

It says L, and it's name mangled. Which didn't make the much simpler example happy. But I'm wondering how this could happen with the extern on it at every appearance. Am I right to believe this is a smoking gun...and is it generally not supposed to happen that something only declared as extern should not have local linkage, anywhere?

回答1:

i can't make sense of what you ask.

but …

having

extern int const values[2] = {1, 2};

in a header file, you have UB if that header is included in more than one translation unit. most likely but not necessarily you will then get a linking error.

one solution: declare the array in the header, like

extern int const values[2];

but define it (with initializer) in the implementation file.

another solution is to use the template trick or the inline function trick to define the array in the header file.

the inline function trick:

typedef int const Values[2];

inline Values& valuesRef()
{
    static Values   theValues = {1, 2};
    return theValues;
}

static Values& values = valuesRef();


回答2:

Yes, correct.

The file you (I) were editing was auto-generated and actually destroyed by a clean make, which was noticed halfway writing down the thought process. That "tmp" you see in that particular header was supposed to hint toward "temporary, do not edit", and if the file doesn't reload in the editor you're using it can be confusing.

(Finished up the train of thought anyway for the institutional knowledge. :-P)