c++ - C 与 C++ 中的编译器在目标文件中包含未使用的符号

标签 c++ c compilation

这可能是一个愚蠢的问题,但也许有人可以提供一些见解。

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

如果源文件是 C 语言,那么编译器似乎会忽略全局符号,并且所有链接都没有错误。如果源文件是 C++,则符号包含在两个目标文件中,然后我会收到链接器错误。对于 C++,我在包含 header 时使用 extern "C"。

我使用的是来自 VS2005 的 Microsoft 编译器。

这是我的代码:

头文件(test.h):

#ifndef __TEST_H
#define __TEST_H

/* declaration in header file */
void *ptr;

#endif

C 源文件:

测试1.c

#include "test.h"

int main( ) {
    return 0;
}

测试2.c

#include "test.h"

C++ 源文件:

测试1.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++ 源代码时定义了它,这会导致链接器错误。

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

谢谢。

最佳答案

在 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

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

因此,您可能在头文件中有一个暂定的定义。我们需要查看实际代码才能确定。

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

An identifier with external linkage is used, but in the program there does not exist exactly one external definition for the identifier, or the identifier is not used and there exist multiple external definitions for the identifier.

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

编辑:根据this thread on comp.lang.c++ , C++ 没有暂定定义。原因是:

This avoids having different initialization rules for built-in types and user-defined types.

(该线程处理相同的问题,顺便说一句。)

现在我几乎可以肯定,OP 的代码在头文件中包含了 C 所谓的“暂定定义”,这使得它在 C 中是合法的,在 C++ 中是非法的。不过,只有当我们看到代码时,我们才能确定。

有关“暂定定义”的更多信息以及为什么需要它们,请参见 excellent post on comp.lang.c (克里斯托雷克)。

关于c++ - C 与 C++ 中的编译器在目标文件中包含未使用的符号,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1987413/

相关文章:

c++ - 类型转换 C++ 的行为方式很奇怪

c++ - 在程序中获取AIX中进程的虚拟内存大小

c++ - 使用 LD_PRELOAD 修复对 malloc 的递归调用

c++ - 防止使用 glibc 头文件

c++ - OpenGL 半球纹理贴图

c++ - 使用链接为静态的 boost::regex 编译我的项目

c++ - 如果临时对象是隐式不可修改的,这是如何工作的?

c - 我的数组 printf 循环在末尾缺少一位数字

linux - 编译linux内核openssl/opensslv.h错误

c++ - 编译DLL时如何添加include