C语言从头学42——预处理指令(一)

一、基本概念
       C语言编译器在编译程序之前,会先使用预处理器处理代码。预处理器进行删除注释、删除空行、多行语句合成一个逻辑行等等工作。然后,执行 "#" 开头的预处理指令。#号开头是预处理指令的典型特征,比如:#include <stdio.h> 就是预处理指令。
       本文重点就是学习预处理指令。预处理指令习惯上写在代码的开头部分。每个预处理指令都以 # 开头,放在一行的行首,预处理指令应当写成一行,除非在行尾使用反斜杠,将其折行。预处理指令结尾处不需要分号,这是也是预处理指令的特征。
二、预处理指令 #define
        #define 是使用频率很高的预处理指令。define 翻译成中文是定义、下定义的意思。一条完整#define 指令称为一个宏,宏这个概念在C语言中使用非常广泛。宏的本质就是用易记、易理解的内容替换不易记、不易理解的内容,进到编译器中再替换回来;这一连串的替换容易把人搞糊涂,所以我们区分一下概念:在宏命令中用于替换其它内容的称之为"宏名",被替换的部分称为"宏替换内容",到编译器内又恢复的内容称为"宏展开"。
1.使用#define定义无参宏
        使用格式:#define 参1 参2 //注意:参1与参2间只有空格,参数之间无逗号、结尾无分号
              参1 宏名 参2 宏替换内容(宏名的命名规则同变量)
        举例:#define MAX 100 //代码中凡需使用100的地方,都可以使用MAX,到编译器中MAX又展开成100
       说明:a. #define命令后可以跟注释符号"//"或"/* */";b. 宏是原样替换,程序执行时宏名会按照所途欢的内容展开;c. #define命令可出现在程序任何地方,但习惯上写在头部,如是全文件用的宏,应写在main()前面;d. #define命令应写在一行,一行容不下时,可用"\"转到下一行,转行符后不能加注释;e. 允许多重替换,即一个宏可以作为另一个宏的参数;f. 宏不能出现在字符串中,在字符串中宏不会展开。
       例子(具体见代码及注释):

#include<stdio.h>
#define MAX 10000 //定义最大值10000的宏MAX
#define SUM a+b   //a+b被SUM替换,程序中原样展开
#define TOTAL SUM\
                +c //宏SUM作为宏TOTAL的参数,并将宏TOTAL的定义写在两行
int main(void)
{
    int a = 10, b = 20, c = 30;
    printf("MAX=%d\n", MAX); //运行结果:MAX=10000 如写成printf("MAX");是不会显示10000的
    printf("SUM=%d\n", SUM); //运行结果:SUM=30
    printf("TOTAL=%d\n",TOTAL); //运行结果:TOTAL=60
    getchar();
    return 0;
}

2.#define定义带参数的宏
       带参宏与函数表面上难以区分,使用过程中也感觉与函数无异。从根本上说,带参宏是预处理指令定义的,函数是由C语言命令编写的,这是本质区别;但带参宏仍然是宏,还是有将被替换内容原样展开的特性,所以,使用带参宏时,实参中尽量不要有表达式,以防展开时带来意想不到的结果。
       带参宏的名称后面使用括号,能够接受一个或多个参数:
       固定个数参数情形:
       格式:#define 宏名(参数列表) 宏替换内容 //参数多于一个时中间用","隔开,注意宏名与括号间不要留空格
       说明:从上面的格式可以看出,带参宏并未涉及数据类型,因此使用时代入任何适合的类型均可。
3.与#define相关的#、## 运算符
       本来接下来应该学习不定参数宏,但由于不定参数宏中涉及到#、##运算符,所以先介绍一下这两个运算符。
       #运算符:
       功能一:指定宏替换内容为字符串,做法是在在宏替内容前加上 "#"。例:
                     #define STR(x) #x //相当于将x的值用引号引起来
       功能二:起将某字符串与宏替换内容连接作用,做法是:"字符串"#宏替换内容。例:
                     #define CONECT(x) "Name: "#x //作用是把字符串"Name: "与x代表的字符串连在一起形成一个大字符串(说明:"#"仅是对字符串的操作)
        ##运算符:
        功能一:将作为参数的两个字符串(或其它类型)连接在一起。例:
                     #define cons(a,b) a##b //如 a="123";b="456";则 cons(a,b)="123456";
        功能二:参数与其它标识符连在一起,组成一个新的标识符。例:
                     #define makeVar(n) i##n //这里的作用也是连接,但连接出来的是标识符,不是字符串,具体见后例子 (说明:"##"操作的对象及结果不仅仅是字符串)
4.#define定义不定参数宏
       宏的参数事先不能确定时,可以使用不定参数宏。做法是参数表中使用 "..." 代表未确定参数,宏替换内容中用预定义宏__VA_ARGS__代表"..."对应的宏替换内容。例:
       #define 宏名(参1,参2, ... ) 参1参2对应宏替换内容,__VA_ARGS__
      说明:__VA_ARGS__对应...;宏替换内容"..."只能放在最后参数位置。
5.下面将带参宏、#和##运算符、不定参数宏用一个程序做进一步说明。代码如下:

#include<stdio.h>
int main(void)
{
//A、#define带参宏
#define MAX(x, y) ((x)>(y))?(x):(y) //使用三目表达式返回x,y中最大值
                                    //在三目表达式中多加括号,目的是防止展开式出现意外结果
    double x = 3.14, y = 2.71;
    printf("x,y较大的是%f\n", MAX(x, y));//运行结果:x,y较大的是3.140000
//B、#运算符的使用
#define Str(x) #x //给x代表的值加上双引号
    printf(Str(3.1415926));//相当于printf("3.1415926");
    printf("\n");
#define Str01(x) "No."#x //将No.与x所代表的值连接成字符串
    printf(Str01(001)); //运行结果:No.001
//C、##运算符
#define cons(x,y) x##y
    printf("\n%f\n", cons(123.23, 4567)); //运行结果:123.4567 小数位数加长了四位
#define makeIntVar(n) int i##n
    makeIntVar(1); //等价 int i1
    makeIntVar(2); //等价 int i2
    i1 = 100, i2 = 300;
    printf("i1+i2=%d\n", i1 + i2);
//D、不定参数宏的使用
#define PR(...) printf(__VA_ARGS__)  //相当于printf()函数
    PR("123456\n"); //运行结果:123456
     //如果这样定义宏
#define PR01(...) printf(#__VA_ARGS__) //可以省略双引号
    PR01(abcdefg\n); //运行结果:abcdefg
    getchar();
    return 0;
}

       说明:总的说来,宏的优点是相对简单,本质上是字符串替换,不涉及数据类型,不像函数必须定义数据类型。而且,宏将每一处都替换成实际的代码,省掉了函数调用的开销,所以性能会好一些。但宏有时候会产生意想不到的替换结果,而且往往只能写成一行,除非对换行符进行转义,但是可读性就变得很差。所以应该首先使用函数,它的功能更强、更容易理解。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值