Basics and Concepts of C programming

Execution of a C program starts with ‘main’ function. Let us define a function in the later sessions. But for now, assume that a function is a set of statements/instructions.
Can we write a C program without a function? The answer is obviously NO! Checkout the following program.
#include<stdio.h>  
printf("Before main..\n");   
int main()  
{  
 printf("In main..\n"); 
 return 0; 
}  
printf("After main..\n");   
Now, let us go through the four stages of compilation in C language and understand what happens in every stage of compilation. Checkout the program coded below. We use gedit editor in Ubuntu for editing/manipulating/viewing the contents of a file. The ‘vi’ editor can also be used. You can get the usage of vi editor here.
After preprocessing, you can observe that no error is produced, though two printf statements are mentioned outside any function. Also, for the few lines of code we have, the preprocessed file test.i produces a code close to 1000 lines, which consists of all the code present in the stdio.h header file. Let us move on to translation.
Here, this produces an error stating that…
Error: expected declaration specifiers or ‘…’ before string constant
This is because we have put two printf statements outside function (particularly main function here). Hence, it can be concluded that syntax errors are checked by the translator. The only way to rectify these errors is to place them inside a function. Let’s do it.
#include<stdio.h>  
void f1(void)  
{  
 printf("In f1\n");  
}  
int main()  
{  
 printf("In main..\n");
 return 0;  
}  
void f2(void)  
{  
 printf("After main..\n");  
}  
Now let us preprocess (test.i), translate (test.s) and assemble (test.o) the source code to generate the object file. We know the contents of the preprocessed file test.i – it consists of pure C code after removal of comments and respective file inclusion. After translation, the test.s file consists of the respective assembly code.  The object file of the translated code test.o can be generated by the assembler. Finally, the object file is converted into executable file ‘test’ using linker.
The object file (.o) consists of two sections:
Text section – consists of function body
Data section – consists of global and static variables.
After successfully generating the executable file, it can be executed as shown below.
./test
This produces the output as shown below.
It is evident that we have two more functions – f1 and f2 written in the code. What happened to these two functions? Are they present in the executable file? To figure out this, let us do some reverse engineering. Let us disassemble the executable file ‘test’ to another file ‘prog’. It can be done using the following command.
objdump -D test.o >prog
We can check the contents of the ‘prog’ file in text editors. We can clearly observe that the definition of both the functions f1 and f2 are present in the executable file. But, why are the printf statements not executed though they are present in the executable file? The simple answer is that the function f1 and f2 are not called.
But, we have been telling that ‘main’ is also function. Who called the ‘main’ function? The answer for this is the _start function. This _start function calls main and the rest of the functions are called based upon requirement. You can check the code for _start function in the ‘prog’ file calling the ‘main’ function, where _start function is called by the OS/kernel when the gcc compiler initiates compiling a given program.
Now, let us call the functions f1 and f2 from main as shown below.
#include<stdio.h>  
void f1(void)  
{  
 printf("In f1\n");  
}  
int main()  
{  
 printf("In main..first time..\n");  
 f1();  
 printf("In main..second time..\n");  
 f2();  
 printf("In main..third time..\n");  
 return 0;
}  
void f2(void)  
{  
 printf("After main..\n");  
}  
Now, when we preprocess this code, no errors are produced. But produces a warning stating that...
warning: conflicting types for ‘f2’ [enabled by default]
Why hasn’t this happened with the function f2? The answer is – because the function definition for f1 appears before the function call f1() but, the definition for f2 appears after the function call f2(). Hence, the gcc compiler can locate the address of f1, from where it should start executing but cannot locate the address of f2.

*Note that gcc is flexible in ignoring such function declarations when the function returns/accepts void/int data types. But strictly speaking according to ANSI C standards, it is an error.
As this is not an error (w.r.t gcc), we can proceed to further stages of compilation. After translation, we proceed for assembling; and then we go for linking. The effect of the warning shown during translation is shown here – a linking error producing some addresses. Hence, the function declaration or function prototype for f2 is mandatory at the top.
The conclusion is that if a function definition appears after the function call the function should be declared at the top of the program.
Let us declare f2, and compile the program, which produces that output as shown below.
#include<stdio.h>  
void f2(void);//declaration of f2  
void f1(void)  
{  
 printf("In f1\n");  
}  
int main()  
{  
 printf("In main..first time..\n");  
 f1();  
 printf("In main..second time..\n");  
 f2();  
 printf("In main..third time..\n");  
 return 0;
}  
void f2(void)  
{  
 printf("After main..\n");  
}  
Bypassing the four stages of compilation, we can simply generate the executable file using the following command.
gcc test.c
This command produces the default executable file a.out, which can be executed as follows:
./a.out
Now, let me answer the question – Can we write a C program without main function? The answer is YES! But, there is a catch.
While executing a C program without main function, the switch -nostartfiles is to be used as shown below. In this case, the _start function calls the first defined function in the program and continues execution in top-to-bottom fashion.
gcc test.c -nostartfiles
*Never forget that main is a user-defined function. Because, the programmer defines the functionality of the main function.  
Share:

0 comments:

Post a Comment