C语言宏定义技巧
出处:http://blog.chinaunix.net/uid-14022540-id-2849095.html
1.宏中"#"和"##"的用法
一、一般用法
我们使用#把宏参数变为一个字符串,用##把两个宏参数贴合在一起.
#include <stdio.h> #define CONS(a,b) ((int)(a##e##b)) #define STR(s) #s int main() { printf("%s\n",STR(ABC)); printf("%d\n",CONS(2,3)); return 0; } /* root@oucaijun:/work/dcc# gcc *.c; ./a.out ABC 2000 */
二、当宏参数是另一个宏时: 需要注意的是凡宏定义里有用'#'或'##'的地方宏参数是不会再展开.
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 CONS(a,b) ((int)(a##e##b) )
printf("%s\n", CONS(A, A)); // compile error
这一行则是:
printf("%s\n", int(AeA));
然而解决这个问题的方法很简单. 加多一层中间转换宏: 凡是涉及到#, ## 的宏,使用另一个宏来包装它。
#include <stdio.h> #define _GET_FILE_NAME(f) (#f) #define GET_FILE_NAME(f) _GET_FILE_NAME(f) int main() { // 根据GET_FILE_NAME(f)的定义,它并没有直接使用#,因此内部宏得以完整地展开,得到正确结果。 static char FILENAME[] = GET_FILE_NAME(__FILE__);//"test.c" printf("%s\n", FILENAME); // _GET_FILE_NAME(f)在展开的时候,引用了宏 __FILE__ // 根据_GET_FILE_NAME的定义,它的内部遇到宏并不会展开,而只是把其当作字符串而已,得不到正确的结果 static char FILENAME2[] = _GET_FILE_NAME(__FILE__);//__FILE__ printf("%s\n", FILENAME2); return 0; } /* root@oucaijun:/work/dcc# gcc *.c; ./a.out "test.c" __FILE__ */
2.宏跟踪调试
#include <stdio.h> #include <time.h> int main() { time_t t; time(&t); printf("file:%s, line:%d, function:%s, msg:%s, date:%s\n", \ __FILE__, __LINE__, __FUNCTION__,"hello", ctime(&t)); return 0; } /* root@oucaijun:/work/dcc# gcc *.c; ./a.out file:1.c, line:11, function:main, msg:hello, date:Tue Jul 21 17:03:01 2015 */
当定义了_DEBUG,输出数据信息和所在文件所在行
#include <stdio.h> #include <time.h> #define _DEBUG #ifdef _DEBUG #define DEBUGMSG(msg, date) \ do{\ printf("msg: %s; file:%s, line:%d, date:%s\n",msg, __FILE__, __LINE__, date);\ }while(0) #else #define DEBUGMSG(msg,date) #endif int main() { time_t now; time(&now); DEBUGMSG("hello",ctime(&now)); return 0; } /* root@oucaijun:/work/dcc# gcc *.c; ./a.out msg: hello; file:1.c, line:22, date:Tue Jul 21 17:05:44 2015 */
3.下面列举软件中常用得一些宏定义:
1,防止一个头文件被重复包含
#ifndef COMDEF_H
#define COMDEF_H
//头文件内容
#endif
2,重新定义一些类型,防止由于各种平台和编译器的不同,而产生的类型字节数差异,方便移植.
typedef unsigned char boolean; /* Boolean value type. */
typedef unsigned long int uint32; /* Unsigned 32 bit value */
typedef unsigned short uint16; /* Unsigned 16 bit value */
typedef unsigned char uint8; /* Unsigned 8 bit value */
typedef signed long int int32; /* Signed 32 bit value */
typedef signed short int16; /* Signed 16 bit value */
typedef signed char int8; /* Signed 8 bit value */
typedef unsigned char byte; /* Unsigned 8 bit value type. */
typedef unsigned short word; /* Unsinged 16 bit value type. */
typedef unsigned long dword; /* Unsigned 32 bit value type. */
3,得到指定地址上的一个字节或字
#define MEM_B( x ) ( *( (byte *) (x) ) )
#define MEM_W( x ) ( *( (word *) (x) ) )
4,求最大值和最小值
#define MAX( x, y ) ( ((x) > (y)) ? (x) : (y) )
#define MIN( x, y ) ( ((x) < (y)) ? (x) : (y) )
7,按照LSB格式把两个字节转化为一个Word
#define FLIPW( ray ) ( (((word) (ray)[0]) * 256) + (ray)[1] )
8,按照LSB格式把一个Word转化为两个字节
#define FLOPW( ray, val ) \
(ray)[0] = ((val) / 256); \
(ray)[1] = ((val) & 0xFF)
12,将一个字母转换为大写
#define UPCASE( c ) ( ((c) >= 'a' && (c) = '0' && (c) = '0' && (c) = 'A' && (c) = 'a' && (c) (val)) ? (val)+1 : (val))
20,宏定义防止使用是错误
用小括号包含.
用do{}while(0)语句包含多语句防止错误
eg: #define ADD(a,b) do{a+b;\
a++;}while(0)