当前位置: 首页> 教育> 幼教 > C语言-第十章:预处理

C语言-第十章:预处理

时间:2025/7/14 13:00:12来源:https://blog.csdn.net/2303_78095330/article/details/142218134 浏览次数:0次

传送门:C语言-综合案例:通讯录

目录

第一节:什么是预处理

       

第二节:#define

        2-1.定义符号

        2-2.给类型取别名

        2-3.定义一段代码

        2-4.定义宏

        2-5.#define 的替换规则

        2-6.宏中的#

        2-7.宏中的##

        2-8.#undef 移除一个宏定义

第三节:条件编译

        3-1.#ifdef

        3-2.#ifndef

        3-3.#if

        3-3.#elif 和 #else

第四节:文件包含

        4-1.库文件

        4-2.防止文件被多次包含

结语:


第一节:什么是预处理

        预处理是对代码进行的一些修改,从代码变成可执行程序,就先需要经过预处理。

        之前出现的 #define、注释都与预处理有关。进行预处理时,会将 define 定义的符号替换成预设的符号,注释会被删除。

        还有一些预处理符号,这些符号会被替换成相应的字符串,它们的写法和内容如下:

__FILE__它所在的文件路径

__LINE__

它所在的行号
__DATE__处理它时的日期
__TIME__处理它时的时间
__FUNCTION__它所在的函数名

        请看以下代码:

#include <stdio.h>
int main()
{printf("文件路径:%s\n",__FILE__);printf("当前行号:%d\n",__LINE__);printf("当前日期:%s\n",__DATE__);printf("当前时间:%s\n",__TIME__);printf("所在函数:%s\n",__FUNCTION__);return 0;
}

       

第二节:#define

        #define 定义的符号也需要经过预处理才能发挥作用,它会将代码中的符号替换成预设的符号,#define 有以下几种用法:

        2-1.定义符号

#define M 100 // 预处理时,会将 M替换成100
#include <stdio.h>
int main()
{int a = M;printf("%d\n",a);return 0;
}

        2-2.给类型取别名

#define un_t unsigned int // 给 unsigned int 取了一个短的名字 un_t
#include <stdio.h>
int main()
{un_t a = 100;printf("%d\n",a);return 0;
}

  

        关键字 typedef 也有给类型取别名的作用,它们之间也有区别,请看以下代码:

#define char_ptr char* // 给 char* 取了一个的名字 char_ptr
#include <stdio.h>
int main()
{char_ptr a,b;printf("a的大小:%u\n",sizeof a);printf("b的大小:%u\n",sizeof b);return 0;
}

  

typedef char* char_ptr;
#include <stdio.h>
int main()
{char_ptr a,b;printf("a的大小:%u\n",sizeof a);printf("b的大小:%u\n",sizeof b);return 0;
}

 

        它们都给char*取了别名,但是 #define 定义出来的变量类型却不一样,这是因为 #define 实行的是,直接替换,替换后的内容为:

char* a,b;

        第一个 * 与 a 结合,a就是指向char的指针,而b没有 * ,它就是char类型的变量。

        而 typedef 是类型重定义,它取的别名会作为一个整体给后面的变量,即:

char* a;char* b;
//或
char* a,* b;

        这是它们之间的一些区别,需要注意。

        2-3.定义一段代码

        例如我们需要知道当前文件路径+行号+日期+时间+函数名,如果用5个 printf,当需要多次调用时就太麻烦了,而且也不能封装成函数,这样的话行号和函数名就是被封装的函数的了,可以用#define,请看以下代码:

#define Print_Info printf("文件路径:%s\n",__FILE__);\printf("当前行号:%d\n", __LINE__);\printf("当前日期:%s\n", __DATE__);\printf("当前时间:%s\n", __TIME__);\printf("所在函数:%s\n", __FUNCTION__)
#include <stdio.h>
#include <Windows.h>
int main()
{Print_Info;printf("\n");Sleep(5); // 休眠5sPrint_Info;return 0;
}

        #define 中的 '\' 是续行符,在内容较多、需要换行时使用

        虽然我们写的是 Print_Info,但在预处理时,它会自动被替换成上述的5个printf。

        在上述代码和结果中,还需要注意的是:虽然程序休眠了5s再打印下一个Print_Info,但是它们的当前时间是一样的,这是因为预处理在程序执行前就完成了,即__TIME__在执行前就完成替换了,之后如何执行与它无关。

        2-4.定义宏

        宏是一种类似函数的结构,它也允许传参,一个简单的宏如下:

#define ADD(x,y) x+y // 宏
#include <stdio.h>
int main()
{printf("%d\n",ADD(1,2));return 0;
}

 

        它也可以传参,但是与函数传参不同,在传参过程中不会计算,而是直接替换,请看以下代码:

#define MUL((x),(y)) x*y
#include <stdio.h>
int main()
{printf("%d\n",MUL(1,2+3));return 0;
}

        如果MUL是个函数,传入2和2+3的参数,它会计算成2和5的参数传递进去,那么答案就是10,但是实际上的结果是7:

 

        这是因为宏的参数不会计算,而是直接替换,这个过程如下:

        那么怎么避免这种情况发送呢?,我们可以给宏中的x,y加上括号:

