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

2023-12-06

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

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

如果源文件是 C 语言,那么编译器看起来会忽略全局符号,并且所有内容都会链接而不会出现错误。如果源文件是 C++,则符号将包含在两个目标文件中,然后我会收到链接器错误。对于 C++,当我包含标头时,我使用 extern "C"。

我使用的是VS2005的微软编译器。

这是我的代码:

头文件(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++ 源文件:

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

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

Thanks.


在 C 语言中,标识符具有三种不同类型的“链接”:

  1. 外部链接:粗略地说,这就是人们所说的“全局变量”。通俗地说,它指的是“随处”可见的标识符。
  2. 内部联系:这些是声明的对象static关键词。
  3. 没有链接:这些是“临时”或“自动”的对象,例如函数内部声明的变量(通常称为“局部变量”)。

对于具有外部链接的对象,您只能有one定义。由于您的头文件定义了这样一个对象并包含在两个 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):

使用了具有外部链接的标识符,但在程序中不存在 标识符只有一个外部定义,或者未使用标识符并且存在 标识符存在多个外部定义。

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

Edit: 按照这个线程在comp.lang.c++,C++没有暂定定义。原因是:

这避免了内置类型和用户定义类型具有不同的初始化规则。

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

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

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

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C 与 C++ 中的编译器将未使用的符号包含在目标文件中 的相关文章

随机推荐

  • 根据 PWD 更新多项缓冲区名称

    如果我使用 konsole 或其他终端 终端标签名称可以根据 PWD 更改 但在多项中 缓冲区名称是 terminal
  • 如何在 WPF 中创建反斜杠键的键绑定?

    Trying to define a CTRL Backslash keybinding for our WPF command but we re running into two issues 反斜杠键没有任何预定义常量 只有 Oem
  • 如何向下滚动 UITable 视图,直到在 Calabash 中看到带有标签“Value”的单元格

    如何向下滚动 UITableView 直到看到带有标签 Value 的单元格葫芦 黄瓜 我一直在尝试使用以下方法来做到这一点 Then I swipe down until I see Value 并使用 Then I scroll dow
  • 有没有办法在属性网格之外使用 CollectionEditor?

    我正在用一些可以让我更好地自定义 UI 的东西替换我的属性网格 我在表单上放置了一个按钮 我希望单击该按钮时会弹出一个 CollectionEditor 并允许我修改我的代码 当我使用 PropertyGrid 时 我所需要做的就是向指向我
  • 使用 Flexbox 在具有共享标题的两列布局中拉伸列

    我正在使用 Flexbox 创建带有标题行的两列布局 box sizing border box position relative container border 2px solid gray display flex flex wra
  • VBA - 将 SAPI 语音保存到给定的文件类型?

    My Task 可以在 Office 应用程序中使用语音 我的目标是将 MS SAPI 语音保存为给定的文件类型 AFAIK 我的代码示例保存到 WAV 文件 Problem 我不知道是否可以仅定义所需的文件类型扩展名 或者是否有必要进行一
  • 来自 Qt C++ 应用程序的倍频程图

    我有一个 QT C 应用程序 它使用 QProcess 运行 Octave 程序 我可以通过读取标准输出 错误并使用 write 方法写入其标准输入 例如 octave gt write 5 5 n 来与它进行通信 正如我告诉你的 我得到了
  • 屏幕方向更改时旋转视图(但不是布局)

    我想旋转按钮 文本视图 等 屏幕方向发生变化 但我想保持布局不变 如何做呢 我正在使用线性布局 This is what I mean Create res gt layout gt layout land并将你的 xml 文件放入横向 模
  • 要避免的 jQuery 陷阱 [关闭]

    就目前情况而言 这个问题不太适合我们的问答形式 我们希望答案得到事实 参考资料或专业知识的支持 但这个问题可能会引发辩论 争论 民意调查或扩展讨论 如果您觉得这个问题可以改进并可能重新开放 访问帮助中心以获得指导 我正在用 jQuery 启
  • dblookupcombobox 有空行

    我有一个关于 DBLookupComboBox 的问题 我有一个程序 其中有我编写的数据库 它拥有一切 除了当我打开 DBLookupComboBox 时 它必须有一行带有空值 因为当用户不想选择任何内容时 但没有一个 如何让空行显示出来
  • Kotlin 中是否有适用于可序列化类型的接口?

    我想创建一个大致如下所示的类 class MyWrapperClass
  • 如何控制推送通知中按钮的功能?

    我能够向 iOS 设备发送推送通知 通知上有一个 关闭 按钮和一个 查看 按钮 当用户点击 查看 按钮时 应用程序将打开根视图控制器 该应用程序内有一个新闻部分 假设该通知是为了提醒用户有新的新闻报道可供他们查看 如果他们点击 查看 将显示
  • 每当 Android 派中的应用程序被杀死时,服务也会被杀死

    我正在通过创建 Android 应用程序来学习 Android 编程 但是每当我杀死应用程序服务时也会被杀死 我在用着JobIntentService 使该应用程序在后台运行 工作意图服务类 public class BackGroundD
  • Windows 服务中的 TCP IP 侦听器

    我正在尝试创建一个需要在后台运行并侦听传入流量的 Windows 服务 正常且常规的 TCP 侦听器 我的代码是 private TcpListener server public void startServer EventLog Wri
  • 如何使 razor 成为现有项目中的默认视图引擎

    我将 MVC 2 项目升级到 MVC 3 如何在现有项目上将默认视图引擎设置为 Razor 编辑 抱歉 我不太清楚 我希望 Razor 成为 添加视图 对话框中的默认类型 简短回答 更改 global asax 以同时使用 Webforms
  • 通讯Arduino-C++不读Arduino

    我有以下代码 QSerialPort arduPort COM5 arduPort setBaudRate QSerialPort Baud9600 arduPort setDataBits QSerialPort Data8 arduPo
  • Push_back() 导致程序在进入 main() 之前停止

    我正在为我的 STM32F3 Discovery 板使用 C 进行开发 并使用 std deque 作为队列 在尝试调试我的代码 直接在带有 ST link 的设备上或在模拟器中 后 代码最终在 main 中输入我的代码之前在断点处停止 然
  • 为什么在 Angular 中使用 $http 而不是 jquery 的 ajax?

    我不明白何时使用 Angular 而不是 jquery 来处理 ajax 请求 例如 我为什么要使用 function ItemListCtrl scope http http get example com items success f
  • Google 地图路线 - 哪个 API?

    我正在尝试获取从用户当前位置到我正在构建的应用程序中用户定义位置的路线 这看起来应该是一件相对容易的事情 但我在使用哪个 API 上遇到了困难 现在我已经成功连接到谷歌路线API但它返回的 JSON 非常奇怪 他们在各处添加了 n 以使其易
  • C 与 C++ 中的编译器将未使用的符号包含在目标文件中

    这可能是一个愚蠢的问题 但也许有人可以提供一些见解 我在头文件中定义了一些全局变量 是的 我知道这很糟糕 但这只是假设的情况 我将此头文件包含在两个源文件中 然后将它们编译为两个目标文件 代码中的任何地方都没有引用全局符号 如果源文件是 C