如何解决使用现代 gcc 编译抛出错误多重定义的旧 C 代码在 Linux 上?
我为什么要这个?
我想使用最初于 2007 年构建并根据更新日志于 2016 年更新的 C package。我认为它会编译干净。
遗憾的是,情况不再如此。
运行 ./configure
和 make
,出现 Multiply defined
错误:
gcc -g -O2 -o laplaafit laplaafit.o multimin.o common.o lib/libgnu.a -lgsl -lgslcblas -lm
/usr/bin/ld: common.o:/home/<user>/build/subbotools/subbotools-1.3.0/common.c:27: multiple deFinition of `Size'; laplaafit.o:/home/<user>/build/subbotools/subbotools-1.3.0/laplaafit.c:38: first defined here
/usr/bin/ld: common.o:/home/<user>/build/subbotools/subbotools-1.3.0/common.c:26: multiple deFinition of `Data'; laplaafit.o:/home/<user>/build/subbotools/subbotools-1.3.0/laplaafit.c:37: first defined here
具体来说,两个文件(laplaafit.c
和 common.c
)都有声明
double *Data; /*the array of data*/
unsigned Size;/*the number of data*/
在两个文件的代码中进一步定义了两个变量(我相信 load(&Data,&Size,infile);
调用函数 int load()
在 common.c
中读取数组 *Data
并确定其长度 Size
)。
这就是导致错误的原因。变量在两个文件中都很重要(在任一文件中删除都会导致 '(variable)' undeclared
错误)。如果头文件(例如 common.h
)包含在两个 .c
文件中,移动到头文件不会改变任何内容。
编辑:由于在评论中提出 load(&Data,infile);
“远非定义”,我想我应该更详细一些。
load(&Data,infile);
从 int load(...)
common.c
函数
int load(double **data,unsigned *size,FILE *input)
这里, *Data
是从地址 Data
开始的数组。 &Data
是指向数组开头的指针(双指针?)的指针。 **data
是指向 load()
中的局部数组的双指针。如果函数为此获得&Data
,则data
实际上是指原始全局数组,程序可以通过指针访问它来写入它 *data
。
And *size
(函数获取&Size
)是地址&Size
中的值,所以另一个全局变量。
函数然后多次写入 *data
and *size
,例如,最后:
*size=i;
*data = (double *) my_realloc((void *) *data,(*size)*sizeof(double));
如果我没记错的话,这可能算作全局变量 *Data
和Size
被定义了。
此外,评论说我实际上并没有足够的 C 知识来诊断程序,因此我应该雇用一个这样做的人。这会将允许在 Stackoverflow 中发帖的门槛提高到一个非常高的水平;在通常张贴并被视为完全可以接受的问题中并不总是达到的水平。这实际上可能是一个合理的建议,但它不会让我有机会提出有关 C 或任何其他语言的问题。如果评论的作者对此是认真的,那么可能值得在 Meta 上发帖并建议将 Stackoverflow 分成两部分,一个给专家,一个给其他人。
- 重写软件包避免多重定义。理想情况下,我希望避免这种情况。
- 找到一种编译方式,因为它本来是在 2007 年到 2016 年之间编译的。我认为当时它会编译得很干净。这有多个潜在问题:旧编译器是否仍适用于我的 2021 系统?这可以与现代系统中的图书馆一起使用吗?即使我成功了,生成的可执行文件是否会像作者预期的那样运行?不过,这似乎是更可取的选择。
也有可能我误解了错误或误解了某些东西。
也有可能即使在 2007 年到 2016 年之间,我的编译器 (gcc) 也无法完全编译,并且作者使用了接受多个定义的不同编译器。
通过使用旧的编译器行为进行编译的解决方案
包括 kaylum's answer below 中讨论的 -fcommon
选项。
预期行为显然是让两个文件中的两个变量 Data
和 Size
引用同一个变量(内存中的同一点)。因此,在 extern
中将变量声明为 laplaafit.c
应该恢复相同的行为。具体来说,交换
double *Data; /*the array of data*/
unsigned Size;/*the number of data*/
为了
extern double *Data; /*the array of data*/
extern unsigned Size;/*the number of data*/
代码再次干净地编译。不过,我不确定我对 bahavior 实际上与作者预期的相同(并且使用旧的 gcc
版本和最近的 gcc
使用 -fcommon
实现)的确定程度。
为什么我认为这个问题对编程具有普遍意义(这属于 Stackoverflow)
然而,我猜这个问题更笼统。周围有很多旧的软件包。如果有足够的时间,它们中的大多数最终都会破裂。
软件
我的系统是 Arch Linux 内核 5.11.2; C 编译器:gcc 10.2.0; GNU Make 4.3。
解决方法
如果源代码是用 -fcommon
构建的,则 gcc 中允许多个同类型同名全局变量定义。来自gcc manual:
-fcommon 将未初始化的全局变量放在公共块中。这允许链接器将不同编译单元中相同变量的所有暂定定义解析为同一个对象,或解析为非暂定定义。这种行为与 C++ 不一致,并且在许多目标上意味着全局变量引用的速度和代码大小损失。主要用于使遗留代码能够无错误地链接。
10 之前的 gcc 的默认值曾经是 -fcommon
,但在 gcc 10 中已更改为 -fno-common
。来自 gcc 10 release notes:
GCC 现在默认为 -fno-common。因此,全局变量访问在各种目标上更有效。在 C 中,具有多个暂定定义的全局变量现在会导致链接器错误。使用 -fcommon 时,此类定义会在链接期间以静默方式合并。
这解释了为什么在您的环境中使用 gcc 10 构建失败,但能够使用较旧的 gcc 版本构建。您的选择是将 -fcommon
添加到构建中或使用 10 之前的 gcc 版本。
或者正如@JohnBollinger 所指出的,另一种选择是修复代码以删除这些多重定义并使代码严格符合 C 标准。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。