C语言深度解剖读书笔记(3.预编译处理)

来源:互联网 发布:武汉明源软件 编辑:程序博客网 时间:2024/06/01 19:21

本节知识点:

1.编译过程的简介:

   预编译:

a.处理所有的注释,以空格代替。

b.将所以#define删除,并展开所有的宏定义,字符串替换。

c.处理条件编译指令#if,#ifdef,#elif,#else,#endif

d.处理#include,并展开被包含的文件,把头文件中的声明,全部拷贝到文件中。

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

怎么样观察这些变化呢?最好的方法就是在GCC中,输入预处理指令,可以看看不同文件经过预处理后变成什么样了,预处理指令:gcc -E file.c -o file.i   注意:-C -E一起使用是预编译的时候保留注释。

   编译:

a.对预处理文件进行一系列词法分析,语法分析和语义分析

                词法分析:主要分析关键字,标示符,立即数等是否合法

                语法分析:主要分析表达式是否遵循语法规则

                语义分析:在语法分析的基础上进一步分析表达式是否合法

b.分析结束后进行代码优化生成相应的汇编代码文件               编译指令:gcc -S  file.c  -o  file.s

   汇编:

汇编器将汇编代码转变为机器可以执行的指令,每个汇编语句几乎都对应一条机器指令,其实机器指令就是机器码,就是2进制码。汇编指令:gcc  -c  file.c  -o file.o  注意:-c是编译汇编不连接。

   链接:

再把产生的.o文件,进行链接就可以生成可执行文件。连接指令:gcc  file.o  file1.o  -o  file  这句指令是链接file.o和file1.o两个编译并汇编的文件,并生成可执行文件file。

链接分两种:静态链接和动态链接,静态链接是在编译器完成的,动态链接是在运行期完成的。静态链接的指令是:gcc -static file.c -o file对于一些没有动态库的嵌入式系统,这是常用的。

一般要想通过一条指令生成可执行文件的指令是:   gcc file.c  -o  file

   资料:这里面说到了很多关于gcc的使用的问题,我提供一个gcc的学习资料,个人觉得还不错,也不长,就是一个txt文档,很全面。资源下载地址http://download.csdn.net/detail/qq418674358/6041183   Ps:嘿嘿,设了一个下载积分,因为真的是没分用了!希望大家见谅哈!

 

2.c语言中的预处理指令:#define、#undef(撤销已定义过的宏名)、#include、#if、#else、#elif、#endif、#ifdef、#ifndef、#line、#error、#pragma。还有一些ANSI标准C定义的宏:__LINE__、__FILE__、__DATA__、__TIME__、__STDC__。这样使用printf("%s\n",__TIME__);     printf(__DATE__);

一个#undef的例子:

[cpp] view plaincopy
  1. #include <stdio.h>  
  2. #include <stdlib.h>  
  3. #include <string.h>  
  4.   
  5.   
  6. #define X 2  
  7. #define Y X*2  
  8. #undef X  
  9. #define X 3  
  10.   
  11.   
  12. int main()  
  13. {  
  14.     printf("%d\n",Y);  
  15.     return 0;  
  16. }  

这个输出的是6,说明了#undef的作用

3.宏定义字符串的时候:应该是 #define HELLO "hello world"  记住是双引号。还有就是一切宏都是不能有分号的,这个一定要切忌!!!

4.宏与函数的比较:

   a.宏表达式在预编译期被处理,编译器不知道有宏表达式存在

   b.宏表达式没有任何的"调用"开销

   c.宏表达式中不能出现递归定义

5.为什么不在头文件中定义全局变量:

如果一个全局变量,想要在两个文件中,同时使用,那这两个文件中都应该#include这个头文件,这样的话就会出现重复定义的问题。其实是重名的问题,因为#include是分别在两个文件中展开的,试想一下,如果在两个文件中的开始部分,都写上int  a = 10;  是不是也会报错。可能你会说那个#ifndef不是防止重复定义吗?是的 ,那是防止在同一个文件中,同时出现两次这个头文件。现在是两个文件中,所以都要展开的。全局变量就重名了!!!所以 对于全局变量,最好是定义在.c文件中,不要定义在头文件中。

6.#pargma pack 设置字符对齐,看后面一节专门写字符对齐问题的!!!

7.#运算符(转换成字符串):

    假如你希望在字符串中包含宏参数,那我们就用#号,它把语言符号转换成字符串。

    #define SQR(x) printf("the "#x"lait %d\n",((x)*(x)));
    SQR(8)
    输出结果是:the 8 lait 64   这个#号必须使用在带参宏中

有个小例子:

[cpp] view plaincopy
  1. #include <stdio.h>  
  2. #include <stdlib.h>  
  3. #include <string.h>  
  4. /*在字符串中  加入宏参用的*/  
  5. #define SCAN(N,String) scanf("%"#N"s",String);  //N是截取的个数  String是存储的字符串   
  6. int main()  
  7. {  
  8.     char dd[256];  
  9.     SCAN(3,dd) //记得没有分号哈  自定义 任意格式输入的scanf  截取输入的前三个   
  10.     printf("%s\n",dd);  
  11.     return 1;  
  12. }  

8.##运算符(粘合剂)

    一般用于粘贴两个东西,一般是用作在给变量或函数命名的时候使用。如#define XNAME(n) x##n

    XNAME(8)为8n   这个##号可以使用在带参宏或无参宏中

下面是一个##运算符的小例子,代码如下:

[cpp] view plaincopy
  1. #include <stdio.h>  
  2. #include <stdlib.h>  
  3. #include <string.h>  
  4.   
  5. #define BL1 bb##ll##1  
  6.   
  7. #define BL(N) bbll##N  
  8. int main()  
  9. {  
  10.     int BL1=10;  
  11.   
  12.     int BL(4)=15;  
  13.     printf("%d\n",bbll1);  
  14.       
  15.     printf("%d\n",bbll4);  
  16.     return 1;  
  17. }  

注意:#号和##号都必须只能在宏定义中使用,不能使用在其他地方
9.其实预编译这块还有一些,不常用到的预编译指令,也是盲点,但是不难理解,用到的时候查查就好。比如说#line、#error、#warning等。

 

原创粉丝点击