2

I got "multiple definition of" error on Ubuntu 22.04 though I have not got such kind of error on Ubuntu 20.04. I put include guards on the top of the header file but it does not work. Does anyone know how to solve this problem other than downgrading to Ubuntu 20.04?

I have these three files

global.h

#ifndef GLOBAL_H
#define GLOBAL_H

int a;
int b;


#endif

a.c

#include "global.h"
#include <stdio.h>
#include<unistd.h>

void defb(void);

int main(int argc, char **argv)
{
  defb();
  a = 1;
  printf("a=%d\n",a);
  return 0;
}

b.c

#include "global.h"
#include <stdio.h>
#include<unistd.h>


void defb()
{
  b = 2;
  printf("b=%d\n",b);
}

And I compiled with gcc like

gcc -O a.c b.c

then I got the error below

/usr/bin/ld: /tmp/ccOgmibV.o:(.bss+0x0): multiple definition of `b'; /tmp/ccsYu4Ch.o:(.bss+0x0): first defined here
/usr/bin/ld: /tmp/ccOgmibV.o:(.bss+0x4): multiple definition of `a'; /tmp/ccsYu4Ch.o:(.bss+0x4): first defined here
collect2: error: ld returned 1 exit status

How to resolve this probelem? Thank you in advance.

  • 1
    You got a newer version of GCC withthe new Ubuntu. Defining variables or functions in a headers is normally a bad idea. Just place a declaration there and define the variable in one (!) C file. – Gerhardh Apr 20 '23 at 05:42
  • Thank you for your comment! But I have some complicated C codes with many functions divided into source files with the same variables declared in the header file. Therefore, I would like to avoid using only one C file. How should I deal with them in this case? – user1602885 Apr 20 '23 at 07:24
  • 1
    You don't have to put all variables into one C file. You should place each variable in only one C file. – Gerhardh Apr 20 '23 at 07:25
  • Do you mean no header file is better in the new version of GCC? Then, could you tell me the purpose of header files for now? – user1602885 Apr 20 '23 at 07:41
  • 1
    Add `-fcommon` to the compilation switches. – Eric Postpischil Apr 20 '23 at 07:51
  • https://stackoverflow.com/q/1164167/4788546. There's a lot of info on this topic. One should only *Google* it. – CristiFati Apr 20 '23 at 07:51
  • You should read more carefully what I wrote. I did not ask you to remove everything from your headers. Maybe you need to read on the difference between definitions and declarations. Any guide about splitting projects in multiple files should cover that. – Gerhardh Apr 20 '23 at 07:59
  • In **global.h**, change `int a;` to `extern int a;` and `int b;` to `extern int b;`, then add each of `int a;` and `int b;` to a .c file. (Not necessarily the same .c file. E.g. add `int a;` to **a.c** and add `int b;` to **b.c**.) – Ian Abbott Apr 20 '23 at 09:26

1 Answers1

3

GCC’s default behavior changed in version 10. You can get the old behavior by adding the switch -fcommon to compilation options.

Outside of a function, int a; is a tentative definition. In spite of its name, it is not actually a definition. However, if there is no definition for an identifier in a translation unit, the presence of a tentative definition will cause a definition to be created.

When a tentative definition or a definition for an identifier appears in a header file that is included in multiple translation units, that results in there being multiple definitions for the identifier. The C standard does not define the behavior when there are multiple definitions for an identifier.

Before version 10, GCC’s default behavior was to mark a definition resulting from a tentative definition as a “common” symbol, and Unix linkers coalesce multiple definitions of a “common” symbol to a single definition and do not produce an error message for it. In version 10, GCC’s default behavior changed to mark a definition resulting from a tentative definition as an ordinary symbol, and linkers treat multiple definitions of an ordinary symbol as an error.

Using -fcommon when compiling requests the old behavior.

You can also change tentative definitions to ordinary declarations by inserting extern. Changing int a; to extern int a; makes it a plain declaration that is not a tentative definition, so it will not cause a definition to be created. If you make this change, you need to ensure that one definition for each such symbol is present in the sources. So you would need to add int a; to one source file if it does not already exist.

Further information is in this answer and this one.

Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312