Repeat after me: never include .c
for no other reason
We can use yours more efficiently Makefile
using automatic variables. See that answer explaining a little about the variable $<
. For an extensive and more focused explanation for C/C file compilation++, see this other answer.
About the use of files .h
, I disagree a little bit about forward declaration in that other answer.
Now, let’s focus. The archive arp_poisoning.c
does not depend on the arp_discover.c
, but it depends on his statements. It depends on the forward declarations which are contained in .h
. To explain what is really going on, I now need to go into details of how the compilation of a C source file works.
Compilation of C file
Amazingly, the C compiler works only one entry at a time. It takes this input from the so-called C pre-processor. In the next section I’ll tell you what the pre-processor is.
C is a language that in practice is parsed in a single pass, like most programming languages. The compiler will identify every definition + open references and will put your results in so-called object files (files .o
in the world of GCC).
What would be considered definition in this case? A body function is definitely defined. A global variable is also defined.
What about open reference? This is a concept for things that should exist but don’t exist in the context of compiled code. It may exist on the outside, but the compiler is not able to close which implementation is actually being used.
When an open reference happens? It happens when you say the variable exists, but the file is not "owner" of it; the variable is external (extern
). This happens with the variable errno
. Another open point of reference? Functions they had forward declaration.
You can and should create object files with open references. But you can only create executable after closing all static references, after making all the links, all the Binds. And who does that? The linker.
The linker
The role of the linker is to resolve all open references on top of the set of available objects and libraries.
How does it do that? With a protocol. A C function will have a name, and that name is recognized by the linker and generated by the compiler. GCC generates this value by placing _
in front of the function name C. For example, the function capacidade
would have your link name _capacidade
.
The linker will search for any open reference and, if it has not found the function to close this reference, it will keep this open reference pointed. If you find a function definition, it will add to the set of known functions; if that function has an open reference to it, the linker will close the reference. If a second definition is found, the linker aborts with error: definition duplicity.
The pre-processor C
The C preprocessor is a word processor in general; it is also known as CPP. It has emerged to work with C files, but can be used to process other textual files.
In general, everything contained #
C code line will be interpreted by CPP. After a #
, will come a CPP command. CPP will also replace textual everything that is set for preprocessing.
Some commands:
#define MACRO val
: defines MACRO
with the value val
; this means that every string in the text, from the moment of the definition of MACRO
will be replaced Verbatim for val
; if the value is not defined, an empty string is assumed; arguments can be used for the definition;
#include "abcde"
: considers that, the exact point of this include
it should take into consideration as continuation of the entire file content entry abcde
, preprocessing abcde
also; all previous definitions remain valid.
What happens when you include another .c
at my source?
Come on. Let’s take as an example a simpler file. I’ll call hello.c
and the say_hello.c
.
hello.c
:
#include <stdio.h>
void hello() {
printf("hello\n");
}
say_hello.c
#include <stdio.h>
#include "hello.c"
int main() {
hello();
return 0;
}
Let’s see what will be the output of the CPP, which is the input of the compiler itself? I will ignore the standard library part because it is not the case:
/* coisas do stdio.h */
void hello() {
printf("hello\n");
}
int main() {
hello();
return 0;
}
Okay. So, when compiling say_hello.c
, we will have two properly implemented definitions and an open reference in the say_hello.o
. The definitions are:
_hello
_main
The open reference is:
printf
By having the GCC join the say_hello.o
with the hello.o
, we will have two definitions for _hello
. Which means you won’t be rolling the compilation.
How to proceed?
Simple: include the headers/.h
. Compile the files .c
individually. Put it all together in one large executable. The C++ compilation response takes care of that very well. In general, this can also be done in the makefile
:
.PHONY: all
all : teste
teste : arp_discover.o arp_poisoning.o
gcc -o $@ $^
%.o : %.c
gcc -o $@ $< -c
arp_discover.o : arp_discover.h
arp_poisoning.o : arp_discover.h
Makefile looks right. It can show the files you are compiling?
– mercador
Oops, well they’re giant hehehe, but basically what there is is a file. h that defines the arp_discover and this dependency declared inside the arp_poisoning file with #include "arp_discover. c"
– Emanoel
by Makefile was able to know this. By the errors shown, you are declaring
capacidade
,posicao
,sendRequests
,receiveReplies
andgetHosts
more than once.– mercador
it is, but it shouldn’t, because my code is declaring this only in a file, you will be able to take a look at the code by following the link from github: https://github.com/emanoelvianna/work-networks, the Makefile code is different and the dependency on arp_poisoning is commented, the rest is the same I am using.
– Emanoel
@Emanoel, do not include files
.c
– Jefferson Quesado