75

I have a 2 modules (.c files) and one .h header file:

file1.c:

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

int main()
{
    i = 100;
    printf("%d\n",i);
    foo();
    return 0;
}

file2.c

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

void foo()
{
    i = 10;
    printf("%d\n",i);
}

global.h

int i;
extern void foo()

When I do gcc file1.c file2.c everything works fine and I get the expected output. Now, when I initialize variable 'i' in the header file to say 0 and compile again I get a linker error:

/tmp/cc0oj7yA.o:(.bss+0x0): multiple definition of `i'
/tmp/cckd7TTI.o:(.bss+0x0): first defined here

If I just compile file1.c (removing call to foo()) with the initialization in the header file i.e. gcc file1.c, everything works fine. What is going on?

Coder
  • 1,415
  • 2
  • 23
  • 49
Bruce
  • 33,927
  • 76
  • 174
  • 262

6 Answers6

72

There are 3 scenarios, you describe:

  1. with 2 .c files and with int i; in the header.
  2. With 2 .c files and with int i=100; in the header (or any other value; that doesn't matter).
  3. With 1 .c file and with int i=100; in the header.

In each scenario, imagine the contents of the header file inserted into the .c file and this .c file compiled into a .o file and then these linked together.

Then following happens:

  1. works fine because of the already mentioned "tentative definitions": every .o file contains one of them, so the linker says "ok".

  2. doesn't work, because both .o files contain a definition with a value, which collide (even if they have the same value) - there may be only one with any given name in all .o files which are linked together at a given time.

  3. works of course, because you have only one .o file and so no possibility for collision.

IMHO a clean thing would be

  • to put either extern int i; or just int i; into the header file,
  • and then to put the "real" definition of i (namely int i = 100;) into file1.c. In this case, this initialization gets used at the start of the program and the corresponding line in main() can be omitted. (Besides, I hope the naming is only an example; please don't name any global variables as i in real programs.)
glglgl
  • 89,107
  • 13
  • 149
  • 217
  • 11
    What I said about "put either `extern int i;` or just `int i;` into the header file": `extern int i` is better because it immediately tells you if the "real" definition is missing by some accident. With a mere `int i`, there would be a silent definition to `0`. – glglgl Nov 13 '11 at 12:06
  • i didn't get the first explanation can you elaborate more as when memory is allocated to variable i. till now what i understand is int i in global.h is equivalent to extern int i; which means both object file has the reference that memory to i which is allocated somewhere else. – user Feb 11 '15 at 07:12
  • 2
    @user2383973 The memory is allocated and reserved by the linker. When it sees one request with assignment and one "tentative" definition, all is fine. When it sees several assignments, even if they have the same value, it is not ok. – glglgl Feb 11 '15 at 12:16
  • @glglgl. What If I put #ifndef in the header and declare the variable `int a` in the header? Multiple definition error is still coming although now I'm just having only one copy of the header file. – CKM Oct 14 '16 at 09:55
  • @Assimilater The question was about C where it works as described. For using a C source file/header file in C++, some more care has to be taken. – glglgl Oct 14 '16 at 10:59
  • @chandresh You *always* have only copy of the header file. A .c file is never included and always compiled once, so `int i;` happens only once. A .h file can be included several times, but several `extern int i;` won't hurt. Nevertheless, an include guard in the header file is never a bad idea. – glglgl Oct 14 '16 at 11:00
  • @glglgl. My concern is that despite include guard, multiple definition error is still there. Then why include guard does not prevent that? – CKM Oct 14 '16 at 11:57
  • @chandresh Maybe you misimplemented the include guard, or something else is wrong. Hard to tell from remote. – glglgl Oct 14 '16 at 12:01
  • @glglgl. Pl download the files from here to see yourself [link](https://mega.nz/#F!pcdggCAA!ts7ci9hmZl00lcFaggbQag) – CKM Oct 14 '16 at 12:23
  • @chandresh Because you have the `int g = 0;` in the header file. Besides, the thing you do is C++ and not C. – glglgl Oct 14 '16 at 12:27
  • @glglgl. Thanks. It worked. Strange thing is that include guard did not work in C++ wheather `int g = 0` or `int g`. It's always an error. – CKM Oct 14 '16 at 12:35
  • why when moving the usage of i in file1.c to out of the function's scope (the global scope) I get : file1.c:4:1: warning: type specifier missing, defaults to 'int' [-Wimplicit-int] – Foad Rezek May 30 '18 at 07:34
  • 1
    @FoadRezek I assume you tried the file structure in the question. That is because assigments are not valid on file level, only inside functions. – glglgl May 30 '18 at 08:40
  • Does adding `#ifndef GLOBAL_H #define GLOBAL_H` `#endif` not work ? – twitu Apr 03 '19 at 05:28
  • 2
    case 1 is undefined behaviour, the tentative definition causes an external definition to be generated for each translation unit it appears in . (C11 6.9.2/2). Your recommendation without `extern` has the same problem – M.M Feb 07 '20 at 05:13
