目录
前言
前面我们笼统地介绍过了翻译环境和运行环境。接下来,我们会进一步去了解它们。
一、预处理
1.预定义符号
__FILE__ //进行编译的源文件
__LINE__ //文件当前的行号
__DATE__ //文件被编译的日期
__TIME__ //文件被编译的时间
这些预定义符号都是语言内置的,我们可以直接使用。
#include<stdio.h>
int main()
{
printf("file:%s\n",__FILE__);
printf("line:%d\n", __LINE__);
printf("date:%s\n", __DATE__);
printf("time:%s\n", __TIME__);
return 0;
}
输出结果:
2.#define
2.1 #define定义标识符
#include<stdio.h>
#define M 20
int main()
{
int a = 0;
a = M;
printf("%d", a);
return 0;
}
这里一定要注意#define语句后面没有加分号!!! 很容易出问题的。
2.2.#define定义宏
#define机制包括了一个规定,允许把参数替换到文本中,这种实现通常称为宏或者定义宏。
宏的申明方式:
#define name(parament-list) stuff
例:
#include<stdio.h>
#define MAX(x,y) (x)>(y)?(x):(y)
int main()
{
int a = 1;
int b = 2;
printf("%d",MAX(a, b));
return 0;
}
虽然看似简单,但是还有一些需要注意的点哟~我们再看一下下面的代码:
#include<stdio.h>
#define SQUARE(x) x*x
int main()
{
printf("%d",SQUARE(2+3));
return 0;
}
运行结果是11。
我们的原意是想打印5*5的,为什么结果是11呢?原来替换后的结果长这样:
#include<stdio.h>
int main()
{
printf("%d",2+3*2+3);
return 0;
}
2+3*2+3=11。这就对上了。我们就要修改一下我们的代码,加上括号。
#include<stdio.h>
#define SQUARE(x) (x)*(x)
int main()
{
printf("%d",SQUARE(2+3));
return 0;
}
这样子就能得出我们想要的结果了。
紧接着看下一个代码:
#include<stdio.h>
#define DOUBLE(x) (x)+(x)
int main()
{
printf("%d",2*DOUBLE(5));
return 0;
}
运行结果:
我们这边本来是想算2*(5+5)的,结果应该是20。原来替换的结果是这个样子的:|
#include<stdio.h>
int main()
{
printf("%d",2*5+5);
return 0;
}
2*5+5=15。这个结果也就对上了。
所以,我们需要加括号的时候一定要加括号。大家千万不能贪图一下子的省事,不在#define语句加括号,而是在用的时候加,那样子就是一逸永劳了。
2.3 #define 替换规则
1.在调用宏时,首先对参数进行检查,看看是否包含任何由#define定义的符号,如果是,它们首先被替换。
2.替换文本随后被插入到程序中原来文本的位置。对于宏,参数名被他们的值所替换。
3.最后,再次对结果文件进行扫描,看看它是否包含任何由#define定义的符号。如果是,就重复上述处理过程。
【注意点】
1.宏参数和#define定义中可以出现其他#define定义的符号。但是对于宏来说,不能出现递归。
举个例子:
#include<stdio.h>
#define M 10
#define DOUBLE(x) ((x)+(x))
int main()
{
printf("%d",DOUBLE(M));
return 0;
}
这是可以运行的,没有报错。
//error
#include<stdio.h>
#define M 10
#define DOUBLE(x) ((x)+(x))
#define ADD(c,d) ((c)+(d))
int main()
{
int a = 1;
int b = 2;
printf("%d",DOUBLE(ADD(a,b)));
return 0;
}
而这个样子的代码,就会报错。
2.当预处理器搜索#define定义的符号是,字符串常量的内容并不会被搜索到。
#include<stdio.h>
#define M 10
int main()
{
printf("M");
return 0;
}
2.4.#的作用
使用#,把一个宏参数变成对应的字符串。
#include<stdio.h>
#define PRINT(val,format) printf("the value of "#val" is "format"\n",val)
int main()
{
int a = 10;
PRINT(a, "%d");
int b = 20;
PRINT(b, "%d");
float f = 3.5f;
PRINT(f, "%f");
return 0;
}
运行结果如下:
2.5.##的作用
##可以把位于它两边的符号合成一个符号。它允许宏定义从分离的文本片段创建标识符。
#include<stdio.h>
#define CAT(a,b) a##b
int main()
{
int xy =1;//定义我们要合成的符号
printf("%d", CAT(x, y));
return 0;
}
2.5.带有副作用的宏参数
当宏参数在宏的定义中出现超过一次时,如果参数带有副作用,那么我们在使用这个宏的时候就可以出现危险,导致不可预测的后果。副作用就是表达式求值的时候出现的永久性效果。
如:
#include<stdio.h>
#define MAX(x,y) ((x)>(y)?(x):(y))
int main()
{
int a = 3;
int b = 4;
int m = MAX(++a,++b);
printf("m=%d a=%d b=%d",m,a,b);
}
这里我们的结果是:
我们来解释一下,我们的结果为什么是6,4,6。
int m=MAX(++a,++b)
#可以替换成:
int m=((++a)>(++b)?(++a):(++b))
3+1=4 4+1=5
4<5 5+1=6
m= 6
a= 4
b= 6