【10月更文挑战第3天】
1.预定义符号
C语⾔设置了⼀些预定义符号,可以直接使⽤,预定义符号也是在预处理期间处理的。
__FILE__ //进⾏编译的源⽂件
__LINE__ //⽂件当前的⾏号
__DATE__ //⽂件被编译的⽇期
__TIME__ //⽂件被编译的时间
__STDC__ //如果编译器遵循ANSI C,其值为1,否则未定义
int main()
{
printf("%s\n", __FILE__);//文件名
printf("%d\n", __LINE__);//行号
printf("%s\n", __DATE__);//日期
printf("%s\n", __TIME__);//时间
//printf("%s\n", ____STDC___); //无法运行,索命gcc并没有完全遵循ANSI C
//但是在vscode环境中是可以运行的
return 0;
}
2.#define定义常量
//#define M 100
//#define STR "hehe"
///*
//像这种#定义的M是怎么使用的呢?
//在预编译阶段的时候会将M后面的内容替换到M出现的位置
//*/
//int main()
//{
// int a = M;
// int arr[M] = { 0 };//在预编译阶段M已经被替换为100了
// printf("%d\n", M);
// printf("%s\n", STR);//这里的STR已经被替换成hehe了
// return 0;
//}
/*
更加牛逼的写法
*/
//#define CASE break;case
//int main()
//{
// int n = 0;
// scanf("%d", &n);
// switch (n)
// {
// case 1:
// break;
// case 2:
// break;
// case 3:
// break;
//
// }
// return 0;
//}
/*
上面的就是正常的写法了,但是我们将上面的CASE定义为break;case后代码就能进行建议更改了
*/
//int main()
//{
// int n = 0;
// scanf("%d", &n);
// switch (n)
// {
// case 1:
// //..
// CASE 2://break;case 2这就是替换过来的
// //..
// CASE 3:
// //..
//
//
//
// }
// return 0;
//}
/*
但是这个不是一种很好的写法,没有一个很好的阅读体验
*/
//那么我们在使用#define的时候要不要在后面加上分号呢?
//有时候加上也不会有太大的问题,但是不建议加
//加上可能会导致问题
//#define M 100;
//int main()
//{
// int a = M;//如果在上面对M进行定义的时候在后面加上了分号的话
// //那么这里的M会被替换为100;
// printf("%d", M);//这种直接会报错
// return 0;
//}
//所以我们在对变量进行定义的时候别在后面加分号,加上分号的话可能会出现两个语句
后面不能加分号
3.#define定义宏
define 机制包括了⼀个规定,允许把参数替换到⽂本中,这种实现通常称为宏(macro)或定义宏(define macro)。
宏其实是有参数的
下⾯是宏的申明⽅式:
define name( parament-list ) stuff
parament-list是参数列表,意味着宏是有参数的
其中的 parament-list 是⼀个由逗号隔开的符号表,它们可能出现在stuff中。
参数列表的左括号必须与name紧邻,如果两者之间有任何空⽩存在,参数列表就会被解释为stuff的
⼀部分。
如果name和括号直接加了空格的话,编译器会认为这里是将name定义为后面的东西的
//实现一个宏,求平方
//#define SQUARE(n) n*n
////SQUARE是宏的名字,括号内是宏的参数,宏的参数会替换到宏的体内去
//
//int main()
//{
// int x = 0;
// scanf("%d", &x);
// int ret = SQUARE(5 + 1);//计算的是x的平方
// //相当于这里的SQUARE(x)被替换为x*x
// printf("%d", ret);
// return 0;
//}
/*
宏和函数有不同点,宏没有参数类型,也没有函数的大括号
宏呢,适合完成那些相对简单的任务
*/
//但是我们假设传的是5+1的话,那么宏里面的计算内容就是这样的
//5+1*5+1
//得到的就是11
//所以我们得到的结果在宏内的参数是不会进行运算的
//而是直接将参数替换到宏内的运算里面
//宏的参数是不进行计算直接替换进去的
//但是我们给5+1加上一个括号就能计算(5+1)*(5+1)的内容了
//我们在写宏的时候不能吝啬符号
//求一个数的二倍
#define double(n) n+n
int main()
{
int n = 0;
scanf("%d\n", &n);
int ret = double(n);
printf("%d", ret);
ret = 10 * double(5);
//当我们想计算10*(5+5)的时候
//这里的double可能会被替换为5+5
//那么代码就会变成这样10*5+5
//解决方法就是在宏的定义中将n+n整体加上括号
printf("%d", ret);
return 0;
}
/*
所以我们在写宏的时候一定不要吝啬括号
*/
我们在写宏的时候一定不要吝啬括号
所以⽤于对数值表达式进⾏求值的宏定义都应该⽤这种⽅式加上括号,避免在使⽤宏时由于参数中的
操作符或邻近操作符之间不可预料的相互作⽤
当宏参数在宏的定义中出现超过⼀次的时候,如果参数带有副作⽤,那么你在使⽤这个宏的时候就可
能出现危险,导致不可预测的后果。副作⽤就是表达式求值的时候出现的永久性效果。
副作用就是表达式的求值会出现永久的效果
int main()
{
int a = 10;
//int b = a + 1;//b=11,a=10;//a没有做出改变
int b = ++a;//b=11,a=11;这个表达式为了获得b,a获得了牺牲
//那么下面的代码就具有副作用,为了得到一个新的值,改变了自身
return 0;
}
/*
x+1 不带有副作用
++x 带有副作用
下面 的写法对a进行了彻底的改变
*/
//如果我们将这种带有副作用的表达式放到宏的参数里面会发生什么现象呢?
////写一个宏,求两个数的较大值
//#define MAX(x,y) ((x)>(y)?(x):(y))//x大于y的话,那么较大值就是x,否则就是y
//int main()
//{
// int a = 10;
// int b = 20;
// int m=MAX(a,b);
// printf("%d", m);
// return 0;
//}
////在这个宏内,参数都出现了两次
//当我们对参数进行改变,变成a++,b++
#define max(x,y) ((x)>(y)?(x):(y))
int main()
{
int a = 10;
int b = 20;
//int m=max(a++,b++);//因为是后置++,所以我们将a先传过去,b先传过去
//替换后的代码--后置++,先使用再++
int m = ((a++) > (b++) ? (a++) : (b++));
//10 20 x 21
// 在前面b先用,再加加,所以到后面,b就是21了 ,再后面又是b++,所以先使用21的值,然后再自增
//所以这个表达式执行完之后,a变成了11,b变成了22
printf("%d", m);
printf("%d\n", a);
printf("%d\n", b);
return 0;
}
//在这个宏内,参数都出现了两次