1

I have two .c files which I compile over a makefile.

foo.c:

void foo()
{
    printf("this is foo");
}

main.c:

#include  <stdio.h>

int main()
{
    printf("this is main\n");
    foo();
}

the makefile looks like that:

all: main.o foo.o
    gcc -o prog foo.o main.o

main.o: main.c
    gcc -c main.c

foo.o: foo.c
    gcc -c foo.c

So the question is: how can foo.c use printf() without me including stdio.h AND how can main.c use the method foo() without me including foo.c.

My guess/research is that the makefile works as a linker. But I dont have prove for that and want to understand how this works excactly.

Correct me if I misunderstood something.

Spirit
  • 43
  • 1
  • 7
  • 1
    If you add the `-Wall` flag, you'll get a warning. – Oliver Charlesworth Nov 19 '14 at 15:22
  • Well yes, but why dont I get an error instead of just a warning? In my understanding of a compiler it should say that foo.c and main.c are complete nonsense. – Spirit Nov 19 '14 at 15:24
  • If a function isn't defined it gets a default definition. That definition may or may not be correct. If you happen to use the correct function once linked and the definition wasn't *too* far off things will work out but you shouldn't expect or depend on that (which is why you get the warning). – Etan Reisner Nov 19 '14 at 15:44

3 Answers3

0

The makefile is not a linker. It is input to make. The makefile just tells make what commands to execute under what conditions.

Your all target is running gcc in linking/linker mode gcc -o prog foo.o main.o.

The same way your foo.o and main.o targets are running gcc in compilation mode gcc -c foo.c.

For the record you can combine the two .o targets into just

%.o: %.c
        gcc -c $^

which is, in fact, already a default rule in make so you need not include that rule at all.

Additionally your all target is not following bet make practices because it generates a file that does not match the name of the target. So you should use

all: prog

prog: main.o foo.o
        gcc -o prog foo.o main.o

instead.

Though once again there make has you covered by default and so your entire makefile can be replaced by

all: prog

prog: main.o foo.o

and you should get the same results.

Etan Reisner
  • 77,877
  • 8
  • 106
  • 148
  • 1
    The makefile tells `make` what to do, it doesn't execute any commands ;-). And I don't see how this answers the main question "how can foo.c use printf() without me including stdio.h AND how can main.c use the method foo() without me including foo.c?" – rubenvb Nov 19 '14 at 15:36
  • @rubenvb True. I'll edit. And it addresses the "is the makefile a linker? is that why this works?" part of the question which is that he is linking them together in the end. It doesn't cover the details of *why* C works this way but I wasn't sure that was the question originally. – Etan Reisner Nov 19 '14 at 15:45
0

In the makefile you can force the complier to include <stdio> or any other header:

From the docs:

-include file

Process file as if #include "file" appeared as the first line of the primary source file. However, the first directory searched for file is the preprocessor's working directory instead of the directory containing the main source file. If not found there, it is searched for in the remainder of the #include "..." search chain as normal. If multiple -include options are given, the files are included in the order they appear on the command line.

Just add -include filename.h in the GCC/compiler command line within the makefile.

egur
  • 7,830
  • 2
  • 27
  • 47
0

In the compilation phase, the compiler checks function calls against prototypes. Any function that lacks a prototype is assumed to return int and to accept any number of arguments.

If you turn up the warning level, gcc will warn you if a prototype is missing. You should add -Wall and you could also add -pedantic to get diagnostics on additional things the compiler think are suspicious.

If the compilation step succeeds, the compiler creates an object file which contains the compiled code and 2 reference tables. The first table is the export table. It contains the names of all functions and variables that are exported from the object file. The second table is the import table. It contains a list of all functions and variables that are referenced, but where the declaration was missing.

In your case we have:

foo.o:

Export:

foo

Import:

printf

main.o

Export:

main

Import:

printf
foo

In the linker phase, the linker will take the list of imports and exports and match them. In addition to the object files and libraries you specify on the command line, the linker will automatically link with libc, which contains all functions defined by the c language.

Klas Lindbäck
  • 33,105
  • 5
  • 57
  • 82