Thread: Multiple Definitions Problems (versus C)

  1. #1
    Registered User
    Join Date
    Aug 2010
    Location
    Rochester, NY
    Posts
    196

    Multiple Definitions Problems (versus C)

    Hey,

    I ran into this problem on a project I'm working on. I dumbed it down to like 5 lines in like 4 files to prove the concept, and it failed.

    Can anybody tell me what the deal is??

    I have 4 files:
    t1.c, t2.c, t1.h, t2.h

    t1.c
    Code:
    #include <stdio.h>
    #include "t1.h"
    #include "t2.h"
    
    int main()
    {
       val = 7;
       printf("val = %i\n", val);
       change();
       printf("val = %i\n", val);
    }
    t1.h
    Code:
    int val;
    t2.c
    Code:
    #include "t2.h"
    #include "t1.h"
    
    int change()
    {
       val = 9;
    }
    t2.h
    Code:
    int change();
    Compile it, compiles and runs absolutely fine.

    NOW

    I change the .c extensions to .cpp, compile it with g++ (was using gcc before) - get multiple definitions on 'val.'

    Okay, I figured, it's because t1.h is included twice...so I change it to:
    Code:
    #ifndef T1_H
    #define T1_H
    
    int val;
    
    #endif
    Still has the error, exact error:

    Code:
    g++ t1.cpp t2.cpp
    /tmp/ccQuwt1c.o:(.bss+0x0): multiple definition of `val'
    /tmp/cc0hcOlA.o:(.bss+0x0): first defined here
    collect2: ld returned 1 exit status
    Obviously the problem is with the linker - this is in essence the problem I'm having (at a much larger scale) on the project I'm working on.

    This annoys me b/c I typically program C in a C way and when I use C++ it's typically object oriented - very rarely do I use C++ in a C way like I am now, but due to needing the STL for this project, I'm forced to.

    Can anybody shed some light on this? If not, is there any way to do some weird extern magic so that I can add numbers to a vector in C (yes, that's weird, I know). There's just no object oriented nature in this C++ code and it's obviously causing problems even though anything that compiles in C should compile in C++.

    Thanks in advance.

  2. #2
    Registered User
    Join Date
    Aug 2003
    Posts
    1,218
    Well ignoring the fact that global variables are typically a bad thing to be using here is how you possibly solve it:
    Code:
    // t1.h
    extern int val;
    
    // t1.c
    int val;
    Note that you should have inclusion guards on all your h files.

  3. #3
    Registered User
    Join Date
    Aug 2010
    Location
    Rochester, NY
    Posts
    196
    Quote Originally Posted by Shakti View Post
    Well ignoring the fact that global variables are typically a bad thing to be using here is how you possibly solve it:
    Code:
    // t1.h
    extern int val;
    
    // t1.c
    int val;
    Note that you should have inclusion guards on all your h files.
    Yeah, please ignore any stylistic things.

    That won't work, I also need it in t2.c - I tried that, it errors out in the same place.

    I guess I'm more so wondering as to why it works fine when compiled with gcc, but fails when it's compiled with g++ - what are they doing differently, as far as I know if it works in C, it should also work in C++.

  4. #4
    and the Hat of Guessing tabstop's Avatar
    Join Date
    Nov 2007
    Posts
    14,336
    The reason is that you can do this in C:
    Code:
    int x;
    int x;
    int x;
    int x;
    int x;
    and C will say "oh you're just being crazy" and give you one variable called x (as long as all the declarations are compatible with each other, which they are here). Try that in C++, and bad things. So you can only declare variables one time (all the rest of the time, which means for sure in the headers, you need to mark them as extern).

    (EDIT: So in your particular situation you need extern int in the headers, and then one .c file to say int. You'll be able to access the variable in the other .c file, since you'll see the extern declaration from the header.)
    Last edited by tabstop; 01-01-2011 at 04:22 PM.

  5. #5
    Registered User
    Join Date
    Aug 2010
    Location
    Rochester, NY
    Posts
    196
    Quote Originally Posted by tabstop View Post
    The reason is that you can do this in C:
    Code:
    int x;
    int x;
    int x;
    int x;
    int x;
    and C will say "oh you're just being crazy" and give you one variable called x (as long as all the declarations are compatible with each other, which they are here). Try that in C++, and bad things. So you can only declare variables one time (all the rest of the time, which means for sure in the headers, you need to mark them as extern).

    (EDIT: So in your particular situation you need extern int in the headers, and then one .c file to say int. You'll be able to access the variable in the other .c file, since you'll see the extern declaration from the header.)
    I'm having a little bit of trouble following the compiler if that's the case.

    So I extern what's in the header, that tells it 'ok, create static space in the code/static section of memory, it'll be declared later' - then in ONE .cpp file, it'll be allocated into the code/static section of memory upon compilation when it sees it in the global scope. So now, how did you avoid conflicts from the second one (in another .cpp file) being compiled and and packed into the code segment? Since C++ typically uses name mangling, what assurances do you have that it would realize there's something in the same scope waiting to have space allocated for it, and that the names won't be different? Or is that something the C++ compiler is sure to take care of?

    Also, what happens if I don't actually declare the space (just extern in the header) - will it complain, or just go with it?

    Lastly, why doesn't the sentry do anything for this? That's why I put it there, is it because each file is compiled differently and each time it's "not defined" so it keeps recompiling it into different object files, then runs into problems when it links? How does it usually work when you use a class declaration in the header, never had multiple definition errors with classes...though the structs defined in the header for my real project on the example above aren't throwing errors...but still, that's kind of the purpose of putting the sentries up, no?

    Lastly (again), can anybody link me to more info (in plain, simple, english) on extern? I get it when you do extern <string> { } - and I get the concept (sort of) of externing a declaration or variable to be resolved at link time...but I never know when to use them..

    I'll try what you guys said again, though I'm pretty sure I tried that before.

  6. #6
    and the Hat of Guessing tabstop's Avatar
    Join Date
    Nov 2007
    Posts
    14,336
    Quote Originally Posted by Syndacate View Post
    I'm having a little bit of trouble following the compiler if that's the case.

    So I extern what's in the header, that tells it 'ok, create static space in the code/static section of memory, it'll be declared later'
    That's exactly what it doesn't do. extern means "DON'T make any space for it here, someone else will come along with the space later."

  7. #7
    Registered User
    Join Date
    Aug 2010
    Location
    Rochester, NY
    Posts
    196
    Quote Originally Posted by tabstop View Post
    That's exactly what it doesn't do. extern means "DON'T make any space for it here, someone else will come along with the space later."
    Err, yeah, sorry, that's what I meant to say, that's why I followed up with "the space will be allocated when it's actually declared in a source file" - or something along those lines.

    Though inline with the rest of what I said (I know it's a lot to read - sorry), what happens if space is never declared for it, is it just a linker error?

    As I said, I'm just kind of confused when to use externs - I get the basic concept behind it, I just never know when to use them...

    I was never 'officially' taught C/C++, I just know them from using them. So I never got an 'official' explanation on them...and all tutorials explaining them I can find online either A) don't do a good job of making it easy to understand, or B) only explain it when using it with a string literal.

    I'm not completely dumb to computers believe it or not, did systems programming & computer organization and the like - just nobody went over this stuff .

    Like, I get why this works, but I wouldn't have thought to use it.

    This also annoys me b/c now I'm wondering what the ........ the purpose of sentries are.
    Code:
    #ifndef FILE_H
    #define FILE_H
    
    #endif // sentry
    I feel that doesn't have as much meaning any more as the header will simply be included and it'll be re-defined with each file that includes it upon compilation - not quite sure how it avoids multiple definition errors upon linking though..

  8. #8
    and the Hat of Guessing tabstop's Avatar
    Join Date
    Nov 2007
    Posts
    14,336
    Surely you've done this (everybody's done this):
    Code:
    int function1(int a);
    
    int main(void) {
        int f = function1(5);
    }
    
    /* forgot to write function1 */
    And it compiles just fine. But the linker complains, that no function1 is ever defined. (Much the same as if you were to, say, call log10() and forget to link in the math library with -lm.) It's the same thing.

    The include guards keep the header from being read in more than once. The real reason doesn't have much to do with worrying about variables (although that's a small side benefit, since if you've declared a variable in a header you've already shot yourself in the foot and the include guards aren't really going to help). But there are instances where header2 includes header1, and header3 includes header1, and you want to include both header2 and header3 in your file.

  9. #9
    Algorithm Dissector iMalc's Avatar
    Join Date
    Dec 2005
    Location
    New Zealand
    Posts
    6,318
    Quote Originally Posted by Syndacate View Post
    Yeah, please ignore any stylistic things.

    That won't work, I also need it in t2.c - I tried that, it errors out in the same place.

    I guess I'm more so wondering as to why it works fine when compiled with gcc, but fails when it's compiled with g++ - what are they doing differently, as far as I know if it works in C, it should also work in C++.
    Actually, the above suggestion from Syndacate is precisely what works, and is the correct way everybody typically solves this problem. t2.c will see the definition through it's inclusion of t1.h that you've shown, and the multiple definition is prevented at link-time through having it actually only existing in one source file.

    You are either lying and simply just did not try the above, or you misread it and did something similar but not the same as what you were told, or you stuffed it up in some other way such as your actual code not matching the example you're showing here.

    C and C++ are different languages, C is very much not just a subset of C++.
    My homepage
    Advice: Take only as directed - If symptoms persist, please see your debugger

    Linus Torvalds: "But it clearly is the only right way. The fact that everybody else does it some other way only means that they are wrong"

  10. #10
    C++まいる!Cをこわせ!
    Join Date
    Oct 2007
    Location
    Inside my computer
    Posts
    24,654
    The purpose of include guards is to protect including a header twice or more in the same source file. Say that we a.cpp wants to include a.h. a.cpp also includes b.h, which includes a.h. Now we write

    #include "a.h"
    #include "b.h"

    As a result, a.h is included twice (once for the first include, and once from b.h, which includes a.h). This will lead to multiple definition errors.
    But include guards cannot protect against multiple source files including the same header (as well they shouldn't). Say a.cpp and b.cpp includes a.h. All fine. Header guards do not interfere here.
    Quote Originally Posted by Adak View Post
    io.h certainly IS included in some modern compilers. It is no longer part of the standard for C, but it is nevertheless, included in the very latest Pelles C versions.
    Quote Originally Posted by Salem View Post
    You mean it's included as a crutch to help ancient programmers limp along without them having to relearn too much.

    Outside of your DOS world, your header file is meaningless.

  11. #11
    Registered User
    Join Date
    Aug 2010
    Location
    Rochester, NY
    Posts
    196
    Quote Originally Posted by tabstop View Post
    Surely you've done this (everybody's done this):
    Code:
    int function1(int a);
    
    int main(void) {
        int f = function1(5);
    }
    
    /* forgot to write function1 */
    And it compiles just fine. But the linker complains, that no function1 is ever defined. (Much the same as if you were to, say, call log10() and forget to link in the math library with -lm.) It's the same thing.

    The include guards keep the header from being read in more than once. The real reason doesn't have much to do with worrying about variables (although that's a small side benefit, since if you've declared a variable in a header you've already shot yourself in the foot and the include guards aren't really going to help). But there are instances where header2 includes header1, and header3 includes header1, and you want to include both header2 and header3 in your file.
    Ah, okay, that makes sense - didn't think about that, that can happen on the same object module, so that would be an issue. Thanks.

    Quote Originally Posted by iMalc View Post
    Actually, the above suggestion from Syndacate is precisely what works, and is the correct way everybody typically solves this problem. t2.c will see the definition through it's inclusion of t1.h that you've shown, and the multiple definition is prevented at link-time through having it actually only existing in one source file.

    You are either lying and simply just did not try the above, or you misread it and did something similar but not the same as what you were told, or you stuffed it up in some other way such as your actual code not matching the example you're showing here.

    C and C++ are different languages, C is very much not just a subset of C++.
    You'd be surprised how many people feel differently.

    Though yeah, I was wrong. I thought I tried that, I tried something similar, but it wasn't that. I think I tried it with declaring them in ALL source files which used it. It worked fine when I only put it in one.

    Quote Originally Posted by Elysia View Post
    The purpose of include guards is to protect including a header twice or more in the same source file. Say that we a.cpp wants to include a.h. a.cpp also includes b.h, which includes a.h. Now we write

    #include "a.h"
    #include "b.h"

    As a result, a.h is included twice (once for the first include, and once from b.h, which includes a.h). This will lead to multiple definition errors.
    But include guards cannot protect against multiple source files including the same header (as well they shouldn't). Say a.cpp and b.cpp includes a.h. All fine. Header guards do not interfere here.
    Gotcha, I was thinking of it over the total binary image, being included, but it's only each time the compiler is run (say, for each obj module), the symbol table can't be packed with multiple definitions. I'm assuming it works out the fact that each object has its "own" definition of a shared header at link time (it would have to...right?).

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Replies: 3
    Last Post: 05-24-2009, 02:42 AM
  2. why Multiple define error ...
    By nilathinesh in forum C Programming
    Replies: 2
    Last Post: 10-19-2006, 06:31 AM
  3. Multiple Definition problems
    By shiver in forum C++ Programming
    Replies: 12
    Last Post: 08-30-2006, 02:33 PM
  4. Multiple problems with my program
    By Seirei in forum C Programming
    Replies: 4
    Last Post: 02-17-2006, 10:15 PM
  5. Multiple Problems( window(), gotoxy(), & getchar() )
    By drharv in forum C Programming
    Replies: 2
    Last Post: 02-20-2002, 09:47 PM