第十六章 C预处理器和C库

第十六章 C预处理器和C库

define与预处理器的行为

define所定义的宏在被预处理器处理时需要注意一点,就是预处理器知识进行替换,而不会进
行运算.请看一个例子:

#define SQUARE(X) X*X

int main()
{
    int num = 4;
    int ret = 0;

    ret = SQUARE(num+4);
}

上述例子中ret的值是这么计算的ret = num + 4 * num + 4.也就是ret为24,而非我们希望
的64.因为这里预处理器只是字符串替换,不会为我们进行提前运算.如果想得到正确答案,应
该这么定义SQUARE宏:

#define SQUARE(X) (X) * (X)

这也是在使用宏函数时候非常需要注意的一点.

#运算符

在宏定义中#号可以把一个语言符号转换为字符串,具体请看下面的例子.

#define MY_PRINT_1(X) printf("Hello #X")
#define MY_PRINT_2(X) printf("Hello X")

int main(void)
{
    int NewName = 0;
    MY_PRINT_1(NewName);
    MY_PRINT_2(NewName);
}

在上面例子的输出结果中,MY_PRINT_1的结果为Hello NewName.MY_PRINT_2的结果为Hello
X.也就是在MY_PRINT_1中,通过#符号,可以将NewName这个语言符号直接转换为字符串
NewName.这里还需要注意的一点是被转换为字符串符号的NewName,实际上为”New Name” ,这
也是为什么当使用#运算符时,在诸如

#define PSQR(X) printf("The square of " #X "is what\n")

的定义中不需要使用转义\”的原因,因为上面那句话实际上为

printf("The square of" "X" "is what\m")

通过字符串结合,中间的字符串就可以变为”The square of X is what\n”.(具体请看c
primer plus第452页)

##运算符

在宏定义中##运算符把两个语言符号组合成单个语言符号.

#define XNAME(n) X ## n

XNAME(4)

在上例中XNAME(4)实际上为X4,这就是##运算符的作用,能够通过组合生成新的语言符号.

VA_ARGS可变宏

#define PR(X,...) printf("Message" #X ":" __VA_ARGS__)

int main(void)
{
    int x = 48;
    PR(x, "%4d\n", x);
}

上面的例子中printf()的最终参数是 printf(“Message x : %4d\n”, x).其中#X做了字符串
转换,然后使用VA_ARGS来代替了宏定义中的…(省略号)所输入的参数.关于
VA_ARGS,只要理解上面的例子即可.

一些常用的宏定义指令汇总

  1. #undef 取消一个之前已经定义好的宏 比如MAX_INT之前已经定义了,则#undef MAX_INT
    后, 后续的就不存在MAX_INT这个宏了(只针对使用了#undef的本文件)
  2. #ifdef
  3. #else
  4. #endif
  5. #ifndef 常用语避免头文件重复定义,例子:
#ifndef CODER_H
#define CODER_H

...具体内容
...
#endif
  1. #if 特别的在这里有一个新的预处理运算符defined(),若defined的参数被定义过,则返
    回1,否则返回0;
#if defined(IBMPC)
    #include "1.h"
#elif defined(MACPC)
    #include "2.h"
#endif
  1. #elif

有关于函数的可变参数,stdarg.h

函数的可变参数必须按照如下步骤进行:
1. 在函数原型中使用省略号
2. 在函数定义中创建一个va_list类型的变量
3. 用宏将该变量初始化为一个参数列表
4. 用宏访问这个参数列表
5. 用宏完成清理工作
具体例子如下:

double sum(int lim,...)
{
    va_list ap;     // 声明用于存放参数的变量
    double dSum = 0;
    int i;

    va_start(ap,lim);                        //把AP初始化为参数列表
    for(i = 0; i < lim; i++)
    {
        dSum = va_arg(ap,double);  // 访问参数列表中的每一个项目
    }
    va_end(ap);                                  // 清理工作
    return dSum;
}

这里有几点需要注意:
1. va_list类型代表一种数据对象,用于存放参量列表中省略号部分代表的参量.
2. va_start(va_list, argN) 把参数列表复制到va_list变量中.其中argN代表固定参数中
的最后一个固定参数. (使参数列表指针va_list指向函数参数列表中的第一个可选参数
)
3. va_arg()逐个返回参数列表中的项,va_arg(va_list,type)中的type描述va_arg的返回值
类型.
4. va_end()完成清理工作
5. 这里要注意,由于va_arg()不提供后退回先前参数的方法,也就是弹出一个参数后就无法
再通过va_arg()弹出相同位置的参数.所以为了保险起见,我们可以通过va_copy()宏函数
对参数列表进行一次复制,已被不时之需.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值