2

Here I have a small program:

// header.h
#pragma once
int a;
int reta();

// extra.c
#include "header.h"
int reta() {
    return a;
}

// main.c
#include "header.h"
#include <stdio.h>
int main(void) {
    printf("%d, %d", a, reta());
}

Result when compiling with Visual Studio (C standard: c17, Debug x64):

0, 0

Result when compiling with GCC (gcc -L. -o test.exe *.c -std=c17 -mwindows -Wl,-subsystem,console):

C:/msys64/ucrt64/bin/../lib/gcc/x86_64-w64-mingw32/13.2.0/../../../../x86_64-w64-mingw32/bin/ld.exe: C:...\Temp\ccGvFOtk.o:main.c:(.bss+0x0): multiple definition of `a'; C:...\Temp\ccANU1lv.o:extra.c:(.bss+0x0): first defined here collect2.exe: error: ld returned 1 exit status

My question: why is there a difference, and what should I do to fix the error in GCC case?

5
  • 1
    You are compiling two .c files, main.c and extra.c, and then linking them. Both define a Change header.h to extern int a; and do the definition in one of the C files. Commented Apr 9, 2024 at 14:59
  • 1
    "what should I do to fix the error" - never define a variable in a H file. Only declarations should go there (unless you have some special cases and know exactly what you are doing). A variable should be defined in a single source. If you want to access it from other sources, you declare it as extern. Commented Apr 9, 2024 at 14:59
  • @stark, ok, but why msvc compiles it without errors? Commented Apr 9, 2024 at 15:03
  • 1
    See stackoverflow.com/q/3663599/1216776 Commented Apr 9, 2024 at 15:06
  • 1
    See the discussion in How do I use extern to share variables between source files?, especially the section on "Not so good way to define global variables". GCC 10.1 changed the rules to conform more accurately to the C standard, electing not to take the option allowed by Annex J as a 'common extension' by default. You are seeing a consequence of that decision. Commented Apr 9, 2024 at 23:21

1 Answer 1

2

In your header file, you have a tentative definition of a. This means that both of your .c files that include this header will have a definition of a. What makes the definition tentative is the fact that it's not explicitly initialized.

Some compilers, like MSVC in this case, will consolidate multiple tentative definitions into a single definition. The version of gcc you're using however does not, which is why you're getting a multiple definition error. Had you initialized a in the header file, it would be a full definition and both compilers would likely have given an error.

The proper way to handle this is to declare a in the header file and define it in exactly one source file.

// header.h
#pragma once
extern int a;        // declaration of a
int reta();

// extra.c
#include "header.h"

int a;               // definition of a

int reta() {
    return a;
}

// main.c
#include "header.h"
#include <stdio.h>
int main(void) {
    printf("%d, %d", a, reta());
}
Sign up to request clarification or add additional context in comments.

4 Comments

Isn't the MSVC behavior not standard in this case (or is in the area of UB)? As far as I understand tentative definition becomes a regular definition if there are no more definitions in the same translation unit?
So variables can exist in 3 ways: declaration, definition and initialisation?
@ItzYurix A variable can be either defined or declared. A definition can optionally include an initializer.
@EugeneSh.: Both GCC and MSVC behaviors conform to the C standard, which does not define the behavior when there are multiple external definitions of an object, whether regular or stemming from tentative definitions.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.