0

I have the following file structure:

X.h

#pragma once

#include "Y.h"

int ONE = 1;

int combine();

X.c

#include "X.h"

int combine()
{
    return ONE + TWO;
}

Y.h

#pragma once

int TWO = 2;

Y.c

#include "Y.h"

Main.c

#include "X.h"

int main()
{
    int fusion = combine();

    return 0;
}

I get the following errors:

LNK1169 one or more multiply defined symbols found

LNK2005 _ONE already defined in Main.obj

LNK2005 _TWO already defined in Main.obj

KLNK2005 _TWO already defined in Main.obj

This makes zero sense. If we start at Main.c the compiler must include X.h. The compiler then looks for the C file associated with X.h. Inside X.c it needs to include X.h but #pragma once SHOULD guard against that. Then it needs to include Y.h. It looks for the C file and finds Y.c which says to include Y.h, but Y.h has already been included. Then, it returns to Main.c and should successfully compile... but nope.

I can add /FORCE to my project settings which makes my code run perfectly but still outputs the:

ONE has already been defined, second definition ignored.

dave
  • 4,476
  • 1
  • 21
  • 38
Hatefiend
  • 2,965
  • 4
  • 21
  • 60
  • 3
    *The compiler then looks for the C file associated with X.h.* -- What?? Where did you come up with this? – PaulMcKenzie Aug 10 '16 at 02:11
  • Are you implying that adding `#include "X.c"` is necessary for all header files? (which `X` is the name of your header file) – Hatefiend Aug 10 '16 at 02:17
  • 2
    I don't know where to start unraveling all the things you are incorrect about w.r.t header files. You stated things that you thought happened, when none of those things happen. The compiler doesn't go searching other `C` modules to see what they've included. – PaulMcKenzie Aug 10 '16 at 02:20
  • Please, instead of mocking my misunderstanding, can you enlighten me about how I'm misinformed? – Hatefiend Aug 10 '16 at 02:22
  • 1
    @Hatefiend No. You tell the compiler which `.c` files to compile - either directly on the command line or via a build system like Make. The compiler does not automatically go looking for `.c` files based on header files. As for pragma once, it prevents multiple inclusion within a *single* source file. It does not prevent inclusion of the same header file from a *different* source file. The latter is what you have and is what is causing the multiple definition link errors. – kaylum Aug 10 '16 at 02:23
  • 1
    @Hatefiend It isn't mocking. You presented a scenario that is totally wrong and given that, would need to have an explanation from us starting from the beginning concerning the build process, header files, etc. – PaulMcKenzie Aug 10 '16 at 02:25
  • Read the first section of [How do I use `extern` to share variables between source files](http://stackoverflow.com/questions/1433204/). Your headers are _defining_ and not _declaring_ the variables because of the initializers, so each file that includes the header defines the variable. When you link separate object files that included the headers, the symbols collide. You simply mustn't define variables in a header if more than one source file in a program will include it — and the whole point of headers is to allow information to be shared. – Jonathan Leffler Aug 10 '16 at 02:30
  • 1
    In general, whenever you ask about compilation or linkage issues, it helps if you also provide info about platform and compiler, and anything that might be atypical about the build (e.g. symlinks). I seriously doubt it's the case here, pragma once is very common, but not strictly part of the standard, neither is how file recognition is implemented by the pre compile chain (e.g. if it uses file identities instead of parsing it it can be broken by symlinks, whereas ifdef guards won't be). –  Aug 10 '16 at 02:31
  • @JonathanLeffler in regards to your >read the first section comment, are you saying that I shouldn't necessarily put all instance variables in my `.h` files? By instance variable, I mean a variable that is used only in the source file it's defined in and is used in many different functions in that source file. I was under the impression that such a variable should always go in your `.h` file, even though you have no intention of another `.c` file ever using it. – Hatefiend Aug 10 '16 at 04:35
  • If the variables are used in a single source file and not outside that source file, then those variables should not be in _any_ header whatsoever and they should all be `static` so that no-one is enticed into using them. Anything private — anything that is not needed outside the current source file — should be static and not in a header. So, if you have a private structure type, that should be present only in the one source file and not in any header. – Jonathan Leffler Aug 10 '16 at 04:37
  • Conversely, if the variables are used in multiple source files, then they must be declared in a header, and that header should be used in the source file where the variables are defined, and it should be used in every source file that references the variables. And the variables declared in the header should be prefixed with `extern`. —— One side-effect of this is that you should never have variable declarations with an `extern` prefix in a source file. There should be a header that gives the declaration for the variable. – Jonathan Leffler Aug 10 '16 at 04:38
  • Similarly, there should never be an `extern` declaration of a function in a source file. Either the function should be declared in a header, or the function should be `static`. If you have to write a variable or function declaration (as opposed to definition) in a source file, your code is improperly factored. (There's an awful lot of code out there that is imperfect in this regard — but there's no harm in aiming for perfection. I've worked on ancient code bases where these rules were not followed; it makes life hell when you're trying to make what should be innocuous changes.) – Jonathan Leffler Aug 10 '16 at 04:42
  • Oh wow that makes a lot of sense. I actually thought that `static` just didn't work because my compiler would always say `static` keyword ignored but it's because I defined EVERYTHING in my header file. Something that is `static` but is also in a header file is counterproductive right? It's nonsensical? Also, I really don't want to declare every variable that I want other files to manipulate `extern`. Can't I just put header guards on every single `.h` file I have and be done with it? – Hatefiend Aug 10 '16 at 04:56

