2009-12-31 84 views
2

这可能是一个愚蠢的问题,但也许有人可以提供一些见解。C或C++编译器在目标文件中包含未使用的符号

我在头文件中定义了一些全局变量(是的,我知道这很糟糕,但这只是一个假设情况)。我将这个头文件包含在两个源文件中,然后将它们编译为两个目标文件。代码中的任何地方都没有引用全局符号。

如果源文件是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 

我注意到,当我编译C源代码时,_ptr被列为UNDEF,当我编译C++源代码时,它被定义为链接器错误。

我知道这在现实生活中并不是一件好事,我只是想明白为什么这是不同的。

谢谢。

+2

是不是真的有这样的事情在C或‘全局’变量或符号C++。所以,发布你的代码来展示你实际做了什么。 – Kylotan 2009-12-31 23:11:09

+0

不只是代码。我们需要了解您如何构建应用程序。你使用静态动态库或只是对象文件等。 – 2009-12-31 23:37:04

+0

我添加了该帖子的代码。我只是试图将两个目标文件链接到一个可执行文件中。 – WildCrustacean 2010-01-04 15:29:39

回答

12

在C,标识符有三种不同类型的“联动”的:

  1. 外部链接:大概,这就是人们的“全局变量”的意思。通常来说,它指的是“随处可见”的标识符。
  2. 内部联动:这些是用static关键字声明的对象。
  3. 无链接:这些对象是“临时”或“自动”的,例如在函数内声明的变量(通常称为“局部变量”)。

对于具有外部连接的对象,您只能有一个的定义。由于头文件定义了这样一个对象,并且包含在两个C文件中,所以它是未定义的行为(但请参见下文)。你的C编译器不会抱怨的事实并不意味着你可以在C中这样做。为此,你必须阅读C标准。 (或者,假设你的编译器没有错误,如果它是以标准兼容模式调用的,并且它抱怨某些事情[给出诊断],这可能意味着你的程序不符合。)

在其他单词,你不能通过测试某些东西并检查你的编译器是否允许它来测试语言允许的内容。为此,您必须阅读标准。

请注意,定义和暂定定义之间存在细微的差异。

$ cat a.c 
int x = 0; 
$ cat b.c 
#include <stdio.h> 
int 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 
#include <stdio.h> 
int 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 

A“暂定定义“如果没有其他定义,则变成C中的”真实定义“。因此,我们可以将两个文件都更改为包含int x;,并且这将是合法的C.

所以,您可能在头文件中有一个试探性的定义。我们需要确认实际的代码。

C标准说,以下是未定义的行为(附录J.2p1):

具有外部链接的标识符被使用,但在节目不存在 恰好一个用于外部定义标识符,或者不使用标识符,并且存在用于标识符的多个外部定义。

C++可能有不同的规则。

编辑:按照this thread on comp.lang.c++,C++没有初步定义。原因是:

这样可以避免对内置类型和用户定义类型有不同的初始化规则。

(同样的问题,顺便说一句线程的交易。)

现在我几乎可以肯定的是OP的代码包含什么C调用在头文件“暂定定义”,这使得它的法律在C和在C++中是非法的。只有当我们看到代码时,我们才会知道。

有关“暂定义”以及为什么需要它们的更多信息在此excellent post on comp.lang.c(Chris Torek着)。

+0

它可能。可以? – 2010-01-01 00:21:37

+0

我不确定。有更多C++知识的人有希望回答这个问题。我不想声称我不确定的事情。 (当然,我希望我在答复中能够得到关于C的所有信息 - 但是如果没有的话,我会很乐意予以更正!) – 2010-01-01 00:23:56

+0

@nobugz:请参阅我的编辑。 – 2010-01-01 00:34:38

1

不要在头文件中定义变量 - 这是邪恶的。

只能在头文件中声明变量 - 使用明确的extern关键字。

不同之处在于C++明确要求一个定义规则 - 在C++程序中可能只有一个具有外部链接的给定变量的定义。

严格来说,C标准具有相同的要求。然而,该标准的附录J列出了允许将多个未初始化的定义视为一个的通用扩展 - 它被称为“通用定义”,因为它类似于(旧式)Fortran中的COMMON块的行为(Fortran IV,又名Fortran 66和Fortran 77)。


警告:是的,如果你知道不够需要问这样的问题,也可以偶尔,非常罕但只是偶尔为之,是在标题定义变量的一个原因。但是这样的场合很少,而且说“不要在头文件中定义变量”就足够准确。


Christoph提出了一个关于'static const变量'的有趣观点。围绕这个问题的一句黄鼠狼话的方式是声称“一个常数不是一个变量”。但是,Christoph是正确的:特别是在C++中,使用了静态const'变量'。在C中,我认为这样的常量会倾向于引发编译器的“未使用”警告;然而,GCC 4.4.2不 给任何警告,当给定的这个最小代码:

static const int x = 3; 
extern int p(void); 
int p(void) 
{ 
    return(3); 
} 

它不抱怨未使用x即使在“-Wall -Wextra”。因此,可以在头文件中定义常量,如'static const SomeType constName = InitialValue;'。至少,如果你的编译器GCC是,尽管代码还没有编译下的Sun Studio编译器警告与“cc -v”:

C compiler: /compilers/v12/SUNWspro/bin/cc 
cc: Sun C 5.9 SunOS_sparc Patch 124867-09 2008/11/25 
acomp: Sun C 5.9 SunOS_sparc Patch 124867-09 2008/11/25 
iropt: Sun Compiler Common 12 SunOS_sparc Patch 124861-13 2009/03/10 
cg: Sun Compiler Common 12 SunOS_sparc Patch 124861-13 2009/03/10 
ld: Software Generation Utilities - Solaris Link Editors: 5.10-1.486 
+0

在头文件中定义常量(即'static const'变量)没有任何问题;如果只声明它们,没有链接时优化的编译器不能内联这些值... – Christoph 2010-01-01 14:46:13

相关问题