Why would I want this?
I'd like to use a C package that was initially built in 2007 and last updated in 2016 according to the Changelog. I gather that it would have compiled cleanly back than.
Sadly, this is no longer the case.
The error
Running ./configure
and make
, I get a Multiply defined
error:
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
Specifically, both files (laplaafit.c
and common.c
) have the declaration
double *Data; /*the array of data*/
unsigned Size;/*the number of data*/
with a definition of both variables following further down in the code in both files (I believe with load(&Data,&Size,infile);
which calls function int load()
in common.c
which reads the array *Data
from a file and determines its length Size
).
This is what causes the error. The variables are important in both files (removal in either leads to '(variable)' undeclared
errors). Moving to a header files would not change anything if the header (say common.h
) is included in both .c
files.
Edit: Since it was raised in the comments that load(&Data,&Size,infile);
is "far from being a definition" I figure I should be a bit more detailed.
load(&Data,&Size,infile);
calls the int load(...)
function from common.c
int load(double **data,unsigned *size,FILE *input)
Here, *Data
is the array starting in address Data
. &Data
is the pointer to the pointer (double pointer?) to the start of the array. **data
is a double pointer to a local array in load()
. If the function obtains &Data
for this, data
actually refers to the original global array and the program gan write into it by accessing it via pointer *data
.
And *size
(for which the function obtains &Size
) is the value in address &Size
so the other global variable.
The function then writes into *data
and *size
multiple times, e.g., in the very end:
*size=i;
*data = (double *) my_realloc((void *) *data,(*size)*sizeof(double));
If I am not mistaken, this may count as the global variables *Data
and Size
being defined.
Furthermore, the comment says that I do not actually know enough C to diagnose the program and that I should therefore rather hire someone who does. This would raise the bar for being allowed to post in Stackoverflow to a very high level; a level that is not always attained in the questions that are commonly posted and seen as perfectly acceptable. It may actually be a reasonable suggestion, but it would leave no place for me to ask questions I might have about C or any other language. If the author of the comment is serious about this, it may be worth posting in Meta and suggesting splitting Stackoverflow in two, one for experts, one for everyone else.
How to solve the problem (making the code compile)
As I see it, there are two ways to approach this:
It is also still possible that I misinterpret the error or misunderstand something.
It is also possible that even between 2007 and 2016 this would not have compiled cleanly with my compiler (gcc) and that the authors used a different compiler that accepts multiple definitions.
Solution by compiling with old compiler behavior
Include the -fcommon
option as discussed in kaylum's answer below.
Attempt to solve by changing the code
The intended behavior is obviously for the two variables Data
and Size
in the two files to refer to the same variable (the same point in memory). Therefore, declaring the variable as extern
in laplaafit.c
should recover the same behavior. Specifically, exchanging
double *Data; /*the array of data*/
unsigned Size;/*the number of data*/
for
extern double *Data; /*the array of data*/
extern unsigned Size;/*the number of data*/
The code compiles cleanly then again. I am not sure how certain I am that the bahavior is actually the same as intended by the authors (and achieved with old gcc
versions and recent gcc
with -fcommon
) though.
Why I think this question is of general interest for programming (and this belongs on Stackoverflow)
I am guessing, however, that the question is more general. There are many old software packages around. Given enough time, most of them will break eventually.
Software
My system is Arch Linux kernel 5.11.2; C compiler: gcc 10.2.0; GNU Make 4.3.
Multiple definitions of global variables of the same type with the same name are permitted in gcc if the source is built with -fcommon
. From the gcc manual:
The -fcommon places uninitialized global variables in a common block. This allows the linker to resolve all tentative definitions of the same variable in different compilation units to the same object, or to a non-tentative definition. This behavior is inconsistent with C++, and on many targets implies a speed and code size penalty on global variable references. It is mainly useful to enable legacy code to link without errors.
The default of pre-10 gcc used to be -fcommon
but that has been changed to -fno-common
in gcc 10. From the gcc 10 release notes:
GCC now defaults to -fno-common. As a result, global variable accesses are more efficient on various targets. In C, global variables with multiple tentative definitions now result in linker errors. With -fcommon such definitions are silently merged during linking.
This explains why the build fails in your environment using gcc 10 but was able to build with older gcc versions. Your options are to either add -fcommon
into the build or use a gcc version prior to 10.
Or as pointed out by @JohnBollinger another option is to fix the code to remove those multiple definitions and make the code conform strictly to the C standard.