提问人:Frotz 提问时间:1/2/2023 更新时间:1/3/2023 访问量:81
2020 年,项目历史记录中出现了多个定义错误。现在,如果不添加“extern”,就无法构建该点之前的提交
Multiple definitions errors showed up in project history in 2020. Now no commit before that point can be built without adding "extern"
问:
自 2000 年以来,我一直在做的一个项目编译得很好,但会出现短暂的问题,一次可能有两到三个提交,这种情况非常罕见。早在 2020 年 2 月,构建就失败了,如下所示:
/usr/bin/ld: src/dumb/frotz_dumb.a(dinit.o):/home/foobar/src/dumb/dinit.c:26: multiple definition of `f_setup'; src/common/frotz_common.a(object.o):/home/foobar/src/common/object.c:23: first defined here
通过更改访问结构的源文件中的 to 实例,可以轻松修复它。很简单,对吧?但后来我回到了 2011 年,当时我终于开始在这个项目中使用 Git。编译失败的方式几乎相同:f_setup_t f_setup;
extern f_setup_t f_setup;
f_setup
gcc -O2 -DCONFIG_DIR="\"/usr/local/etc\"" -DVERSION="\"2.43\"" -DSOUND_DEV="\"\"" -DCOLOR_SUPPORT -o src/dumb/dumb_input.o -c src/dumb/dumb_input.c
src/dumb/dumb_input.c:82:13: error: conflicting types for ‘getline’
82 | static void getline(char *s)
在过去的两年里,我对此感到沮丧,因为,好吧,它现在构建和运行良好。但是现在我正在处理有人提交的问题以及两个构建日志:一个来自最新的提交,一个来自提交我上面描述的简单修复之前的提交。因此,我尝试使用新用户,然后是新的 Debian 机器,然后是新的 FreeBSD 机器来构建这些较旧的提交。我没有 macOS 计算机,而这正是问题提交者正在使用的。这个问题严重影响了我进行二分法以根除麻烦的能力。
我真的真的真的不想从 2011 年开始在这里和那里插入修复程序,从而弄得一团糟。我对发生的事情的唯一猜测是 2020 年左右 Debian 在 GCC 中发生了某种默认更改。所以。。。这到底是怎么回事?
我说的项目在这里:https://gitlab.com/DavidGriffith/frotz/。简单的修复发生在 https://gitlab.com/DavidGriffith/frotz/-/commit/b001e3f64a0f223136babb228f30fcac7fe09804
答:
C 标准没有定义在多个翻译单元中使用的行为。这种形式的声明被称为暂定定义,尽管它实际上不是一个定义。但是,如果在翻译单元结束之前没有看到任何定义,则会导致创建定义。f_setup_t f_setup;
由于 C 开发的历史,这种形式的声明由不同的 C 实现以不同的方式处理。这是一个有记录的Unix行为,即由创建“通用”符号的暂定定义创建的定义在链接过程中被合并(同一“通用”符号的多个实例将链接到对单个实体的引用)。因此,在头文件中使用这些声明将起作用,而不会导致链接器错误。在其他 C 实现中,暂定定义将创建硬引用,从而导致有关多个定义的链接器错误。由于不同的 C 实现以不同的方式对待它们,因此 C 标准不需要一种或另一种行为;它使它未定义。
在 GCC 版本 10 之前,GCC 默认将暂定定义标记为“通用”符号。GCC 版本 10 中的默认值已更改。
要解决此问题,可以使用早于版本 10 的 GCC 版本,可以添加到编译命令中以选择旧行为,也可以添加到声明中,使它们成为不是定义的普通声明,而不是临时定义。在后一种情况下,您需要确保程序中的某个位置只有一个标识符定义。-fcommon
extern
评论
git bisect
评论
-fcommon
-fno-common
extern
-fcommon
getline
stdio.h
-std=c17 -pedantic-errors