3 Answers3

5

but #pragma once SHOULD guard against that

#pragma once is used to avoid reduplicate including within single translation unit (.c file in this case), but it can't prevent multiple definition crossing multiple translation unit.

You're defining global variable ONE and TWO in .h file, and they're included in multiple translation unit and cause multiple definition error.

For example, finally, ONE will be defined in X.c and Main.c, TWO will be defined in Y.c, X.c and Main.c.

You should declare ONE and TWO in .h file (by using extern), and define them in .c file.

X.h

#pragma once

#include "Y.h"

extern int ONE;
int combine();

X.c

#include "X.h"

int ONE = 1;

int combine()
{
    return ONE + TWO;
}

Y.h

#pragma once

extern int TWO;

Y.c

#include "Y.h"

int TWO = 2;
songyuanyao
  • 147,421
  • 15
  • 261
  • 354
  • Might be worth expanding "`#pragma once` is used for duplicated including" to "`#pragma once` is used for duplicated including within a single compilation unit (.c file in this case)" – user4581301 Aug 10 '16 at 02:21
  • @user4581301 Yes, it's important. – songyuanyao Aug 10 '16 at 02:24
  • In what situation would I have a single translation unit? Is that when I have only one file, or only `.c` files? – Hatefiend Aug 10 '16 at 04:31
  • @Hatefiend In your code you have three compilation unit. You compile three .c file, and then link them, right? Multiple definition error occurs at link time here. – songyuanyao Aug 10 '16 at 04:43
  • Okay so you say `#pragma once` only protects me when it's a single translation unit. That means that in order for that to actually protect me, I'd have to attempt to somehow `#include` the same `.c` or `.h` file that I'm already inside of? When would that ever happen or ever be useful? – Hatefiend Aug 10 '16 at 04:52
  • @Hatefiend Suppose you have a common .h file (like `comm.h`), which is include in both `X.h` and `Y.h`. And `Main.c` includes both `X.h` and `Y.h`, `#pragma once` could guarantee that `comm.h` would be included only once in `Main.c` (the single translation unit). – songyuanyao Aug 10 '16 at 04:55
  • But I thought `comm.h` and `comm.c` (even if `comm.c` doesn't exist) are combined into one translation unit, thenn `x` and `y` are each combined into their own translation units, and then `main.c` is it's own translation unit making four, and thus `#pragma once` doesn't help you? – Hatefiend Aug 10 '16 at 04:58
  • The contents of header files are copy/pasted into any source file that `#include`s them (directly or indirectly) as I show in my answer. There is no special relationship between `comm.h` and `comm.c`. – Miles Budnek Aug 10 '16 at 05:02
  • @Hatefiend Let's make it simple. Suppose there's only one .c file, `Main.c`. So there's only one translation unit. `Main.c` includes `X.h` and `Y.h`, and `X.h` and `Y.h` both include `comm.h`. If you don't use `#pragma once`, `comm.h` will be included twice for `Main.c`, compile might fail. – songyuanyao Aug 10 '16 at 05:07
  • @songyuanyao I see what you mean. Now my next question is: don't header guards protect against that? If then answer is yes, that means that not only do header guards protect against multiple translation unit compilation, but then they also protect against single as well. So then it goes back to then question of why use `#pragma once ` if header guards are definitively better – Hatefiend Aug 10 '16 at 05:13
  • @Hatefiend Sorry can't get what you mean. Header guard (`#pragma once`) only guards within single translation unit, not multiple. – songyuanyao Aug 10 '16 at 05:19
1

#pragma once only guards against the same header being included twice in the same compilation unit. It does not prevent problems when you define the same variable in multiple compilation units.

To see what's wrong, let's walk through what each compilation unit looks like as it's being compiled.

After preprocessing, X.c ends up looking like this:

int TWO = 2;

int ONE = 1;

int combine();

int combine()
{
    return ONE + TWO;
}

As you can see, it contains definitions for the symbols TWO, ONE, and combine.

Y.c ends up looking like this:

int TWO = 2;

It also defines the symbol TWO.

main.c looks like this:

int TWO = 2;

int ONE = 1;

int combine();

int main()
{
    int fusion = combine();

    return 0;
}

So it also has definitions for TWO and ONE, along with main.

So after compilation, we end up with 3 object files, all three of which have definitions for TWO, and two of which have definitions for ONE. When we get to linking, the linker sees references to ONE and TWO in the definition of combine and goes looking for those symbols' definitions. Since they're defined in multiple places, it throws an error and gives up.

You can fix this problem by declaring ONE and TWO in X.h and Y.h and defining ONE and TWO in X.c and Y.c.

Miles Budnek
  • 19,769
  • 2
  • 26
  • 41
  • Thank you for your response. So here I'm taking away a few possibilities. I can either A) use header guards in every single `.h` file I make, and **not** using `#pragma once` B) I could do as you said, and define every single global variable I make in every single `.h` file (sounds awful in the case that I have 100+ files in a single compilation unit), or C) I could label every single global variable I make `extern` which also sounds like a pain because initially I might write a lot of variables as not needing to be used by another file and then decide I actually need to later. Am I right? – Hatefiend Aug 10 '16 at 04:26
  • In general, you must only declare globals in your headers, and define them in a single .c file. Using `#ifndef` header guards will not fix your problem, and defining your globals in every header file will only make things worse. In general, every symbol must have exactly one definition in your program, and defining a global in a header that's included in multiple .c files will cause that symbol to be defined multiple times. – Miles Budnek Aug 10 '16 at 04:33
  • I'm still not quite there in terms of understanding you. If I define the global variable `int x = 5` in one header file, there's my one definition. Now if any other file in any other translation unit comes into that `.h` file, header guards say nope and they just leave the file without reading any of it right? Then there's no need for `extern` `pragma once` or typing out the same variables in a billion different files – Hatefiend Aug 10 '16 at 05:02
  • The contents of a header file are simply copy/pasted into any file that `#include`s it. Headers are not compiled separately, and nothing ever "comes into" a header file. In my answer, I've shown exactly what each translation unit will look like to the compiler. It sees nothing else. – Miles Budnek Aug 10 '16 at 05:06
  • Here's one other thing. I was reading [this post](http://stackoverflow.com/questions/1143936/pragma-once-vs-include-guards) and people were basically implying that `#pragma once` and header guards were almost exactly the same. And yet, header guards have the ability to solve my problem in the original post, do they not? They can handle protection from multiple translation units?" – Hatefiend Aug 10 '16 at 05:17
  • No, `#ifndef` guards and `#pragma once` do basically the same thing. Both guard a header from being included multiple times in a single translation unit. – Miles Budnek Aug 10 '16 at 05:23
  • Okay yeah it's safe to say that I just don't understand this multiple translation units thing then. I've been doing C++ for like over a year now and have had projects with like a dozen .h files all including eachother in a spiderweb mesh of includes. How am I only running into this problem now? – Hatefiend Aug 10 '16 at 05:36
  • @Hatefiend Case by case. Maybe they all obey the rule of "declare in .h file, define in .c file". i.e. Don't define in .h file. – songyuanyao Aug 10 '16 at 05:44
  • @songyuanyao Is that rule being broken in my original post? – Hatefiend Aug 10 '16 at 05:47
  • Yes, you're defining `ONE` and `TWO` in your headers, not just declaring them. – Miles Budnek Aug 10 '16 at 05:49
  • @Hatefiend Yes. `int ONE = 1;` is definition, that's why I said you should use `extern int ONE;` to make it a declaration. – songyuanyao Aug 10 '16 at 05:49
  • @songyuanyao that is also a definition. A declaration would just be `extern int ONE;` in the header and `int ONE = 1;` in exactly one source file. – Miles Budnek Aug 10 '16 at 05:52
  • Ohhhh that's interesting. Let's say ONE and TWO were both declared without the `=`, meaning they have no definition yet. Would I still have this problem in the original post? Or is `extern` still needed? – Hatefiend Aug 10 '16 at 06:35
  • Yes, without `extern` the variable will be default-initialized when declared. `extern` basically says "This is just a declaration; look for the definition later at link time". – Miles Budnek Aug 10 '16 at 06:43
0

#pragma once prevents a header file from getting included more than once when compiling a single source module.

That's all it does.

Your header file is getting included from two different source modules.

That header file declares and defines an object in global scope.

Both of your two source modules, therefore, end up defining the same object in global scope.

And that's where the duplicate symbol definition comes from.

#pragma once is not a substitute for knowing and understanding C++ scoping rules.

Sam Varshavchik
  • 84,126
  • 5
  • 57
  • 106
  • In this case I'm using C, but I take it the advice is the same? I find it odd though because Microsoft Visual Studio automatically puts `#pragma once` at the begninning of every `.h` file. I thought this was the program's way to telling me that I am guarded vs. re-definitions. Also, when would you ever have a single source module? I use `.h` files in every single C/C++ program I've ever made since I was taught that was good practice. Does this mean that single source module = `#pragma once` and multiple source modules = `header guards`? – Hatefiend Aug 10 '16 at 04:20
  • Doesn't matter if it's C or C++. Yes, it's guarded against ***re-definitions in the same source module only***. What part of "it doesn't guard anything against redefinitions in multiple source modules" you cannot understand? There is no way to guard against redefinitions in multiple modules. Define "int a=0;" in one module, and "int b=0;" in another, and you have multiple definitions. A compiler compiles one source module at a time. It has no possible way of knowing what might or might not be declared in some other source module. Stop using the pragma as a crutch. Learn how scoping works. – Sam Varshavchik Aug 10 '16 at 11:01