编译过程

编译过程简介:

 预编译:gcc -E file.c -o file.i

处理注释,以空格代替

将宏定义展开

处理条件编译指令

处理#include,展开被包含的文件

保留编译器需要使用的#pragma指令

编译: gcc -S file.i -o file.s

对预处理文件进行词法分析,语法 分析,语义分析

汇编:gcc -c file.s -o hello.o

将汇编代码翻译成机器码

链接:

将各个模块之间的相互引用的部分处理好,使得各个模块之间能够正确的衔接。将各个独立的模块链接成可执行的程序

静态链接:各个模块简单连接,编译期完成

动态链接:共享库文件,运行期完成

 

定义宏常量:

#define定义宏常量,可以出现咋代码的任何地方;从定义行开始,之后的代码都可使用这个宏常量(不管是否在某个函数中定义)

定义宏表达式:

 1 #include <stdio.h>
 2 int f1(int a, int b)
 3 {
 4     #define _MIN_(a,b) ((a)<(b) ? a : b)    
 5     return _MIN_(a, b);
 6 }
 7 int f2(int a, int b, int c)
 8 {
 9     return _MIN_(_MIN_(a,b), c);
10 }
11 int main()
12 {
13     printf("%d\n", f1(2, 1));
14     printf("%d\n", f2(5, 3, 2));   
15     return 0;
16 }

宏表达式的域:自定义行开始之后的所有代码都可以使用

定义宏只能在一行中完成,(使用接续符换行)

宏表达式与函数的对比:
宏表达式在预编译的过程中被处理,编译器不知道宏表达式的存在;宏表达式用实参 完全代替形参,不进行任何运算;宏表达式没有任何的调用开销;宏表达式不能出现递归定义。

#define func(n) ((n>0) ? func(n-1) : 0 ); //错误,递归定义宏

#undef:用于结束宏的作用域

条件编译的使用:

条件编译是预编译指示命令,用于控制是否编译某段代码;执行行为类似:if else语句。

1 #if (exp1)
2 ....;
3 #else
4 ...;
5 #endif

#include:
本质:将已经存在的文件内容,复制到当前文件中。(预编译中完成)

 1 // global.h
 2 int global = 10;
 3 
 4 // test.h
 5 #include <stdio.h>
 6 #include "global.h"
 7 
 8 const char* NAME = "Hello world!";
 9 
10 void f()
11 {
12     printf("Hello world!\n");
13 }
14 
15 // test.c
16 #include <stdio.h>
17 #include "test.h"
18 #include "global.h"
19 
20 int main()
21 {
22     f();
23     
24     printf("%s\n", NAME);
25     
26     return 0;
27 }

编译test.c时,由于global.h同时在test.h中包含了,又在test.c中包含了,这样int global = 10;就在test.c中重复了两次,定义了两次,提示出错。

 1 // global.h
 2 #ifndef _GLOBAL_H_
 3 #define _GLOBAL_H_
 4 int global = 10;
 5 #endif
 6 
 7 // test.h
 8 #ifndef _TEST_H_
 9 #define _TEST_H_
10 #include <stdio.h>
11 #include "global.h"
12 const char* NAME = "Hello world!";
13 void f()
14 {
15     printf("Hello world!\n");
16 }
17 #endif
18 
19 // test.c
20 #include <stdio.h>
21 #include "test.h"
22 #include "global.h"
23 
24 int main()
25 {
26     f();  
27     printf("%s\n", NAME);  
28     return 0;
29 }

使用条件编译,可以保证前面包含进来的(复制进来)内容,不再重复复制,而不管你写了多个头包含语句。

#error和#line:

#error用于生成一个编译错误信息,并停止编译

#error message  //message不需要使用双引号包围,message是程序员自定义的信息

#warring 用于生成警告,但不停止编译

 1 #include <stdio.h>
 2 #define CONST_NAME1 "CONST_NAME1"
 3 #define CONST_NAME2 "CONST_NAME2"
 4 int main()
 5 {  
 6     #ifndef COMMAND
 7     #warning Compilation will be stoped ...
 8     #error No defined Constant Symbol COMMAND
 9     #endif
10     printf("%s\n", COMMAND);
11     printf("%s\n", CONST_NAME1);
12     printf("%s\n", CONST_NAME2);
13     return 0;
14 }

#line用于强制指定新的行号和编译文件名,并对源程序的代码重新编号
本质:是重定义_LINE_和_FINE_(内部宏定义)

用法:#line number filename

 1 #include <stdio.h>
 2 #line 14 "Hello.c"   //__LINE__=14,即下一行行号为14;__FILE__="Hello.c"
 3 #define CONST_NAME1 "CONST_NAME1"
 4 #define CONST_NAME2 "CONST_NAME2"
 5 void f()
 6 {
 7     return 0;  // 该行行号为20,提示出错
 8 }
 9 int main()
10 {
11     printf("%s\n", CONST_NAME1);
12     printf("%s\n", CONST_NAME2);
13     printf("%d\n", __LINE__);  //27
14     printf("%s\n", __FILE__);  //hello.c  
15     f();  
16     return 0;
17 }


#pragma:

#pragma是编译器指示字,用于指示编译器完成一些特定的动作;它所定义的很多指示字是编译器和操作系统特有的;在不同的编译器间不可移植。

预处理器将忽略它不认识的#pragma指令

两个不同的编译器可能以两种不同的方式解释同一条#pragma指令

#pragma message

 

#和##号运算符:

#用于在预编译期将宏参数转换为字符串,注意:是在预编译期完成

 1 #include <stdio.h>
 2 #define CONVERS(x) #x  //#用于将CONVERS(x)中的x转换为x字符串
 3 int main()
 4 {   
 5     printf("%s\n", CONVERS(Hello world!)); //Hello world!
 6     printf("%s\n", CONVERS(100)); //100
 7     printf("%s\n", CONVERS(while)); //while
 8     printf("%s\n", CONVERS(return)); //return
 9     return 0;
10 }
 1 #include <stdio.h>
 2 #define CALL(f, p) (printf("Call function %s\n", #f), f(p))   
 3 int square(int n)
 4 {
 5     return n * n;
 6 }
 7 
 8 int f(int x)
 9 {
10     return x;
11 }
12 
13 int main()
14 {
15     printf("1. %d\n", CALL(square, 4));
16     // Call function square 
17     //1.16
18     printf("2. %d\n", CALL(f, 10)); 
19     //Call function f
20     //2.10
21     return 0;
22 }

##用于在编译期粘连两个符号:

 1 #include <stdio.h>
 2 #define NAME(n) name##n
 3 int main()
 4 {   
 5     int NAME(1);
 6     int NAME(2); 
 7     NAME(1) = 1;
 8     NAME(2) = 2; 
 9     printf("%d\n", NAME(1)); //1
10     printf("%d\n", NAME(2)); //2
11     return 0;
12 }

 

posted @ 2015-09-19 20:35  e-data  阅读(245)  评论(0)    收藏  举报