首先熟悉一些预处理标识符
__FILE__ :进行编译的文件
__LINE__ :文件当前行号
__DATE__ :文件被编辑的周期
__TIME__ :文件被编辑的时间
# :使用预处理器将宏参数转换成一个字符串
##:将位于它两边的符号连接成一个符号。
示例 :
__FILE__ :用LINUX指令来观察它的预处理结果和最后输出的结果
#include<stdio.h>
int main()
{
printf("file:%s\t,line:%d,date:%s,time:%d\n",__FILE__,__LINE__,__DATE__,__TIME__);
return 0;
}
__LINE__ ,__DATE__ ,__TIME__也是同理,因此将它们一次写完观察预处理之后的结果即gcc -E mytest.c -o mytest.i,也可以直接观察程序运行完之后的结果即gcc mytest.c 然后用./a.out观察程序运行完的结果
由以上结果可以看出程序在预处理之后就已经将__LINE__ ,__DATE__ ,__TIME__这些预处理标识符用 ”mytest.c",4,"Nov 3 2016 ","10:24:40"替换了即预处理之后的结果就等同于程序运行完之后的结果。
示例:
'#‘使用预处理器将宏参数转换成一个字符串代码如下:
#define M 10
#define PRINT(FORMAT,VALUE) printf("the value of" #VALUE"is" FORMAT"\n",VALUE);
#include<stdio.h>
int main()
{
PRINT("%d",M);
return 0;
}
预处理之后的结果:
由以上的结果就可以看出在预处理之后宏参数通过’#‘就已经转换成一个字符串,而程序运行完之后的结果也是宏参数M变成了一个字符串。
’##‘使用是将位于它两边的符号连接成一个符号
2、了解宏和函数
1、宏可以非常频繁地用于执行简单的运算,可以进行参数的替换
2、宏可以进行临近字符串的拼接并且可以替换掉其中的一部分
3、对函数中的实参和形参都要定义类型,二者的类型要求一致,如不一致,应进行类型转换;而宏不存在类型问题,宏名无类型,它的参数也无类型,只是一个符号代表,展开时带入指定的字符即可。宏定义时,字符串可以是任何类型的数据。
4、宏不可以出现递归
5、参数宏在定义时要多加小心,多加括号。
6、:函数调用时,先求出实参表达式的值,然后带入形参。而使用带参的宏只是进行简单的字符替换。在考虑效率的时候,可以考虑使用宏
7、在宏定义代码块时加上do while(0)
和函数相比,使用宏的不利之处在于每次使用宏时,一份宏定义代码拷贝将插入到程序中,除非宏非常短,否则使用宏可能会大幅度增大代码长度。但和函数相比有一些任务使用函数无法实现比如:
#define MALLOC(n,type) ((type*)malloc((n)*sizeof(type)))
int *pi=MALLOC(25,int);
这个宏的第二个参数是一种类型,它无法作为函数参数进行传递
这个宏替换完成之后:
int *pi=((int*)malloc((25)*sizeof(int)));
4 带副作⽤的宏参数
当宏参数在宏定义中出现次数超过⼀次时,如果这个参数具有副作
⽤,那么当你使⽤这个宏时就可能出现危险,导致不可预料的结果。副作
⽤就是在表达式求值时出现的永久性效果。例如,下⾯表达式
x + 1;
可以执⾏⼏百次,他每次获得结果都是⼀样的,这个表达式不具有副作
⽤。但是
x ++;
就有副作⽤:它增加x的值。当这个表达式下⼀次执⾏时,他将产⽣⼀个
不同的结果。MAX宏可以证明具有副作⽤的参数所引起的问题。
#define MAX( a, b ) ( (a) > ( b) ? (a ) : (b) )
int x = 5;
int y = 8;
int z = MAX( x++, y++);
printf("x = %d, y = %d, z = %d\n" , x, y, z );
可以用LINUX指令查看一下它的与处理结果
即它在进行选择时对y又进行了一次++
故y++执行了两次最后结果为10,其实比并不符合我们预期的结果故MAX宏可以证明具有副作⽤的参数所引起的问题。
3、理解编译链接整个过程和详细的每个过程
从C语言转换成机器能够识别的二进制需要如下几个过程
预处理(宏替换,去注释,头文件按照路径展开,条件编译)-->编译-->汇编-->链接
以这个程序为例
#define MAX(a, b) ((a) > (b) ? (a) : (b))
#include<stdio.h>
int main()
{
int x = 5;
int y = 8;
int z = MAX(x++, y++);
printf("x = %d, y = %d, z = %d\n", x, y, z);
return 0;
}
~
先来看预处理之后的结果:使用gcc -E www.c -o ww.i 和vim.i指令
接下来进行编译使用gcc -S www.i -o www.s 和vim.s指令
接下来在进行汇编使用gcc -c www.s -o www.o 和vim www.o指令
最后是链接过程采用gcc www.o -o www 和vim www指令