53

Don't initialize variables in headers. Put declaration in header and initialization in one of the c files.

In the header:

extern int i;

In file2.c:

int i=1;
Piotr Praszmo
  • 17,928
  • 1
  • 57
  • 65
14

You should not define global variables in header files. You can declare them as extern in header file and define them in a .c source file.

(Note: In C, int i; is a tentative definition, it allocates storage for the variable (= is a definition) if there is no other definition found for that variable in the translation unit.)

M.M
  • 138,810
  • 21
  • 208
  • 365
ninjalj
  • 42,493
  • 9
  • 106
  • 148
  • 2
    What If I put #ifndef in the header and declare the variable `int a` in the header? Multiple definition error is still coming although now I'm just having only one copy of the header file. – CKM Oct 14 '16 at 09:54
7

The currently-accepted answer to this question is wrong. C11 6.9.2/2:

If a translation unit contains one or more tentative definitions for an identifier, and the translation unit contains no external definition for that identifier, then the behavior is exactly as if the translation unit contains a file scope declaration of that identifier, with the composite type as of the end of the translation unit, with an initializer equal to 0.

So the original code in the question behaves as if file1.c and file2.c each contained the line int i = 0; at the end, which causes undefined behaviour due to multiple external definitions (6.9/5).

Since this is a Semantic rule and not a Constraint, no diagnostic is required.

Here are two more questions about the same code with correct answers:

M.M
  • 138,810
  • 21
  • 208
  • 365
3

@glglgl already explained why what you were trying to do was not working. Actually, if you are really aiming at defining a variable in a header, you can trick using some preprocessor directives:

file1.c:

#include <stdio.h>

#define DEFINE_I
#include "global.h"

int main()
{
    printf("%d\n",i);
    foo();
    return 0;
}

file2.c:

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

void foo()
{
    i = 54;
    printf("%d\n",i);
}

global.h:

#ifdef DEFINE_I
int i = 42;
#else
extern int i;
#endif

void foo();

In this situation, i is only defined in the compilation unit where you defined DEFINE_I and is declared everywhere else. The linker does not complain.

I have seen this a couple of times before where an enum was declared in a header, and just below was a definition of a char** containing the corresponding labels. I do understand why the author preferred to have that definition in the header instead of putting it into a specific source file, but I am not sure whether the implementation is so elegant.

Nicolas Garnier
  • 436
  • 3
  • 9
2

Dont define varibale in header file , do declaration in header file(good practice ) .. in your case it is working because multiple weak symbols .. Read about weak and strong symbol ....link :http://csapp.cs.cmu.edu/public/ch7-preview.pdf

This type of code create problem while porting.

  • 2
    what are u trying to say – tod Feb 17 '15 at 09:09
  • @tod. Not the best worded answer, but if you want to know what he means by definition / declaration, here's a well-formed answer of what you *should* do http://stackoverflow.com/a/1164190/310560 – Assimilater May 31 '16 at 17:03