0

I've had the same problem as described in these two posts (First and Second) regarding declaration of variables in header files. The solution listed works well for me, but nonetheless I have a basic question on the solution:

Why would an include guard still not solve this issue? I would expect that the include guard would avoid my variable to be declared multiple times if I include the same header file multiple times.

ryyker
  • 22,849
  • 3
  • 43
  • 87
drdolphin
  • 117
  • 1
  • 6
  • Pretend you are the preprocessor. Now try "preprocess" a toy example with your solution yourself and see which problems arise. – Eugene Sh. May 25 '21 at 18:05
  • 2
    include guard avoids multiple includes in one compilation. when you link together separate compilations you get duplicate variable. – stark May 25 '21 at 18:10
  • 1
    Please [edit] your question and show a [mre]. The answers to the linked questions can only guess from the error message that you might have a *definition* of a variable (instead of a *declaration*) in a header file. Instead of describing parts of your code, show the code. Copy&paste the contents of minimal example files `a.c`, `a.h`, `b.c` and `b.h`, the error messages and warnings and, if possible, the commands used for compiling and linking your code. – Bodo May 25 '21 at 18:27
  • @Bodo I do not have a particular problem. As I said I was able to solve it with the linked questions. I just didn't quite understand why include guards do not prevent this kind of error for multiply defined variables. My question was just for clarification on the solution. – drdolphin May 26 '21 at 11:26
  • @drdolphin The lack of clarity in your question results from the fact that you omit details about "this kind of error" or "this issue". You write about a "variable to be *declared* multiple times" while I guess that you refer to a problem that occurs if the variable is *defined* multiple times. Adding example code would make this clear and thus improve the quality of the question. – Bodo May 26 '21 at 11:41
  • It probably feels a bit redundant to re-specify details from another question, but your situation is probably not the same (since if it was you'd be asking a duplicate question, which would be closed). So I would set out some basics in this question, even if you feel that you are repeating other material (don't forget those questions can still theoretically be deleted, and thus getting a question to stand alone is useful in its own right). – halfer Jun 11 '21 at 21:12
  • That all said, you can still link to other questions - just err on the side of self-contained clarity if you can. – halfer Jun 11 '21 at 21:13

3 Answers3

5

Include guards are useful for preventing multiple delcarations or type definitions in a single translation unit, i.e. a .c file that is compiled by itself along with all of the headers it includes.

Suppose you have the following headers without include guards:

a.h:

struct test {
    int i;
};

struct test t1;

b.h:

#include "a.h"

struct test *get_struct(void);

And the following main file:

main.c:

#include <stdio.h>
#include "a.h"
#include "b.h"

int main()
{
    struct test *t = get_struct();
    printf("t->i=%d\n", t->i);
    return 0;
}

When the preprocessor runs on the, the resulting file will look something like this (neglecting the contents of stdio.h):

struct test {
    int i;
};

struct test t1;

struct test {
    int i;
};

struct test t1;

struct test *get_struct(void);

int main()
{
    struct test *t = get_struct();
    printf("t->i=%d\n", t->i);
    return 0;
}

Because main.c includes a.h and b.h, and because b.h also includes a.h, the contents of a.h appear twice. This causes struct test to be defined twice which is an error. There is no problem however with the variable t1 because each constitutes a tentative definition, and multiple tentative definitions in a translation unit are combined to refer to a single object defined in the resulting main.o.

By adding include guards to a.h:

#ifndef A_H
#define A_H

struct test {
    int i;
};

struct test t1;

#endif

The resulting preprocessor output would be:

struct test {
    int i;
};

struct test *get_struct(void);

int main()
{
    struct test *t = get_struct();
    printf("t->i=%d\n", t->i);
    return 0;
}

Preventing the duplicate struct definition.

But now let's look at b.c which constitutes a separate translation unit:

b.c:

#include "b.h"

struct test *get_struct(void)
{
    return &t1;
}

After the preprocessor runs we have:

struct test {
    int i;
};

struct test t1;

struct test *get_struct(void);

struct test *get_struct(void)
{
    return &t1;
}

This file will compile fine since there is one definition of struct test and a tentative definition of t1 gives us an object defined in b.o.

Now we link a.o and b.o. The linker sees that both a.o and b.o contain an object called t1, so the linking fails because it was defined multiple times.

Note here that while the include guards prevent a definition from appearing more than once in a single translation unit, it doesn't prevent it from happening across multiple translation units.

This is why t1 should have an external declaration in a.h:

extern struct test t1;

And a non-extern declaration in one .c file.

dbush
  • 205,898
  • 23
  • 218
  • 273
  • The first example is bad as it breaks the most important rule : `Do not define data or functions (except static inline) in the header files. ` – 0___________ May 25 '21 at 22:01
  • 1
    @0___________ That's exactly the point. It illustrates why header guards don't protect against multiple definitions in different files. – dbush May 25 '21 at 22:14
  • Thank you for your well written answer! That also explains why it first was a compile error and turned into a linker problem when I implemented the include guards. – drdolphin May 26 '21 at 11:23
0

Never define data or functions (except static inline) in the header files. Always do it in the .c source files. If you want to make them visible in other compilation units declare them as extern in the header file.

Guards do not protect you if you include the same .h file in many compilation units which are then linked together.

0___________
  • 60,014
  • 4
  • 34
  • 74
0

The include guard will protect you of including several times the same include, it will make the definitions in the include to be handled only once, at the first inclusion point. This means the declarations you have made between the protecting marks not be repeated and so don't produce errors about double definition. This is not normally the case of

extern type_of_variable variable_name;

which you can make several times without any complaint from the compiler... it has more to do with type declarations or static functions implementations included in the header.

But why don't you post an example of full compilable code and show what are you trying, and why it doesn't work. From your question I cannot guess what you pretend to do, if something is not working in your case (well, you put references to other cases that probably will have answers, so why don't you use the answers there? what is wrong with the answers given there?)

Please, post a valid example of what is worrying you, and explain precisely why those other posts don't solve your problem (if you have one, at all)

Think that one of the questions doesn't show the actual contents of the repeated include, and doesn't show the actual variable definition. And the other is a question closed 8 years ago, and for some reason it has not been reopened. So you are running the same risk (and I do run the risk of being downvoted for this answer that doesn't actually answer your question, because you don't actually ask something is happening to you -I don't know from your question if you have an actual problem or not).

Luis Colorado
  • 10,974
  • 1
  • 16
  • 31
  • I think I agree with your assessment - your answer appears to be meta-commentary and not an answer. The first couple of paras of your answer are OK, but the rest would perhaps be best as comments under the question. In any case, I will see if I can close the question - it feels rather vague. – halfer Jun 11 '21 at 21:07