大一的时候,学习c语言,用的是VC6.0。用了1年多,到后来了解了Linux,知道了gcc编译器,开始使用gcc Hello.c -o a.out 这样的命令进行编译。后来又学了gcc的一些其他的命令,不同的命令可以编译出不同的目标代码。现在想想类似于VC这种IDE虽然方便,但是对于具体是怎样的一个过程就不得而知了。作为一个优秀的程序员怎么可以不了解这个过程呢。
Gcc/g++ 在执行编译工作的时候,总共4步
1.预处理,生成.i的文件 (预处理器cpp)
2.将预处理后的文件转换成汇编语言,生成文件.s文件 ()
3.从汇编变为目标代码(机器代码)生成.o(.obj)的文件 (汇编器as)
4.连接目标代码,生成可执行程序 (连接器ld)
我们现在先写一个Hello World吧
1 #include <stdio.h>
2 #define WOELD "World"
3 static int a=0;
4 int main()
5 {
6 int j,jj;
7 int i=0;
8 char ch='a';
9 printf("Hello ");
10 printf(WOELD);
11 printf("
%d %d %c
",i,a,ch);
12 return 0;
13 }
先预处理一下,可以使用cpp命令,或者是使用gcc 的-E选项
gcc -E Hello.c > Hello.i
一个简单的Hello World都要800多行。
可以看到这些都是函数的声明。而函数的定义是在链接库中。我们可以认为是写了好多函数了。然后在main中调用。这个跟一般的函数是一个道理的。只是这些函数是又编译器帮你写了。这个就不得不提到,C语言只是定义了标准库函数的输入和输出,至于实现的过程,是没有规定的。这个由编译器厂商自己设计,这就是为什么有人说Linux下gcc编译后的程序会比Windows下VS编译后的程序运行效率上有些区别的一个原因吧。
像上面的printf函数的原型,如果有兴趣可以去下载源代码查看。
下面这个是VC6.0对printf函数的定义

1 int __cdecl printf (
2 const char *format,
3 ...
4 )
5 /*
6 * stdout 'PRINT', 'F'ormatted
7 */
8 {
9 va_list arglist;
10 int buffing;
11 int retval;
12 va_start(arglist, format);
13 _ASSERTE(format != NULL);
14
15 _lock_str2(1, stdout);
16 buffing = _stbuf(stdout);
17 retval = _output(stdout,format,arglist);
18 _ftbuf(buffing, stdout);
19 _unlock_str2(1, stdout);
20 return(retval);
21 }
Gnu也有个对printf函数的定义,下面这个是glibc-2.2.5对printf的定义

1 int
2 printf (const char *format, ...)
3 {
4 va_list arg;
5 int done;
6 va_start (arg, format);
7 done = vfprintf (stdout, format, arg);
8 va_end (arg);
9 return done;
10 }
11 /* The function itself. */
12 int
13 vfprintf (FILE *s, const CHAR_T *format, va_list ap)
14 {
15 /* The character used as thousands separator. */
16 #ifdef COMPILE_WPRINTF
17 wchar_t thousands_sep = L'