#define MUL(x,y) (x)*(y)

        这就相当于传参时先计算再传入。但是这样还不够保险,情看以下代码:

#define ADD(x,y) (x)+(y)
#include <stdio.h>
int main()
{printf("%d\n", ADD(2, 2 + 3)*2);return 0;
}

        这是一个加法宏,得到2与2+3的和再乘以2,结果似乎是14,但是实际上的答案是12:

 

        这是因为虽然参数先计算后传入,但是替换后y被2抢占先机了,即ADD(2, 2 + 3)*2替换后为

(2)+(2+3)*2,先乘除后加减,结果就是12了。

        又怎么避免它呢?我们可以给 (x)+(y) 又套上括号,让它们成为一个整体:

#define ADD(x,y) ((x)+(y))

 

        这样的"双重保险"下就几乎不会出错了。

        2-5.#define 的替换规则

                如果有宏,会先替换宏中的参数;

        其次再替换参数名、符号等;

        最后检查是否还有没替换的符号,如果有,继续替换;

        因为#define的替换规则是循环的,这代表着#define中可以嵌套其他#define的符号,请看以下代码:

#include <stdio.h>
#define ADD(x,y) ((x)+(y))
#define M 2
#define N 3
#include <stdio.h>
int main()
{printf("%d\n", ADD(M, M + N)*M);return 0;
}

 

        我们在宏中嵌套使用了#define的M、N,这是被允许的。

        但是宏不允许递归,因为没有结束条件,它会无限替换下去。

        还需要知道的是#define不是遇到M、N就替换,它无法替换字符串中的符号:

#define M 2
#define N 3
#include <stdio.h>
int main()
{printf("%s\n", "M,N");return 0;
}

 

        2-6.宏中的#

        #的作用是将一个字符插入字符串中,例如:

#define PRINT(x) printf("the value of "#x" is %d\n",x);
#include <stdio.h>
int main()
{int a = 10;PRINT(a);return 0;
}

  

        它的替换过程如下:

        2-7.宏中的##

        ##可以将两个部分连接成一个整体:

#define cat(x,y) x##y
#include <stdio.h>
int main()
{int a101 = 10;printf("%d\n",cat(a,101));return 0;
}

 

        它的替换过程如下:

        #和##几乎用不上,至少在我的学习过程中从来没有用过。

        2-8.#undef 移除一个宏定义

        它的使用方法如下:

#define M 100
#undef M // 移除M的宏定义
#include <stdio.h>
int main()
{int a = M;printf("%d\n",a);return 0;
}

 

第三节:条件编译

        条件编译与选择语句很像,它需要满足条件才编译这部分代码,可以跳过一些不需要编译的代码,节约了时间。

        3-1.#ifdef

        #ifdef 是如果定义了这个宏,就执行代码,否则不执行:

#define M 100// 定义了M
#include <stdio.h>
int main()
{#ifdef Mprintf("%d\n",M);#endif // 结束return 0;
}

  

        当然也可以不要后面预设的符号:

#define M// 定义了M
#include <stdio.h>
int main()
{#ifdef Mprintf("定义了M\n");#endif // 结束return 0;
}

  

        3-2.#ifndef

        ifndef与ifdef相反,不定义才执行,定义了反而不执行。

        3-3.#if

        #if后跟常量表达式,而不是宏。

        3-3.#elif 和 #else

        #elif 与之 #ifdef,如同 else if 与之 if,如何使用else if 就如何使用 #elif;#else 同理。

第四节:文件包含

        4-1.库文件

        文件包含使用 #include,它可以跟<>包含库文件,例如#include<stdio.h>;也可以跟""包含本地文件,例如我们写通讯录时的#include"Address.h"。

        它们也有差异:

        #include<stdio.h>会直接到标准库去查找,找不到就报错;

        #include"Address.h"会先去本地库中查找,找不到再去标准库查找,还没有就报错。

        虽然冒号可以查找本地库和标准库,但是库文件还是用<>,因为库文件查找就不需要去本地浪费时间了。

        4-2.防止文件被多次包含

        (1)#prama once

        将它放在头文件的最前面,那么该头文件不会被重复包含;

        (2)#ifndef 宏

                 #define 宏

                 #endif

        第一次包含该文件时,没有文件中的宏,就编译该文件,如果再次包含这个文件,因为有了文件中的宏就不再编译,这样也可以防止文件被多次包含。

结语:

        至此,我学到关于C语言的知识就已经完成了,但是我学习编程的道路还没有结束,实际上,我还欠很多博客没有写,比如数据结构、C++、Linux操作系统……接下来我也将努力完成它们。我不是计算机专业的学生,而是药学专业的学生,在大一下册的时候偶然接触到计算机编程,从此一发不可收拾,用了一万块报名了课外机构——比特就业课。至此每天上完课就写算法题,或者写博客,亦或者上编程课。

        大三寒假我想试一下找实习,所以我需要尽快完成博客。

        诸君,共勉。

关键字:C语言-第十章:预处理

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

责任编辑: