这可能是一个愚蠢的问题,但也许有人可以提供一些见解.
我在头文件中定义了一些全局变量(是的,我知道这很糟糕,但这只是一种假设情况).我将此头文件包含在两个源文件中,然后将这些文件编译为两个目标文件.全局符号不会在代码中的任何位置引用.
如果源文件是C,那么看起来编译器省略了全局符号,并且所有链接都没有错误.如果源文件是C++,则符号包含在两个目标文件中,然后我会收到链接器错误.对于C++,当我包含标题时,我使用extern"C".
我正在使用VS2005的Microsoft编译器.
这是我的代码:
头文件(test.h):
#ifndef __TEST_H #define __TEST_H /* declaration in header file */ void *ptr; #endif
C源文件:
test1.c
#include "test.h" int main( ) { return 0; }
test2.c中
#include "test.h"
C++源文件:
test1.cpp
extern "C" { #include "test.h" } int main( ) { return 0; }
测试2.cpp
extern "C" { #include "test.h" }
对于C,目标文件看起来像这样:
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
对于C++,它们看起来像这样:
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
我注意到_ptr在编译C源时被列为UNDEF,并且在编译C++源时定义它,这会导致链接器错误.
我知道这在现实生活中不是一件好事,我只是想了解为什么这是不同的.
谢谢.
在C中,标识符有三种不同类型的"链接":
外部联系:粗略地说,这就是人们对"全局变量"的意思.通常,它指的是"无处不在"可见的标识符.
内部链接:这些是使用static
关键字声明的对象.
无链接:这些是"临时"或"自动"的对象,例如在函数内声明的变量(通常称为"局部变量").
对于具有外部链接的对象,您只能有一个定义.由于您的头文件定义了这样一个对象并包含在两个C文件中,因此它是未定义的行为(但见下文).您的C编译器没有抱怨的事实并不意味着在C中这样做是可以的.为此,您必须阅读C标准.(或者,假设您的编译器中没有错误,如果它是在符合标准的模式下调用的,并且如果它抱怨[给出诊断],则可能意味着您的程序不符合.)
换句话说,您无法通过测试某些内容并检查编译器是否允许来测试该语言允许的内容.为此,您必须阅读标准.
请注意,定义和暂定定义之间存在细微差别.
$ cat a.c int x = 0; $ cat b.c #includeint x = 0; int main(void) { printf("%d\n", x); return 0; } $ gcc -ansi -pedantic -W -Wall -c a.c $ gcc -ansi -pedantic -W -Wall -c b.c $ gcc -o def a.o b.o b.o:(.bss+0x0): multiple definition of `x' a.o:(.bss+0x0): first defined here collect2: ld returned 1 exit status
现在,让我们改变a.c
:
$ cat a.c int x; /* Note missing " = 0", so tentative definition */
现在编译它:
$ gcc -ansi -pedantic -W -Wall -c a.c $ gcc -o def a.o b.o $ ./def 0
我们可以改变b.c
:
$ cat a.c int x = 0; $ cat b.c #includeint x; /* tentative definition */ int main(void) { printf("%d\n", x); return 0; } $ gcc -ansi -pedantic -W -Wall -c a.c $ gcc -ansi -pedantic -W -Wall -c b.c $ gcc -o def a.o b.o $ ./def 0
如果没有其他定义,"暂定"将成为C中的"真实定义".所以,我们可以更改两个文件以包含int x;
,这将是合法的C.
因此,您可能在头文件中有一个暂定的定义.我们需要确定实际的代码.
C标准说以下是未定义的行为(附录J.2p1):
使用具有外部链接的标识符,但是在程序中,不存在标识符的正好一个外部定义,或者不使用标识符,并且标识符存在多个外部定义.
C++可能有不同的规则.
编辑:根据此线程comp.lang.c++
,C++没有暂定定义.原因是:
这避免了对内置类型和用户定义类型的不同初始化规则.
(线程处理相同的问题,顺便说一句.)
现在我几乎可以肯定OP的代码包含C在头文件中称为"暂定定义"的内容,这使得它在C语言中是合法的并且在C++中是非法的.只有当我们看到代码时,我们才会确定.
有关"暂定定义"及其原因的更多信息,请参阅comp.lang.c(Chris Torek)的优秀文章.