我们的程序在从我们的.c源文件到.exe可执行文件会经过编译和链接的过程,在这个过程中有两个环境
翻译环境:
编译:就是生成.obi目标文件的过程,在这个中项目每一个源文件都会经过编译器生成一个目标文件,再通过链接器,将在所有的目标文件和链接库(静态库、动态库,类似于头文件里的函数库)链接起来生成可执行文件,而编译也分为几个过程预编译、编译、汇编过程才能生成.obj文件。预编译过程会打开头文件,头文件里的内容会被包含到我们的程序源文件中:还会用空格替换掉程序里的注释;还会对#define定义的符号,进行替换,所有的预处理阶段进行的是文本操作,把该替换的替换掉生成.i(gcc)文件;编译的过程是将我们的代码编译成汇编代码,生成.s(gcc)文件,这个过程还包括,语法分析,词法分析,语义分析,还有符号汇总(这里的符号包括一些函数名啊之类的exp:main,全局变量);汇编过程会生成.o文件,形成符号表(在汇总符号之后会形成一个符号表,内容是程序里的符号以及这些符号的地址,队友重复的符号,也会进行判断合并),还有将汇编指令转化成二进制指令
链接:生成可执行文件的过程,包含合并段表,符号表的合并和重定位,这里提到的段表,是由于每个生成的目标文件都是有固定的格式的,通过格式将目标文件分成好几个段形成段表,而这种格式叫elf文件格式,而每个.o文件进行链接的时候会将相应段上的数据按照一定规则合并到一起,这个过程叫做合并段表;在一个源文件中声明函数的函数符号是没有有效地址的,假设我们在一个函数中要使用另一个文件中的函数,需要extern指令,而这时的外应用函数的函数符号,没有有效地址,所以要结合拥有着函数的文件,在合并符号表的时候会将拥有这个函数的相应函数符号以及地址,与要使用这个函数的文件符号表进行合并,只放那个有有效地址的符号,以便可以找到这个函数。
预处理选项:gcc -E text.c -o text.i
编译选项:gcc -S text.c
汇编选项:gcc -C text.c
运行环境:
运行程序必须载入到内存中,这个一般又操作系统完成,而独立的环境下,需要手动载入。载入内存中之后,便开始调用main函数,最终以main函数结束。
1.预编译——预定义符号
__FILE__ 代码所在文件的完全路径,以及文件名
__LINE__ 代码现处于哪一行(行号)
__DATE__ 获取写代码的日期
__TIME__ 获取写代码的时间
__FUCTION__ 代码存在的函数
__STDC__ 遵守标准C,其值为1,否则未定义
预处理指令
#define、#pragma....等以#开头的都是预处理指令,可以定义宏(无参宏,就是定义一个符号,等待替换exp:#define MAX100,再执行程序的之前会将代码中的MAX替换成100,当然一个通过定义一个代码段、表达式为一个特定的符号,去替换)
#define MAX 100 #define reg regiest #define do_forever for(;;) #define CASE break;case//这里有个默认规定,定义宏的时候这些符号一般采用大写
在这里的要注意一般不要定义标识符时候,在最后加上语句结束标志“;”,避免替换的时候,造成错误,或者无故的增加运算开销(这里指的是空语句)
#include#define MAX 100; int main() { int a = MAX; //这里经过预编译之后会变成int a = 100;; 语法上没什么问题,但多了一句空语句,增加了运算开销,而 //有的时候替换的位置并不一定是语句末尾,这样的情况在预处理替换之后会造成错误,所以建议不要再定义宏的 //时候,在最后加“;” printf("%d",a);//100 return 0; }
#define也允许把参数定义到文本中,定义有参宏的时候,可以带参
#define name(parament-list) stuff 其中的paramet是用逗号隔开的符号表,类似于函数的参数,而这些符号可能出现在stuff中
#include#define ADD(x,y) x+y #define MUL(x,y) x*y //define MUL(x,y) (x)*(y) int main() { int a; a=ADD(1,2); //a=1+2;再替换的时候将会把参数先传过去,然后再替换到相应位置 //有的时候要注意的在宏传参的时候的这种情况 int add=0; add=MUL(1+2,5+2);//你一定会想add=21 //但其实是这样替换的add=1+2*5+2=13所以遇到这样的情况最好在定义宏的时候在参数的后面加上括号 return 0; }
而在字符串里的相应符号不会被替换
#和##应用
#可以将一个字符(宏的参数)插入到字符串中
#include//#define PRINT(X) printf("the value of X is:%dn",X) //int main() //{ // int a = 20; // int b = 30; // //我们要实现的是通过我们传过去的宏参数,打印相对应的值,就是传a的时候就在屏幕上打印the value of a is:传b的时候 // //就是the value of b is: // PRINT(a); // //the value of X is:20 // PRINT(b); // //the value of X is:30 // //这样不合我们的意愿 // // return 0; //} #define PRINT(X) printf("the value of "#X " is:%dn",X) int main() { printf("hello ""worldn"); //照样可以在屏幕上打印 hello world,按照这个原理,就是在printf这个指令中可以将一个字符串分成几个单另的字符串照常输出 int a = 20; int b = 30; //这个#可以将一个字符加入到字符串中 PRINT(a); //the value of a is:20 PRINT(b); //the value of b is:30 return 0; }
##的应用:这个符号可以将两个分离的符号合并成一符号
#include#define CAT(x,y) x##y int main() { int goodboy = 1; printf("%dn", goodboy);//1 printf("%dn",CAT(good,boy));//1 return 0; }



