C语言关于预处理的基础介绍

一:预定义符号

        在c语言中有一些预定义的符号,这些符号已经定义好了,用户不需要再次定义只需要使用即可。

  1.  __FILE__ :显示当前进行编译的源文件
  2. __LINE__:显示当前代码行号
  3. __DATE__:显示当前文件被编译的日期
  4. __TMIE__:显示当前文件被编译的时间
  5. __STDC__:查看当前编译器是否遵循ANI C。如果返回值为1,反之结构未定义

        

        由此可见vs编辑器是不遵循ANI C标准的。

二:#define

        1.#define定义标识符:

        

从上示例可以看出,#define不仅可以定义符号常量,还可以定义字符串量。

        2.#define定义宏

                #define机制包括了一个定义,它允许把参数替换到文本中,这种实现方法成为定义宏。

                

这里需要注意 ADD后面的左括号需要紧跟着ADD,如果两者之间有空格那么都会解释成ADD宏的一部分。

        

          我们观察上图例子,可以看到MUL1与MUL2中的表达式一样只是区别在于,一个有加()一个没有,但输入相同参数的时,产生的结果确实两个不同的值。原因在于宏在使用时候会将参数直接替换,而上图示例宏将参数替换后变成两个不同的表达式,后根据符号优先级产生了不同的结果。   使用请注意:在写宏时请不要吝啬括号。

        3.#define的替换规则

                1.简单的文本替换#define 指令可以将标识符替换为指定的文本。

                2.带参数的宏: 宏也可以带有参数,类似于函数,在替换时,宏的名字会被他们的参数替换。

                3.宏定义的范围: 宏定义的作用范围从定义点开始一直到文件末尾,除非被undef指令取消。

                4.宏应避免递归

三:#与##

        1.#:

                首先来看下面一段代码

                

        可以看到在第二个printf语句中有两个字符串,而打印时会合并成一个字符串。

                        

        

        可以看到a和b的输出是相似的,此时可以想到是不是可以用函数来进行简化。但肯定的是函数是做不到这样的,因为函数只能改变其值,但不能改变文本如 of a 与 of b,函数做不到这样的替换,此时就可以就可以用到宏。

                

        上示例 定义了一个宏 将刚才printf语句变成宏的参数,将printf语句拆分成两个字符串,而在两个字符串中间 是#N,那么#的作用是:将#后的参数变成字符串。将原本的a的参数10替换成变量名a。那么下次在想输出类似的语句只需要调用宏传入参数即可。

       

        2.##:

                ##可以将位于它两边的标识符合成为一个标识符。

        

        观察上图,这定义了一个宏CAT,参数为(Class,num),其值为Class##num。意思是将Class与num连接,组成Classnum参数。 此时在main函数里定义int类型的变量Classnum,接着打印宏,将参数Class与num带入宏,就会组成刚才定义的Classnum的变量。如果此时将main函数里定义的Classnum参数注释掉或者改变成别的参数,那么结果是未定义的。

        



四:带副作用的宏参数

        如果宏参数在宏定义中出现超过一次的时候,如果此时宏参数带有副作用,那么在使用这个宏时可能会出现意想不到的危险,会与预想的结果出现严重偏差。

        例如: x+1 不带副作用(不会改变其x值) , x++带副作用 (会改变其x值)。     

            

        从上图可以看到,在宏定义中使用了三元运算符表达式。接着在main() 函数中,定义了两个整型变量 a 和 b,分别初始化为 5 和 4。然后调用了 MAX(a++, b++)。                   这会被展开成((5++)>(4++))?  在这个表达式中,(5++) 和 (4++) 会被进行比较,比较完再经行后置++操作,这里5与4先进行比较,接着5++变成了6,4++变成了5,而因为5>4,返回的是第二个表达式(a),而a这里会被替换成(6++)因为刚才5已经变成6了。又因为是后置++,先将数值6返回给ret,最后6++变成7。所以a=7,b=5(因为第三个表达式并没有执行),ret=6 。

      宏的缺点:        

  1.  宏是无法进行调试的,宏展开是在预处理阶段完成的,因此在编译器看到代码之前,它们已经被替换为具体的代码。
  2. 每一次使用宏时,一份宏定义的代码都会插入到程序中,除非宏比较短,否则可能会增加代码长度。
  3. 宏因为与类型无关,所以宏不够严谨。
  4. 使用宏的代码可能难以理解和维护,因为宏展开后的代码可能与原始代码有很大差异,而且不容易阅读。
  5. 宏是不安全的。由于宏是简单的文本替换,没有类型检查或作用域限制,因此可能会导致意想不到的行为。例如,如果在宏中使用了传递给它的表达式多次求值,就会导致副作用,如上面例子中的 a++ 和 b++ 被多次执行。

        

      移除宏:#undef

         #undef用来移除宏命令。

                

五:条件编译

        条件编译是一种编程技术,通常用于根据预定义的条件来控制程序的编译过程。条件编译通过预处理器指令来实现,比如#ifdef、#ifndef等指令。通过在源代码中使用这些指令,可以根据预定义的宏或条件来选择性地排除或添加代码块。

                        

       

关于判断宏是否被定义的指令:

                1.#if defined(宏)  || #ifdef 宏

                2.#if !defined(宏) || #ifndef 宏   

                  #if defined(MAX):定义了MAX就打印

                #if !defined(MAX):没有定义MAX就打印

                #ifdef MAX:定义了MAX就打印

                #ifndef:没有定义MAX就打印

六:头文件包含

        文件包含是通过预处理器指令 #include来实现的。#include 指令告诉编译器在编译时将指定的文件内容包含到当前文件中。这种机制使得可以将一些通用的代码放在单独的文件中,然后通过 #include 指令将其包含到需要使用的程序中。

            从上图示例可以看出 我的test.c的文件只有#include"head.h"的文件包含,编译器会在源文件下的目录进行查找头文件,如果该文件未找到,编译器就会像查找库函数头文件一样在标准位置下查找头文件。如果找不到就会发生编译错误提示。文件还可以进行嵌套包含,上图例子就是在head.h的文件中包含着库文件。

       

        关于< >与" "的区别:

            < >与" "的查找策略不同。

               #include<stdion.h> :查找策略为直接去库目录下查找。

              #include"head.h":查找策略为先去代码所在的路径下查找,如果找不到再去库目录下找。

        

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值