Reputation: 97
i'm trying to figure out how to make an exe with more than 1 c files..
let's say i have 2 c files, first is called main.c and second hello.c.
main.c file is like this...
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
hello();
return 0;
}
and hello.c is like this...
#include <stdio.h>
#include <stdlib.h>
void hello(void)
{
printf("Hello...");
}
how will my main.c file read hello() function from the other file, and how can i compile this files and make an executable with gcc..
The steps i know until now is that(with 1 c file), i open cmd.. go into my project's folder.. path %PATH% with my editor's bin folder and then gcc main.c and -o programsname and i get my exe..
So how can i combine 2 c files like above and make an exe
Upvotes: 0
Views: 2338
Reputation: 4034
As far as how to do it, @Barmar has already answered correctly,
gcc -o programname main.c hello.c
will compile bothe the files and link them together.
But I think you are more interested in how it works.
Checkout the following two questions How does the compilation/linking process work? How does C++ linking work in practice?
I'll try to briefly explain here also...
Essentially the command gcc
is a not just a compiler, it is what they call a driver. It will take the files main.c
and hello.c
through many stages of processing and help make an executable out of all that source code.
Based on the fact that gcc
got two .c
files as argument, it will infer that you want to use the code in both main.c
and hello.c
to make your executable program.
It will first take both those files individually through preprocessing, compiling and assembling phase. Then it will run the linker and input both files to it.
Preprocessing just expands macros like #include
and #define
.
Compiling is where the meat of the operations happen, gcc reads the c file and makes a syntax tree out of it, it might try to optimize it depending on the other optimization parameters that you passed, and then it writes a text file for each of the c files. These text files are assembly language representation of the functions written in c.
Compiling is also where gcc's compiler cc1
will encounter the line of code in main.c
where you are calling hello();
and wonder what is hello
and where is it. The way you can tell the compiler that hello
is a function that's going to be defined elsewhere is called forward declaration. This is how it works
void hello(void);
int main(void){
hello();
...
Now when cc1
encounters the call to hello()
it understands to be patient and just mark this as a reference to symbol hello
that it will eventually have to find.
The best practice for forward declarations is to put them in a header file, usually with the same name as the c file where the code is.
So you'd make a hello.h
(like @Barmar already said) and it will read
void hello(void);
and then in the main.c
you'd have a #include
directive as follows...
#include "hello.h"
int main(void){
...
Now the forward declaration in hello.h
will be copied into the c code at the time of pre-processing.
In the assembler phase gcc
invokes the as
assembler utility which is actually not part of the gcc package, but part of binutils
package. This is the utility that will take the assembly language text and make binary object files with .o
extensions (hello.o
and main.o
). These file contain machine level instructions pertaining to each of the functions defined in both main.c
and hello.c
.
Finally the real answer to your question lies in the linker ld
. Another utility that's part of the binutils
package. The linker will first do the simple task of combining all the code from the different .o
files into one file which will eventually become the executable. The linker has the ability to take two or more .o
files, examine the machine code and find references to foreign objects.. e.g. the code in main.o
has referenced a function from the hello.o
file. In the main.o
file this is documented as a reference to a symbol, but now the linked can actually find that symbol, so it will connect that reference with the actual code object. This process is called static linking.
Finally when you run a program, the machine code gets loaded, which is essentially a set of function objects and the references between function objects are hard wired at that time based on at which memory address the functions finally get deployed.
I have probably cut many corners in my explanation, but hopefully you get the gist of it and can move forward in your quest to understand how things work.
HTH
Upvotes: 2
Reputation: 781716
gcc -o programname main.c hello.c
Note that this will report a linker error, because the function hello()
is not defined anywhere. Either rename the function print
to hello
, or call print()
from main()
.
You should also add a declaration of the function in hello.c
to main.c
. Either put the declaration into the source file by hand, or create hello.h
to contain it and use #include "hello.h"
. The header file is generally preferable, so that when you change hello.c
you only have to update one place, not all the .c
files that use it.
The first way is to change main.c
to:
#include <stdio.h>
#include <stdlib.h>
void hello(void); // declare external function
int main(void)
{
hello();
return 0;
}
The second way is to create hello.h
, which contains:
#ifndef HELLO_H
#define HELLO_H
void hello(void);
#endif
And add:
#include "hello.h"
to main.c
after all the other #include
lines.
Upvotes: 0