This might be a dumb question, but maybe someone can provide some insight.
I have some global variables defined in a header file (yes yes I know that’s bad, but this is just a hypothetical situation). I include this header file in two source files, which are then compiled into two object files. The global symbols are not referenced anywhere in the code.
If the source files are C, then it looks like the compiler omits the global symbols and everything links without errors. If the source files are C++, the symbols are included in both object files and then I get linker errors. For C++ I am using extern “C” when I include the header.
I am using the Microsoft compiler from VS2005.
Here is my code:
Header file (test.h):
#ifndef __TEST_H
#define __TEST_H
/* declaration in header file */
void *ptr;
#endif
C Source files:
test1.c
#include "test.h"
int main( ) {
return 0;
}
test2.c
#include "test.h"
C++ Source Files:
test1.cpp
extern "C" {
#include "test.h"
}
int main( ) {
return 0;
}
test2.cpp
extern "C" {
#include "test.h"
}
For C, the object files look something like this:
Dump of file test1.obj
File Type: COFF OBJECT
COFF SYMBOL TABLE
000 006DC627 ABS notype Static | @comp.id
001 00000001 ABS notype Static | @feat.00
002 00000000 SECT1 notype Static | .drectve
Section length 2F, #relocs 0, #linenums 0, checksum 0
004 00000000 SECT2 notype Static | .debug$S
Section length 228, #relocs 7, #linenums 0, checksum 0
006 00000004 UNDEF notype External | _ptr
007 00000000 SECT3 notype Static | .text
Section length 7, #relocs 0, #linenums 0, checksum 96F779C9
009 00000000 SECT3 notype () External | _main
00A 00000000 SECT4 notype Static | .debug$T
Section length 1C, #relocs 0, #linenums 0, checksum 0
String Table Size = 0x0 bytes
And for C++ they look something like this:
Dump of file test1.obj
File Type: COFF OBJECT
COFF SYMBOL TABLE
000 006EC627 ABS notype Static | @comp.id
001 00000001 ABS notype Static | @feat.00
002 00000000 SECT1 notype Static | .drectve
Section length 2F, #relocs 0, #linenums 0, checksum 0
004 00000000 SECT2 notype Static | .debug$S
Section length 228, #relocs 7, #linenums 0, checksum 0
006 00000000 SECT3 notype Static | .bss
Section length 4, #relocs 0, #linenums 0, checksum 0
008 00000000 SECT3 notype External | _ptr
009 00000000 SECT4 notype Static | .text
Section length 7, #relocs 0, #linenums 0, checksum 96F779C9
00B 00000000 SECT4 notype () External | _main
00C 00000000 SECT5 notype Static | .debug$T
Section length 1C, #relocs 0, #linenums 0, checksum 0
String Table Size = 0x0 bytes
I notice that _ptr is listed as UNDEF when I compile the C source, and it is defined when I compile the C++ source, which results in linker errors.
I understand that this is not a good thing to do in real life, I am just trying to understand why this is different.
Thanks.
In C, identifiers have three different types of “linkage”:
statickeyword.For objects with external linkage, you can have only one definition. Since your header file defines such an object and is included in two C files, it is undefined behavior (but see below). The fact that your C compiler doesn’t complain does not mean it is OK to do so in C. For this, you must read the C standard. (Or, assuming no bugs in your compiler, if it is invoked in a standards-compliant mode, and if it complains about something [gives a diagnostic], it probably means your program isn’t compliant.)
In other words, you can’t test what is allowed by the language by testing something and checking if your compiler allows it. For this, you must read the standard.
Note that there is a subtle difference between definition and tentative definition.
Now, let’s change
a.c:Now compile it:
We can change
b.cinstead:A “tentative definition” becomes “real definition” in C if there is no other definition. So, we could have changed both files to contain
int x;, and it would be legal C.So, you may have a tentative definition in the header file. We need to see the actual code to be sure.
The C standard says that the following is undefined behavior (appendix J.2p1):
C++ may have different rules.
Edit: As per this thread on
comp.lang.c++, C++ does not have tentative definitions. The reason being:(The thread deals with the same question, btw.)
Now I am almost sure that OP’s code contains what C calls “tentative definition” in the header file, which makes it legal in C and illegal in C++. We will know for sure only when we see the code though.
More information on “tentative definitions” and why they are needed is in this excellent post on comp.lang.c (by Chris Torek).