One of the most annoying things new programmers confront with, especially while making transition from Windows environment to Unix environment is the lack of sophisticated IDE (Integrated Development Environment). Most of the compilation is done on command line using cc, gcc or g++. There are third party tools and IDEs available but these differ with the environment. Though some advanced editors such as VIM and EMACS provide some functionality such as syntax highlighting but eventually all of them call the command line compiler only. So this makes it important to know how the command line compiler works.
Compiling a single source C program
For all the programs we are using gcc. You can use other compilers such as cc or acc. All these compilers would vary mostly in the warnings and error messages. Programs can be written in any text editor and saved on to the disk.
Assuming the file is saved as main.c the program can be compiled as
Since we have not provided any extra option to the compiler the output file is saved as a.out. To save the output program with a different name -o option is provided. Note that the "o" here is in lower case which is very important since most of the compilers are case sensitive.
gcc main.c -o main
will save the output program as main.
Executing the output program
Setting the PATH variable : If we try running the program by typing "main" we would get an error message similar to "command not found". Discarding the case that the name of the program has been typed wrongly, the problem lies with the PATH environment variable. By default shell will find the program in the directories listed in the PATH variable and if the name of the current directory is not mentioned in the PATH variable then the error message is displayed. To run the program following things can be done:
Including the current directory in the path variable. This can be done by running
export PATH=$PATH:<current directory name>
on the command prompt before running the program. The effect of command will be there till the user is logged in. The command will have to given again when the user logs in the next time. A better way is to put this command in the .profile file of the user which will get executed every time the user logs in.
Typing the full path of the program for executing it i.e if the current directory is
/user/guest then the program can be executed as
Since each directory in Unix (also in DOS) contains two special directories . and .. . . points to current directory and .. points to the parent directory. So the program can be executed as:
Creating a debuggable code
To be able to debug a program a debuggable version of the program has to be created. This can be achieved by the -g option of the compiler.
gcc -g main.c -o main
This will create a debuggable version of main which can be debugged using a debugger such as gdb or ddd. Notice that the size of this program would be greater than the not debuggable version because of the extra information embedded in the program.
Stripping the debug information
strip command can be used to remove the debug information from the program.
will remove the debug information which was inserted into the program while compiling it with -g option. Even if the program is not compiled with -g option some debug information is inserted into the program which can be removed from strip command. So the size of main would be less than the normal compiled program.
Generating Object Code
Instead of creating a executable output program an intermediate code known as object code can be generated. This is especially useful while creating a multiple source file program. Object file of a program can be created using compile only flag (-c) of the compiler.
gcc -c main.c
This will create object file with the name main.o .
Compiling a Multi Source program
As the programs get bigger they get less maintainable within single source file. It is often reasonable to break program into several files. Such a program can be compiled in two different ways.
Compiling using multiple source files : Consider following three files in which the functionality of a small program has been divided
int add(int a, int b)
int subtract(int a, int b)
extern add( int a, int b);
extern subtract( int a, int b);
printf(" Addition of 3 and 2 gives %d \n",add(3,2));
printf(" Difference of 3 and 2 gives %d \n",subtract(3,2));
One point is worth noting here which is the extern keyword. Whenever a function is referenced in a file whose declaration is provided in different file, its prototype is declared in the file prefixed with extern keyword. This tells that the function code is present external to this file in which it is being used.
The program can finally be compiled as :
gcc add.c subtract.c main.c -o main
This technique has one disadvantage. Whenever code in any of the three files is changed then all of the three files have to be compiled.
Compiling using Object files : Instead of compiling each file every time files can be compiled separately into object files and linked together. This has the advantage that only files which have the change will have to be recompiled and only the linking effort has to be put in. The files can be compiled separately into object files as:
gcc -c add.c
gcc -c subtract.c
gcc -c main.c
This would create three object files. In the end an executable can be generated using these object files.
gcc main.o add.o subtract.o -o main
There are two points to note here:
1. The final step is same as providing c source files. The compiler is intelligent enough to figure out that the input files are object files because of the .o extesions.
2. The order of the object/source files at the command line is not important. The compiler will resolve the function names from within the files.
Providing path for including files
In the above examples we have assumed that all the files would be present in the current directory. By default when a file name is provided to the compiler on the command line, compiler will search file for that file in the current directory and in directories where headers for the standard libraries are installed. To include files from different directories without copying them into current or standard libraries path, compiler can be provided the path using the -I flag. Note that the case is important here. Assuming that the object have been kept in files directory in the parent directroy the program can be compiled as
gcc -I ../files add.o subtract.o main.o -o main