C语言编译连接过程和宏

来源:互联网 发布:为什么知乎很多反政府 编辑:程序博客网 时间:2024/06/16 07:56
C语言代码到二进制代码中间的四个转换:
1.预处理(宏替换、取注释、头文件展开、条件编译)
     对应的linux指令(gcc -E test.c -o test.i)//E预处理指令,在预处理之后停止,生成test.i文件
2.编译 (C语言代码   转换为 汇编代码)
     (gcc -S test.i -o test.s)//S编译指令,在编译之后停止,生成test.s文件
3.汇编 (汇编代码   转换为 二进制代码)
     (gcc -c test.s -o test.o)//c汇编指令,在汇编之后停止,生成test.o文件
4.链接 (将不同部分的代码和数据搜集组合成一个单一文件的过程)
     (gcc test.o -o mytest)//链接指令,在链接之后停止,生成可执行文)

1.什么是宏?
#define机制包括了一个规定,允许把参数替换到文本中,这种实现通常称为宏或者宏定义
ANSI 标准定义了如下几个宏
_LINE_ 表示正在编译的文件行号 
_FELE_ 表示正在编译的文件名字
_DATE_  表示正在编译时刻的日期字符串
_TIME_  表示编译时刻的时间字符串
_STDC_  表示该文件是不是定义成标准的C程序,其值为1,否则未定义
2.#define替换
在程序展开#define定义和宏的几个步骤
 1.调用宏时,检查参数本身是不是一个宏,如果是#define定义的符号,替换它
 2.替换文本插入到原来位置,对于宏,参数名被它们的值替换
 3.最后在进行扫描,看是否还有#define定义的符号,如果有替换它
例:
#include<stdio.h>   
      #define M 10                   
#define SUM(x) ((x)*(x)+(M)) 
int main()
{
    int a = 0;
    a = SUM(M);
    printf("a = %d\n",a);
    return 0;
}
在预处理时候a=SUM(M)先被替换成a=SUM(10)
a=SUM(10)在被替换成a=((10)*(10)+(M))
最后进行扫描发现还有被#define定义 的符号M,则在一次进行替换 a=((10)*(10)+(10))
在linux下生成进行完成预处理的文本
3.在定义宏函数时的注意事项
     1.每个实参实例应该被小括号括起来
例:
          a.#define SQRT(x) x*x
           当你SQRT(4+1)这样调用时,它会被替换成4+1*4+1==9,显然这个结果是不符合预期的
               所以你应该这样定义#define SQRT(x) (x)*(x),当然这样也是不完美的!
          b.#define SUM(x) (x)+(x)
           当你2*SUM(5)这样调用时,它会被替换成2*(5)+(5)==15,这样得到一个不符合预期的结果
               所以应该更加小心的定义它#define SUM(x) ((x)+(x))#define SQRT(x) ((x)*(x))
     2.用结构封装语句序列并确保其正确
例:
#include<stdio.h>
#define FUN() fun1();fun2()
#define fun1() printf("hello,")
#define fun2() printf("world\n")

int main()               int main()    
{                             {
    int flag = 1;               int flag = 1;  
    if(flag)                       if(!flag)
        FUN();                         FUN();
    return 0;        return 0;
}         }
结果:
hello,world    world
为什么有差异呢?看看预处理之后的结果
可以清楚地看出,与处理之后的结果与我们预想的逻辑并不是很相符
因为在定义宏函数时不能判断用户的使用环境,所以我们采用do-while-zero结构进行封装
我们把第一句宏定义经行处理  #define FUN() do{fun1();fun2();}while(0)
在预处理之后我们可以看到这样的结果
         
这个结果就符合我们的逻辑预期。
4. 宏参数插入字符串常量的方法
     1.临近字符串的连接特性
#define M 9
#define PRINT (FORMAT,VALUE)\
printf("The value is "FORMAT"\n",VALUE)
PRINT("%d",M);
结果:The value is 9
     2.使用"#"运算符进行处理
#define M 9
#define PRINT(FORMAT,VALUE) \
printf("The value of "#VALUE" is "FORMAT"\n",VALUE)
PRINT("%d",M);
结果:The value of M is 9
"#"用于函数宏的替换部分,可以将一个宏参数转换成一个字符串
     3."##"它把它两边的符号连接成一个符号
#include<stdio.h>
#define M 9
#define N 10
#define MN 20
#define PRINT() printf("%d\n", M##N)
int main()
{   
    PRINT();
    int flag = 1;
    return 0;
}
结果 : 20
在预处理时M##N连接在一块成为MN,MN又是一个被定义的宏,值为20
5.宏与函数的比较


#define宏
函数
代码长度
每次使用时,进行代码替换。代码出现在程序中,导致程序长度大幅增长
每次使用时 
执行速度
存在函数调用和返回时的额外开销
参数求值
参数每次用于宏定义,他们都将重新求值。
参数只在调用前求值一次
参数类型
与类型无关
与类型有关
操作符优先级
对于宏函数中的参数求值应该非常小心,除非加上括号,否则会因为优先级的原因产生不可预料的结果
函数参数只是在调用的时候求值一次,它的结果传递给函数,表达式的结果更容易预料
1 0
原创粉丝点击