This really shouldn't be something I have to ask, but somehow I can find absolutely nothing by searching to answer my question.
In order to ask another question, I made three files: main.c, sub.c and sub.h. main.c has the 'main' function, while sub.c contains only function definitions.
Initially, main.c had '#include "sub.h"' as its only include statement.
Trying 'gcc main.c -O3 -o test' resulted in an error, saying that the function f() (declared in sub.h, defined in sub.c, and referenced in main.c) was unreferenced. Trying 'gcc main.c sub.c -O3 -o test' resulted in expected behaviour.
I then modified test.c, removing the #include and references to f. 'gcc main.c -O3 -o test2' worked as expected.
I then re-added the references to f, and forgot to re-add the #include. Despite this, 'gcc main.c sub.c -O3 -o test3' worked as expected.
I noticed the mistake, and re-added the include intentionally as '#include sub.c'. 'gcc main.c sub.c -O3 -o test4' resulted in an error, saying f() was defined multiple times. 'gcc main.c -O3 -o test4 returned to working as expected.
The only conclusion I can draw from this is that as far as local files are concerned, if the file is a source code file then include it and don't add it to the command, else add its source to the command and don't bother including it, because apparently it doesn't matter whether you include it or not. I guess?
Basically my question is, is the behaviour above intended, and if so is it documented, and where, so I can read it and make better informed decisions about how to handle my included files in the future.
I then re-added the references to f, and forgot to re-add the #include. Despite this, 'gcc main.c sub.c -O3 -o test3' worked as expected.
For suitably loose definitions of "worked"; I'm going to bet that f()
returns an int
, and that gcc
was defaulting to C89 mode.
Prior to C99, if the compiler encountered a function call before it saw a function definition or declaration, it assumed that the called function returned an int
. Thus, as long as f()
actually returns an int
, your code will compile and run successfully. If f()
doesn't return an int
the code will still compile, but you will have a runtime problem. All the linker cares about is that the symbol is there; it doesn't care about type mismatches.
C99 did away with implicit int
typing, so under a C99 compiler your code would fail to compile if you didn't have a declaration for f()
in scope (either by including sub.h
or adding the declaration manually).
The only conclusion I can draw from this is that as far as local files are concerned, if the file is a source code file then include it and don't add it to the command, else add its source to the command and don't bother including it, because apparently it doesn't matter whether you include it or not. I guess?
That is the exact wrong conclusion to draw. You do not want to include a .c
file within another .c
file as a regular practice, as it can lead to all kinds of mayhem. Everything in main.c
is visible to sub.c
and vice versa, leading to potential namespace collisions - for example, both files could define a "local" helper function named foo()
. Normally such "local" functions aren't visible outside of the source file, but by including one source file within the other, both versions of foo()
are visible and clash with each other. Another problem is that if a .c
file includes another .c
file which includes another .c
file, etc., you may wind up with a translation unit that's too large for the compiler to handle. You will wind up recompiling both files every time you change one or the other where it isn't necessary. It's just bad practice all the way around.
The right thing to do is compile main.c
and sub.c
separately and make sure sub.h
is included in both (you want to include sub.h
in sub.c
to make sure your declarations line up with your definitions; if they don't, the compiler will yak while translating sub.c
).
Edit
Answering the following question in the comments:
When you say to compile main.c and sub.c separately, I'm assuming you mean to make object files out of them each individually and then link them (3 commands total)? Is there any way to do that with a single command?
The command gcc -o test main.c sub.c
does the same thing, it just doesn't save the respective object files to disk. You could also create a simple Makefile, like so:
CC=gcc
CFLAGS=-O3 -std=c99 -pedantic -Wall -Werror
SRCS=main.c sub.c
OBJS=$(SRCS:.c=.o)
test: $(OBJS)
$(CC) -o $@ $(CFLAGS) $(OBJS)
clean:
rm -rf test $(OBJS)
Then all you need to do is type make test
:
[fbgo448@n9dvap997]~/prototypes/simplemake: make test
gcc -O3 -std=c99 -pedantic -Wall -Werror -c -o main.o main.c
gcc -O3 -std=c99 -pedantic -Wall -Werror -c -o sub.o sub.c
gcc -o test -O3 -std=c99 -pedantic -Wall -Werror main.o sub.o
There are implicit rules for building object files from .c files, so you don't need to include those rules in your Makefile. All you need to do is specify targets and prerequisites.
You may need to drop the -pedantic
flag to use some platform-specific utilities, and you may need to specify a different standard (c89, gnu89, etc.) as well. You will definitely want to keep the -Wall -Werror
flags, though - they'll enable all warnings and treat all warnings as errors; they'll force you to deal with warnings.
Collected from the Internet
Please contact [email protected] to delete if infringement.
Comments