常用宏(#,##和可变宏) 带小练习

1.1:#

在宏体中,如果宏参数前加个#,那么在宏体扩展的时候,宏参数会被直接被扩展成字符串的形式,而不在乎宏参数在上文中的值。

1.2:##

      ##是个粘合剂,先分隔,然后进行强制连接当两边参数有变量时,将前后两部分粘合起来,“组成变量名”;若两边参数都是常数,则输出粘合的常数结果。

     若两边都是字符串,会报“does not give a valid preprocessing token”错:

关于g++报错这两行:
cout < <NUM("1","2","3") < <endl;      --> "1""2""3" 
cout < <STR("aa","bb","cc") < <endl;  --> "aa""bb""cc"
这种情况下完全没必要使用##,直接写成"1""2""3"就是一个字符串。
g++大概是对这种行为很不理解,认为使用##应该产生一个新的token才合理,
而VS和VC就觉得无所谓。
http://topic.csdn.net/u/20071210/15/0fc45077-21fd-4b7b-a0fa-d6abaea27503.html

1.3:可变宏:… 和_VA_ARGS

//两种写法同效果
#define DBG(fmt,...)     \
	do {\
		debug(number++, fmt, __VA_ARGS__);\
	   } while(0)

#define DBG(fmt, args...)     \
	do {\
		debug(number++, fmt, ##args);\
	   } while(0)

1.4:注意事项

在单一的宏定义中,最多可以出现一次“#”或“##”预处理操作符。如果没有指定与“#”或“##”预处理操作符相关的计算次序,则会产生问题。为避免该问题,在单一的宏定义中只能使用其中一种操作符(即,一份“#”或一个“##”,或都不用)。

1.4:常用宏定义

防止一个头文件被重复包含 

#ifndef BODYDEF_H 

#define BODYDEF_H 

 //头文件内容 

#endif

 得到一个field在结构体(struct)中的偏移量

 #define OFFSETOF( type, field ) ( (size_t) &(( type *) 0)-> field )

 返回数组元素的个数 

#define ARR_SIZE( a ) ( sizeof( (a) ) / sizeof( (a[0]) ) )

 防止溢出的一个方法

#define INC_SAT( val ) (val = ((val)+1 > (val)) ? (val)+1 : (val))

 判断字符是不是16进值的数字

#define HEXCHK( c ) ( ((c) >= ''0'' && (c) <= ''9'') ||((c) >= ''A'' && (c) <= ''F'') ||((c) >= ''a''&& (c) <= ''f'') )

将一个字母转换为大写

#define UPCASE( c ) ( ((c) >= ''a'' && (c) <= ''z'') ? ((c) - 0x20) : (c) )

1.5  ANSI C标准宏

__func__    //在源代码中插入当前函数名行号
__LINE__    // 在源代码中插入当前源代码行号
__FILE__    // 在源文件中插入当前源文件名
__DATE__    // 在源文件中插入当前的编译日期
__TIME__    // 在源文件中插入当前编译时间
__STDC__    // 当要求程序严格遵循ANSI C标准时该标识被赋值为1
__cplusplus // 当编写C++程序时该标识符被定义

_WIN32      // 在程序运行在windows系统上被定义位1
linux       // 在程序运行在linux系统上被定义位1
__x86_64__  // 在程序运行在64位系统上被定义位1
__i386__    // 在程序运行在32位系统上被定义位1

__VA_ARGS__ // 是一个可变参数的宏,在新C99规范中新增,
            // 目前似乎gcc和VC6.0之后的都支持(VC6.0的编译器不支持)。
            // 宏前面加上##的作用在于,可以接受参数为0个或者多个

 

小练习:

#include <stdio.h>
#define Conn(x, y) x##y                            //拼接出上文中的变量,若结果变量不存在会报错
#define ToString(x) #x                                 //将入参而不是入参值转化为字符串
int main () { 
	int k = Conn(5, 1);                            //粘合常量
	printf("k = %d\n", k);

	int i1, j;
	for(i1 = 1; i1 < 10 ; i1++) {
		j = Conn(i, 1)++;                          //粘合变量
		printf("number %d:the sum of %s is: %d\n", i1, ToString(j*10+i1), j*10+i1);
	}
	printf("well done!\n");
	return 0;
}

 输出:

k = 51
number 2:the sum of j*10+i1 is: 12
number 4:the sum of j*10+i1 is: 34
number 6:the sum of j*10+i1 is: 56
number 8:the sum of j*10+i1 is: 78
number 10:the sum of j*10+i1 is: 100
well done!

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
c语言中的函数是一种宏定义的形式,可以在编译预处理阶段通过替换来实现类似函数调用的功能。它不同于普通的函数调用,其主要特点是在宏定义时使用一些特殊的形参,这些形参可以包含可变参数。 可变参数是指函数在调用时可以传入不定数量的参数。在宏定义中,我们可以使用"..."来表示可变参数,且该参数必须放置在参数列表的最后。 通过宏定义中的可变参数,我们可以通过函数实现一些功能较为复杂的操作。比如,我们可以编写一个求和的函数,可以传入任意数量的参数,并对它们进行求和计算。例如: ``` #define SUM(...) sum(__VA_ARGS__) int sum(int count, ...) { int result = 0; va_list arg; va_start(arg, count); for(int i = 0; i < count; i++) { result += va_arg(arg, int); } va_end(arg); return result; } ``` 在使用可变参数的函数时,我们需要使用宏定义中的特殊形参,包括`va_list`、`va_start`、`va_arg`和`va_end`。`va_list`是一个用于存储可变参数的类型,`va_start`用于初始化可变参数的访问,`va_arg`用于按顺序获取可变参数的值,`va_end`用于结束对可变参数的访问。 总之,函数和可变参数是C语言中非常有用的功能,可以通过它们来实现一些复杂的操作和功能。但需要注意的是,在使用可变参数时要小心,保证参数的正确性和合法性,以免出现错误。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值