C语言--预处理详解(1)

简介: 【10月更文挑战第3天】

【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;
}
//在这个宏内,参数都出现了两次
相关文章
|
2月前
|
编译器 Linux C语言
C语言--预处理详解(3)
【10月更文挑战第3天】
|
2月前
|
自然语言处理 编译器 Linux
【C语言篇】编译和链接以及预处理介绍(上篇)1
【C语言篇】编译和链接以及预处理介绍(上篇)
42 1
|
1月前
|
C语言
【c语言】你绝对没见过的预处理技巧
本文介绍了C语言中预处理(预编译)的相关知识和指令,包括预定义符号、`#define`定义常量和宏、宏与函数的对比、`#`和`##`操作符、`#undef`撤销宏定义、条件编译以及头文件的包含方式。通过具体示例详细解释了各指令的使用方法和注意事项,帮助读者更好地理解和应用预处理技术。
25 2
|
2月前
|
编译器 Linux C语言
【C语言篇】编译和链接以及预处理介绍(下篇)
【C语言篇】编译和链接以及预处理介绍(下篇)
33 1
【C语言篇】编译和链接以及预处理介绍(下篇)
|
2月前
|
C语言
C语言--预处理详解(2)
【10月更文挑战第3天】
|
2月前
|
编译器 C语言
C语言预处理详解
C语言预处理详解
|
2月前
|
存储 C语言
【C语言篇】编译和链接以及预处理介绍(上篇)2
【C语言篇】编译和链接以及预处理介绍(上篇)
38 0
|
4月前
|
存储 自然语言处理 程序员
【C语言】文件的编译链接和预处理
【C语言】文件的编译链接和预处理
|
4月前
|
程序员 编译器 C语言
C语言中的预处理指令及其实际应用
C语言中的预处理指令及其实际应用
90 0
|
6月前
|
C语言 编译器 开发者
【C语言基础】:预处理详解(二)
【C语言基础】:预处理详解(二)