宏定义 转自 百度百科
C语言中如何使用宏C(和C++)中的宏(Macro)属于编译器预处理的范畴,属于编译期概念(而非运行期概念)。下面对常遇到的宏的使用问题做了简单总结。
宏中'#'和'##'的用法
#define STR(s) #s
#define CONS(a,b) int(a##e##b)
int main()
{
printf(STR(vck)); // 输出字符串'vck'
printf('%d/n', CONS(2,3)); // 2e3 输出:2000
return 0;
}
1, 非''#''和''##''的情况
#define TOW (2)
#define MUL(a,b) (a*b)
printf('%d*%d=%d/n', TOW, TOW, MUL(TOW,TOW));
这行的宏会被展开为:
printf('%d*%d=%d/n', (2), (2), ((2)*(2)));
MUL里的参数TOW会被展开为(2).
2, 当有''#''或''##''的时候
#define A (2)
#define STR(s) #s
#define CONS(a,b) int(a##e##b)
printf('int max: %s/n', STR(INT_MAX)); // INT_MAX #include
这行会被展开为:
printf('int max: %s/n', 'INT_MAX'); //而不是0x7FFFFFFF INT_MAX不展开了
printf('%s/n', CONS(A, A)); // compile error
这一行则是:
printf('%s/n', int(AeA));
INT_MAX和A都不会再被展开, 然而解决这个问题的方法很简单. 加多一层中间转换宏.
加这层宏的用意是把所有宏的参数在中间层里全部展开, 那么在转换宏里的那一个宏(_STR)就能得到正确的宏参数.
#define A (2)
#define _STR(s) #s
#define STR(s) _STR(s) // 转换宏
#define _CONS(a,b) int(a##e##b)
#define CONS(a,b) _CONS(a,b) // 转换宏
printf('int max: %s/n', STR(INT_MAX)); // INT_MAX,int型的最大值,为一个变量 #include
输出为: int max: 0x7fffffff
STR(INT_MAX) --> _STR(0x7fffffff) 然后再转换成字符串;
printf('%d/n', CONS(A, A));
输出为:200
CONS(A, A) --> _CONS((2), (2)) --> int((2)e(2))
2、填充结构
#define FILL(a) {a, #a}
enum IDD{OPEN, CLOSE};
typedef struct MSG{
IDD id;
const char * msg;
}MSG;
MSG _msg[] = {FILL(OPEN), FILL(CLOSE)};
相当于:
MSG _msg[] = {{OPEN, 'OPEN'},
{CLOSE, 'CLOSE'}};
3、记录文件名
#define _GET_FILE_NAME(f) #f
#define GET_FILE_NAME(f) _GET_FILE_NAME(f)
static char FILE_NAME[] = GET_FILE_NAME(__FILE__);
4、得到一个数值类型所对应的字符串缓冲大小
#define _TYPE_BUF_SIZE(type) sizeof #type
#define TYPE_BUF_SIZE(type) _TYPE_BUF_SIZE(type)
char buf[TYPE_BUF_SIZE(INT_MAX)];
--> char buf[_TYPE_BUF_SIZE(0x7fffffff)];
--> char buf[sizeof '0x7fffffff'];
这里相当于:
char buf[11];
C语言宏定义##连接符和#符的使用 http://hi.baidu.com/ufo008ahw/item/dbe3f24254976c08e83504fc
...在C宏中称为Variadic Macro,也就是变参宏。比如:
第一个宏中由于没有对变参起名,我们用默认的宏__VA_ARGS__来替代它。第二个宏 中,我们显式地命名变参为args,那么我们在宏定义中就可以用args来代指变参了。同C语言的stdcall一样,变参必须作为参数表的最有一项出 现。当上面的宏中我们只能提供第一个参数templt时,C标准要求我们必须写成:
的形式。这时的替换过程为:
替换为:
fprintf(stderr,'Error!/n',);这是一个语法错误,不能正常编译。这个问题一般有两个解决方法。首先,GNU CPP提供的解决方法允许上面的宏调用写成:
而它将会被通过替换变成:
很明显,这里仍然会产生编译错误(非本例的某些情况下不会产生编译错误)。除了这种方式外,c99和GNU CPP都支持下面的宏定义方式:
这时,##这个连接符号充当的作用就是当__VAR_ARGS__为空的时候,消除前面的那个逗号。那么此时的翻译过程如下:
被转化为:
fprintf(stderr,templt);这样如果templt合法,将不会产生编译错误。 这里列出了一些宏使用中容易出错的地方,以及合适的使用方式。
通常情况下,为了使函数模样的宏在表面上看起来像一个通常的C语言调用一样,通常情况下我们在宏的后面加上一个分号,比如下面的带参宏:
但是如果是下面的情况:
这样会由于多出的那个分号产生编译错误。为了避免这种情况出现同时保持MY_MACRO(x);的这种写法,我们需要把宏定义为这种形式:
这样只要保证总是使用分号,就不会有任何问题。
这里的Side Effect是指宏在展开的时候对其参数可能进行多次Evaluation(也就是取值),但是如果这个宏参数是一个函数,那么就有可能被调用多次从而达到不一致的结果,甚至会发生更严重的错误。比如:
这时foo()函数就被调用了两次。为了解决这个潜在的问题,我们应当这样写min(X,Y)这个宏:
({...})的作用是将内部的几条语句中最后一条的值返回,它也允许在内部声明变量(因为它通过大括号组成了一个局部Scope)。
ANSI C标准中有几个标准预定义宏:__FILE__ __DATE__ __TIME___ __LINE__ 等
__LINE__:在源代码中插入当前源代码行号;
__FILE__:在源文件中插入当前源文件名;
__DATE__:在源文件中插入当前的编译日期
__TIME__:在源文件中插入当前编译时间;
__STDC__:当要求程序严格遵循ANSI C标准时该标识被赋值为1;
__cplusplus:当编写C++程序时该标识符被定义。
_FUNCTION_ 当前所在函数名
这几个宏比较有用
在调试程序时或编译时,__LINE__比较有用,可以用来打印逻辑错误的行号~~~~~~~,例子:
switch(x)
{
case 1:
....;
break;
case 2:
.....;
break;
default:
printf('logic erro line number%d!/n',__LINE__);
break;
}
又如利用__DATE__和__TIME__可以插入编译时间。
代码:
void print_version_info(void)
{
printf('Date Compiled:%s/n',__DATE__);
printf('TimeCompiled:%s/n',__TIME__);
}
输出格式为:mm dd yy 和 hh:mm:ss
对于__FILE__,__LINE__,__func__这样的宏,在调试程序时是很有用的,因为你可以很容易的知道程序运行到了哪个文件的那一 行,是哪个函数。
下面一个例子是打印上面这些预定义的宏的。
__DATE__,__FILE__,__LINE__,__TIME__,__FUNCTION__ C标准中指定了一些预定义的宏,对于编程经常会用到。下面这个表中就是一些常常用到的预定义宏。
__DATE_ %s_
进行预处理的日期(“Mmm dd yyyy”形式的字符串文字)
__FILE__ %s
代表当前源代码文件名的字符串文字
__LINE__ %d
代表当前源代码中的行号的整数常量
__TIME__ %s
源文件编译时间,格式微“hh:mm:ss”
__FUNCTION__(__func__) %s
当前所在函数名
__PRETTY_FUNCTION__
%s
当前所在函数名,包括返回类型和参数类型。
例如:printf('__PRETTY_FUNCTION__:%s/n', __PRETTY_FUNCTION__);
输出:__PRETTY_FUNCTION__:int main(int, char**)
#include
#include
void why_me();
int main()
{
printf( 'The file is %s./n', __FILE__ );
printf( 'The date is %s./n', __DATE__ );
printf( 'The time is %s./n', __TIME__ );
printf( 'This is line %d./n', __LINE__ );
printf( 'This function is %s./n', __FUNCTION__ );
why_me();
return 0;
}
void why_me()
{
printf( 'This function is %s/n', __func__ );
printf( 'The file is %s./n', __FILE__ );
printf( 'This is line %d./n', __LINE__ );
}
使用ANSI C 、 C++中常用的预定义宏 转自 http://blog.csdn.net/clozxy/article/details/5629203
经常在/usr/include目录下看到这种字句:
#ifdef __cplusplus
extern 'C' {
#endif
...
#ifdef __cplusplus
}
#endif
不太明白是怎么用的。今天阅读autobook,在第53页看到了作者的解释:C/C++编译器对函数和变量名的命名方法不一样(例如C++中重载的函数有多个名字,而C的函数只有一个名字),这样当C编译器去引用C++编译器编译出来的符号时,会找不到链接。因此,当一个头文件可能既被C程序引用,又被C++程序引用时,需要使用如上代码进行区分。
上面的写法太复杂了,况且两个大括号{和}分离,会造成有些编辑器的缩进错误。更好的手法是将如下代码定义在一个公共头文件中,然后所有其它头文件去引用它:
#ifdef __cplusplus
# define BEGIN_C_DECLS extern 'C' {
# define END_C_DECLS }
#else
# define BEGIN_C_DECLS
# define END_C_DECLS
#endif
#define _cplusplus 的详细妙用http://www.cnblogs.com/stonecrazyking/archive/2006/09/23/512552.html
联系客服