Strange problem with extern "C" (not the usual one)

We have a program with some c and c++ in it and have come across some strange effects. I'll go through the whole thing to make it as clear as possible as extern and extern "C" is a tricky feature with some subtleties that might not be immediately obvious.


(1) extern int a; (in a .cpp file) is saying there is a variable 'a' somewhere else and link to that object, The linker will see '?a@@3HA' as its official name for example.

(2) extern "C" int a; (in a .cpp file) is saying there is a C variable 'a' in some .c file somewhere and link to that object. The linker will see '_a' as its official name, C variables and functions don't get extra stuff tacked onto their names, they are just underscore then name.

(3) extern "C" { int a; } (in a .cpp file) is the one that usually trips people up. This is saying that we are defining a variable 'a' here, but are going to use C style linkage. The linker will see it as _a' and it is created here in this file.

The tricky bit is actually in number 2:
extern "C" int a;
actually means
extern "C" extern int a;
but because you put it all on one line you get to saved the 2 seconds it would take write that second extern with the fun bonus of confusing lots of developers that will come back to your code one day to maintain it.
extern "C" { extern int a;} is also the equivalent to (2).


Now thats all the simple stuff out of the way and now to my actual question. I have three files:
____________________________________
main.cpp

#include <iostream>
using namespace std;

extern "C" int a;
void Add();

int main()
{
cout << a << " goes to";
Add();
cout << a << endl;

int x = 0; //to keep window open
cin >> x;
return 0;
}

____________________________________
globals.c

int a;
____________________________________
add.cpp

extern "C" int a;

void Add()
{
a++;
}

____________________________________

Compile that its fine, works as expected, we get " 0 goes to 1" as output.
Now in main.cpp change
extern "C" int a;
TO
extern "C" {int a;}
We should expect that we are creating two 'a' variables now, one in main.cpp and one in globals.c and should get an error when we build. But we don't, it builds fine and when we run it we get '0 goes to 1' like before.

Now in globals.c change
int a;
TO
int a = 0;
Then we build again, we get the link error that _a is already defined in globals.obj, this is what we expected last time, but it seems that the "int a;" in globals.c was acting only as a declaration not as a definition.


As an extra thing you can try, make the following changes:
____________________________________
globals.c

int a;

void change()
{
a++;
}

____________________________________
main.cpp

#include <iostream>
using namespace std;

extern "C" {int a;}
void Add();
extern "C" void change();

int main()
{
cout << a << " goes to";
Add();
cout << a << endl;

cout << a << " goes to ";
change();
cout << a << endl;

int x = 0; //to keep window open
cin >> x;
return 0;
}

____________________________________
As output we get:
'0 goes to 1'
'1 goes to 2'

But in the function change(), what 'a' is being incremented?
If you put the line 'a++' outside the function change(), you get the error that there is not object 'a' to do this on.

I looks like something weird is happening to that 'int a;' in globals.c any help understanding it would be appreciated.

I found this in the C++11 standard ยง7.5/6
An entity with C language linkage shall not be declared with the same name as an entity in global scope, unless both declarations denote the same entity; no diagnostic is required if the declarations appear in different translation units.
Thanks Peter, that could be related, but is not very clearly worded (to me anyways). Has anyone else come across this before and could explain it to me?
Last edited on
Topic archived. No new replies allowed.