用现代的GCC编译抛出错误(多重定义)的旧C代码(在LINUX上)? [英] Compile old C code (on Linux) that throws errors (Multiply defined) with modern gcc?
问题描述
我为什么要这样?
我想使用C package,根据ChangeLog,它最初构建于2007年,最后一次更新是在2016年。我猜想它会被干净地编译回来。
遗憾的是,情况不再是这样了。
错误
运行./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);
调用common.c
中的函数int load()
,它从文件中读取数组*Data
并确定其长度Size
)。
这就是导致错误的原因。变量在这两个文件中都很重要(删除其中任何一个文件都会导致'(variable)' undeclared
错误)。如果头文件(例如common.h
)包含在两个.c
文件中,则移动到头文件不会更改任何内容。
编辑:由于评论中提到load(&Data,&Size,infile);
远远不是一个定义,我想我应该更详细一点。
load(&Data,&Size,infile);
从common.c
int load(...)
函数
int load(double **data,unsigned *size,FILE *input)
这里,*Data
是从地址Data
开始的数组。&Data
是指向指针的指针(双指针?)指向数组的开头。**data
是指向load()
中的本地数组的双指针。如果函数为此获取&Data
,则data
实际上是指原始全局数组,程序可以通过指针*data
访问的方式写入。
和*size
(函数获取&Size
)是地址&Size
中的值,因此另一个全局变量也是如此。
该函数然后多次写入*data
和*size
,例如在最后:
*size=i;
*data = (double *) my_realloc((void *) *data,(*size)*sizeof(double));
如果我没记错的话,这可以算作定义了全局变量*Data
和Size
。
如何解决问题(编译代码)
在我看来,有两种方法可以解决此问题:
- 重写软件包,避免多个定义。理想情况下,我希望避免这种情况。
- 想办法编译,因为它是在2007到2016年间编译的。我想那时候它应该已经编译得很干净了。这有多个潜在的问题:旧的编译器还能在我的2021系统上工作吗?这对现代系统中的图书馆有效吗?即使我成功了,生成的可执行文件是否会按照作者的预期运行?不过,这似乎是更可取的选择。
仍有可能是我误解了错误或误解了某些内容。
也有可能即使在2007年到2016年间,使用我的编译器(GCC)也不能很好地编译,并且作者使用了接受多个定义的不同编译器。
使用旧编译器行为编译的解决方案
包括kaylum's answer below中讨论的-fcommon
选项。
尝试通过更改代码来解决
显然,预期的行为是让两个文件中的两个变量Data
和Size
引用同一变量(内存中的同一点)。因此,在laplaafit.c
中将变量声明为extern
应该会恢复相同的行为。具体地说,交换
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*/
代码完全编译,然后再次编译。不过,我不能确定自己的行为是否真的与作者的预期相同(旧的gcc
版本和最近的-fcommon
版本都实现了)。
为什么我认为此问题对编程有普遍意义(此问题属于Stackoverflow)
然而,我猜这个问题更一般。周围有很多旧的软件包。如果有足够的时间,它们中的大多数最终都会破裂。
软件
我的系统是Arch Linux kernel 5.11.2;C编译器:GCC 10.2.0;GNU make 4.3.
推荐答案
如果源是用-fcommon
构建的,GCC允许多个定义相同类型的同名全局变量。来自gcc manual:
-fcommon将未初始化的全局变量放置在公共挡路中。这允许链接器将不同编译单元中同一变量的所有暂定定义解析为同一对象,或解析为非暂定定义。此行为与C++不一致,并且在许多目标上意味着全局变量引用上的速度和代码大小损失。它主要用于使旧式代码能够无错误地链接。
GCC 10之前的默认值是-fcommon
,在GCC 10中默认为-fno-common
,从gcc 10 release notes开始:
GCC现在默认为-fno-common。因此,对各种目标的全局变量访问效率更高。在C语言中,具有多个试探性定义的全局变量现在会导致链接器错误。使用-fcommon时,此类定义在链接期间以静默方式合并。
这解释了为什么在使用GCC 10的环境中构建失败,但可以使用较旧的GCC版本构建。您的选择是将-fcommon
添加到内部版本中,或者使用10之前的GCC版本。
或者正如@JohnBollinger指出的那样,另一个选项是修复代码以删除那些多个定义,并使代码严格符合C标准。
这篇关于用现代的GCC编译抛出错误(多重定义)的旧C代码(在LINUX上)?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!