$ gcc -o code code.c
$ ./code
So what's the difference between header-files and source-files? Basically, header-files are #included and not compiled, whereas source files are compiled and not #included. One should never #include source files.
C/C++ programs are built in a two stage process. First, each source file is compiled on its own. The compiler generates intermediate files for each compiled source file. These intermediate files are often called object files -- but they are not to be confused with objects in your code. Once all the files have been individually compiled, the system then links all the object files together, which generates the final binary (the program).
This means that each source file is compiled separately from other source files.
Inputs: plusvec.h, plusvec.c, minusvec.h, minusvec.c, lalg.cpp./* algebra.c */
#include <iostream>
#include "addvec.h"
#include "deductvec.h"
using namespace std;
int main()
{
int u[2] = {100, 200};
int v[2] = {10, 20};
int res[2] = {0, 0};
plusvec(u, v, res, 2);
cout << res[0] << "\n";
cout << res[1] << "\n";
minusvec(u, v, res, 2);
cout << res[0] << "\n";
cout << res[1] << "\n";
}
/* addvec.h */
void addvec(int* x, int* y,
int* res, int d);
/* addvec.c */
#include "addvec.h"
void plusvec(int* x, int* y,
int* res, int d)
{
for(int i = 0; i < d; i++)
res[i] = x[i] + y[i];
}
/* deductvec.h */
void deductvec(int* x, int* y,
int* res, int d);
/* deductvec.c */
#include "deductvec.h"
void deductvec(int* x, int* y,
int* res, int d)
{
for(int i = 0; i < d; i++)
res[i] = x[i] - y[i];
}
However, this approach above is not recommended since this is not a scalable approach. A much better and scallable solution is to compile the project code in parts. For example, it is advisble to create the object files, .obj or .o, separately, and finally, to link all the .obj files to create the final executable. The set of commands that follows next demonstrates this fact.
# Not preferred approach
$ gcc algebra.c plusvec.c minusvec.c -o code
$./code
# Preferred approach
gcc -c plusvec.c
gcc -c minusvec.c
gcc -c algebra.c
gcc -o code plusvec.o minusvec.o algebra.o
./code
gcc -c filename.c
creates the corresponding
.obj file filename.o
. The last command that performs the
linking
, essentially links all the .o files in order to create the executable
code
.
/* getvec.h */
#include <iostream>
int* getvec(int d, int v);
/* plusvec.h */
#include "getvec.h"
int* plusvec(int* x, int* y, int d);
/* minusvec.h */
#include "getvec.h"
int* minusvec(int* x, int* y, int d);
/* getvec.c */
#include "getvec.h"
int* getvec(int d, int initval) {
int *v = (int *) calloc(d, sizeof(int));
for(int i = 0; i <d; ++i) v[i] = initval;
return v;
}
/* plusvec.c */
#include "plusvec.h"
int* plusvec(int* x, int* y, int d)
{
int* res = getvec(3, 0);
for(int i = 0; i < d; i++)
res[i] = x[i] + y[i];
return res;
}
/* minusvec.c */
#include "minusvec.h"
int* minusvec(int* x, int* y, int d)
{
int* res = getvec(3, 0);
for(int i = 0; i < d; i++)
res[i] = x[i] - y[i];
return res;
}
/* algebra.c */
#include
#include "plusvec.h"
#include "minusvec.h"
using namespace std;
int main() {
int* res;
int u[2] = {100, 200};
int v[2] = {10, 20};
res = plusvec(u, v, 2);
cout << res[0] << "\n";
cout << res[1] << "\n";
cout << res[2] << "\n";
res = minusvec(u, v, 2);
cout << res[0] << "\n";
cout << res[1] << "\n";
cout << res[2] << "\n";
}
Note, the getvec.h is included twice in algebra.c, i.e., once via plusvec.h and another via minusvec.h. Since multiple inclusion often results in hard-to-debug errors, we shall stop multiple inlcusion by eploying a technique that is called the include-guard.
Also note, the three other files, namely, getvec.h
,
getvec.c
, and algebra.c
are kept unchanged.
/* plusvec.h */
#ifndef GETVEC_H
#define GETVEC_H
#include "getvec.h"
#endif
int* plusvec(int* x, int* y, int d);
/* plusvec.c */
#include "addvec.h"
int* plusvec(int* x, int* y, int d) {
int* res = getvec(3, 0);
for(int i = 0; i < d; i++)
res[i] = x[i] + y[i];
return res;
}
/* minusvec.h */
#define GETVEC_H
#include "getvec.h"
#endif
int* minusvec(int* x, int* y, int d);
/* minusvec.c */
#include "minusvec.h"
int* minusvec(int* x, int* y, int d) {
int* res = getvec(3, 0);
for(int i = 0; i < d; i++)
res[i] = x[i] - y[i];
return res;
}
Note, the getvec.h is included twice in algebra.c, once via plusvec.h and another via minusvec.h. Sicne multiple inclusion often results in hard-to-debug errors, we have stopped multiple inlcusion by writing the include-guard.
#ifndef GETVEC_H
#define GETVEC_H
#include "getvec.h"
#endif
Once we include plusvec.h
, the GETVEC_H
gets
defined and getvec.h
gets included. Next time when minusvec.h
notices GETVEC_H
already defined and skips further inclusion of getvec.h
.
Compilation of big projects involving multiple source-files and their headers is a tedious job. Compiling them repeatedly especially during debugging is an extra headache. Automation tools help. Makefile is such a tool that can take much of the load off your head.
Consider one of the previous examples where we compiled the sources into the executable like this:
$ gcc plusvec.c minusvec.c getvec.c algebra.c -o compute
target: prerequisites
recipe
all: compute
compute: plusvec.c minusvec.c getvec.c algebra.c
gcc plusvec.c minusvec.c getvec.c algebra.c -o compute
clean:
rm compute
$ make
, make reads the makefile in
the current directory and proceeds by processing the first target (default target). In the example,
the first target is 'all' which has prerequisite 'compute' as the next target. So the next target
'compute' has prerequisites that are just the source files and hence are available in the current
directory. Then 'compute' starts with the rule that is essentially creating the executable 'compute'.
make clean
the 'clean' target is executed.
How Makefile can be written to include the relinking?
all: compute
compute: algebra.o plusvec.o minusvec.o getvec.o
gcc algebra.o plusvec.o minusvec.o getvec.o -o compute
algebra.o: algebra.c
gcc -c algebra.cpp
plusvec.o: plusvec.c
gcc -c plusvec.c
minusvec.o: minusvec.c
g++ -c minusvec.c
getvec.o: getvec.c
g++ -c getvec.c
clean:
rm *o compute
# variable CC will be the compiler to use.
CC=gcc
# CFLAGS will be the options to pass to the compiler.
CFLAGS=-c -Wall
all: compute
compute: algebra.o plusvec.o minusvec.o getvec.o
$(CC) algebra.o plusvec.o minusvec.o getvec.o -o compute
algebra.o: algebra.c
$(CC) $(CFLAGS) algebra.cpp
plusvec.o: plusvec.c
$(CC) $(CFLAGS) plusvec.c
minusvec.o: minusvec.c
$(CC) $(CFLAGS) minusvec.c
getvec.o: getvec.c
$(CC) $(CFLAGS) getvec.c
clean:
rm *o compute
Lorem ipsum dolor sit amet consectetur adipisicing elit. Ratione dolor explicabo repellendus, natus impedit, eaque itaque reprehenderit alias iure autem, officia aliquid cumque eligendi. Quis doloribus voluptates animi impedit accusantium.
Consider one of the previous examples where we compiled the sources into the executable like this: