#31011 - Wriggler - Tue Dec 07, 2004 1:11 am
Ok, this should be something easy that should just work. I'm baffled as to why it doesn't, but it's got to be one of those really obvious mistakes. Hopefully one of you fellas could help me out?
In bg.h->
Code: |
extern void initBg(); |
In main.c->
Code: |
#include "bg.h"
...
initBg(); |
In my makefile->
Code: |
arm-elf-gcc main.c bg.c -c -g -O2 |
...
However... when linking I get this:
Code: |
main.o(.text+0x462): In function `main':
c:\...\main.c:22: undefined reference to `initBg'
collect2: ld returned 1 exit status |
What the devil is going on? Surely if I declare something as "extern", that means it's available in all linked files?
I've also tried compiling them separately (giving main.o and bg.o) and then linking them. However, then I get loads of "multiple definition" errors. And I suppose that's correct, as my headers use "#ifndefine...#define" to set all the globals. If they're compiled separately, they'll both have the full whack included and then we'll get issues at link time.
I'm so confused. Which is the best way to do it? Compiling both .c into a single .o, or linking many .o's? Why am I getting these silly errors?
(I'm using DevKitArm if it makes any difference...)
Thanks in advance,
Ben
#31015 - Touchstone - Tue Dec 07, 2004 1:24 am
The obvious question, do you have an implementation of initBg() somewhere? I suppose you do but you haven't stated it in your post so I thought I'd ask. Maybe it's a parameter mismatch?
Your makefile line compiles both your C files into a binary executable, no? Personally I compile all C files into object files which I link together later, mainly because that's the only way I know how to do it. :)
If you get the "multiple definition" error that probably means your header file actually implements something, perhaps a variable, and I'm sure you know that that's bad. ifndef/define/endif doesn't save you from that problem regardless of how you compile your code, at least not to my knowledge, because defines are only known to the compiler for each C file that is compiled, and when a new file is compiled all definitions are cleared.
_________________
You can't beat our meat
#31017 - poslundc - Tue Dec 07, 2004 1:34 am
Try reversing the order of your files on the command line, so it's bg.c then main.c.
Dan.
#31026 - sajiimori - Tue Dec 07, 2004 2:21 am
I didn't even know you were allowed to compile and link multiple .c files in one call to gcc. :o
#31047 - R0d Longfella - Tue Dec 07, 2004 10:29 am
Try: In bg.h->
To my knowledge you're not supposed to use extern with function declarations. So try it without the extern for a change.
Goodluck!
#31051 - Wriggler - Tue Dec 07, 2004 12:19 pm
Hi guys, thanks for the help.
Quote: |
The obvious question, do you have an implementation of initBg() somewhere? |
Yup, it's in there. Sorry I didn't mention it. Parameter's aren't mismatched.
Quote: |
If you get the "multiple definition" error that probably means your header file actually implements something, perhaps a variable, and I'm sure you know that that's bad. |
After a quick look at some of my includes, some of them implement global vars (whoops!). I'll fix these when I get back home, but even still bg.h isn't one of those headers.
Quote: |
Try reversing the order of your files on the command line, so it's bg.c then main.c. |
Tried that, no change unfortunately.
Quote: |
So try it without the extern for a change |
That's how I was doing it in the first place, but I was told to use the extern keyword. It doesn't seem to make a difference, as I believe the compiler adds "extern" to function declarations anyway. Can anybody clear this up for me? Are you supposed to use "extern" on function declarations or not?
The problem might be as above, e.g. those header files defining global variables. Unfortunately I can't check it out at the moment, but will give it a go tonight and report back.
Thanks again for the help guys.
Ben
#31097 - sajiimori - Tue Dec 07, 2004 9:16 pm
I've never seen a difference when putting extern on function declarations.
Anyway, your original post only shows how you are compiling. How are you linking?
#31204 - jma - Wed Dec 08, 2004 9:06 pm
sajiimori wrote: |
I've never seen a difference when putting extern on function declarations. |
It can make a big difference if you are using extern "C" for name mangling purposes.
Jeff
_________________
massung@gmail.com
http://www.retrobyte.org
#31210 - bertsnks - Wed Dec 08, 2004 10:06 pm
I've had the same problem with code from an asm file compiled to .o, and adding it to my c source.
extern "C" myfunct(void);
^^ this made it work.
#31219 - sajiimori - Wed Dec 08, 2004 11:23 pm
*sigh*
I've never seen a difference when adding only extern on a function declaration.
#31225 - Wriggler - Thu Dec 09, 2004 12:48 am
Sorry about the delay in replying to this thread guys, I'm just trying out a couple more things before I report back...
*grumble* ...Stupid compiler... ;)
Thanks to all for the help.
Ben
#31227 - jma - Thu Dec 09, 2004 12:54 am
Hehe, I was more teasing than anything. I shoulda put a smiley :)
Jeff
_________________
massung@gmail.com
http://www.retrobyte.org
#31230 - sajiimori - Thu Dec 09, 2004 1:24 am
You can't charm me with your smileys!! ;)
#31234 - poslundc - Thu Dec 09, 2004 2:09 am
For the record, sajimori correctly identified what the most likely problem is, which is that you've changed your compile line to include the new file, but not your linking line. Look for another spot in your makefile where you call either gcc or ld on main.o, and make sure that you are calling it on bg.o as well.
Dan.
#31259 - Wriggler - Thu Dec 09, 2004 9:17 am
Hi Dan, thanks for the advice. Fortunately my linking was ok, I did exactly as you described. I finally got the files linking in ok (and did a little dance around the room!), but then I came to another problem.
All of my globals are now invisible to each of the .c files. Stuff that I can't avoid, like the OAM Copy. For the life of me I can't figure out how I'm supposed to arrange the declarations so that I can use them in all files. At the moment I have a global header, which looks a bit like this:
Code: |
extern OAMEntry gSprites[128]; |
This is #included in every source file, so basically each .o I generate will expect OAMEntry to be linked in, right?
Then in my main.c file, I write this...
Code: |
OAMEntry gSprites[128]; |
...which makes the one "instance" I wish to use. Is that right? Then whenever I change gSprites (in any file), I know that there will only be one version of it? Is that the right theory? Because it's not working! :)
Cheers guys,
Ben
#31260 - sajiimori - Thu Dec 09, 2004 9:36 am
Quote: |
I finally got the files linking in ok... |
So what did the problem turn out to be (for future readers who might encounter a similar issue)? Quote: |
This is #included in every source file, so basically each .o I generate will expect OAMEntry to be linked in, right? |
The portion of the header you posted looks right. All references to the array have to be resolved by the linker, but just declaring the array doesn't generate a reference. You can declare nonexistant extern data without causing problems, as long as you don't try to use it. Quote: |
Is that the right theory? Because it's not working! :) |
It looks right, so maybe you could give some more information about the error (if there is one) and how you are doing things.
#31314 - poslundc - Thu Dec 09, 2004 11:26 pm
You shouldn't have the extern declaration in the same .c file that also has the actual instance declaration. This should generate a compile-time error if you try it.
An effective trick to guard against this is to surround your globals in the .h file with the barrier:
Code: |
#ifndef _MAIN_HEADER_
#define _MAIN_HEADER_
// structures, prototypes, etc.
#ifndef _MAIN_SOURCE_
extern int myGlobalVariable;
#endif
#endif |
... and then in your main.c file define _MAIN_SOURCE_ before including the header file, as follows:
Code: |
#define _MAIN_SOURCE_
#include "main.h"
...
int myGlobalVariable; |
Dan.
#31315 - sajiimori - Thu Dec 09, 2004 11:37 pm
I disagree. I think it should be an error if you don't have a prior prototype for a non-static function, because it should be assumed that modules include their own headers, and if those headers don't have the prototype then it will be a phantom global. (I realize that your example wouldn't actually create a phantom global.)
Our compiler currently gives warnings about such cases, but I've never seen a compiler give an error or warning about having an extern function declaration followed by a non-static definition of that function.
#31342 - poslundc - Fri Dec 10, 2004 7:18 am
I'm confused... was it perhaps unclear that I was responding to Wriggler's most recent post and not yours? I was referring strictly to the definition/sharing of global variables, not prototyping of functions... I am having trouble understand your first paragraph as it relates to this.
In any case, I think my solution is perhaps a bit hacky, although I think I've seen it used quite often by other programmers as a good way to maintain the 1 C file : 1 header file ratio, and have the header contain all the necessary public information for the module. It more has to do with the quirkiness of C not allowing you to declare a global variable and have an extern reference to it in the same file.
Dan.
#31343 - sajiimori - Fri Dec 10, 2004 7:28 am
While I didn't notice you were specifically referring to variables as opposed to functions, my point applies to both.
I've never seen a compiler complain about having an extern declaration followed by a definition of the same function or variable (which would be annoying and useless), but I have seen one complain about getting a global function definition without first seeing a declaration (which is actually a helpful warning).
#31376 - poslundc - Fri Dec 10, 2004 5:50 pm
sajiimori wrote: |
I've never seen a compiler complain about having an extern declaration followed by a definition of the same function or variable (which would be annoying and useless) |
I agree wholeheartedly, but to the best of my memory every version of GCC I've used does this. The fact that other programs do similar shielding is evidence that I'm not the only one to experience it, either. But yes, it is a positively stupid quirk, considering function prototypes behave in the opposite manner.
Quote: |
but I have seen one complain about getting a global function definition without first seeing a declaration (which is actually a helpful warning). |
In practice this shouldn't be as much of a problem, since tradition (and sense) has #includes come before global variable definitions, since the types of those variables may be defined in the #include statements. Even novice programmers seem to naturally abide by this. *shrug*
Dan.
#31393 - sajiimori - Fri Dec 10, 2004 7:54 pm
Quote: |
I agree wholeheartedly, but to the best of my memory every version of GCC I've used does this. |
Hmm... so this gets a warning for you? Code: |
/*gcc -Wall*/ extern int x; int x; |
Quote: |
In practice this shouldn't be as much of a problem... |
The warning helps me when I forget to put 'static' on something I want to be private. It doesn't see a declaration in the header, so it assumes I meant to either add one or change to 'static', which is always the case.
#31406 - poslundc - Fri Dec 10, 2004 8:59 pm
sajiimori wrote: |
Quote: | I agree wholeheartedly, but to the best of my memory every version of GCC I've used does this. | Hmm... so this gets a warning for you? Code: | /*gcc -Wall*/ extern int x; int x; |
|
Sadly I am on vacation at the moment at a computer without GCC, so I am unable to test it... :P When I get back I can have a look and see, but it was this reason that caused me to start using those shields in the first place.
I take it that you are asking because you have observed different results in your compiler... it is quite possible that it was removed from later versions of it; I'm running something, uh, less than 3.x at home.
Dan.
#31422 - tepples - Fri Dec 10, 2004 10:25 pm
sajiimori wrote: |
The warning helps me when I forget to put 'static' on something I want to be private. It doesn't see a declaration in the header, so it assumes I meant to either add one or change to 'static', which is always the case. |
So what does it do for int main(int argc, char **argv)?
_________________
-- Where is he?
-- Who?
-- You know, the human.
-- I think he moved to Tilwick.
#31423 - sajiimori - Fri Dec 10, 2004 10:27 pm
I'm assuming the compiler has a special case for that, just as it's a special case for the programmer.