参考:https://blog.youkuaiyun.com/liguangxianbin/article/details/79312024
https://blog.youkuaiyun.com/qq_20161893/article/details/72581948
https://www.cnblogs.com/fnlingnzb-learner/p/6903966.html
\是行连续的意思,有这个符号,就是下一行是接着上一行的意思,就是下一行和上一行从语法上来说是一行,只是分成了多行来写。
例如:
#include<stdio.h>
#include<stdlib.h>
#define STRING "Hello\
world\
I love U\n"
int main()
{
printf(STRING);
}
运行结果:
Helloworld I love U
分割线
因为对于一个大程序而言,我们可能要定义很多常量( 不管是放在源文件还是头文件 ),那么我们有时考虑定义某个常量时,我们就必须返回检查原来此常量是否定义,但这样做很麻烦.if defined宏正是为这种情况提供了解决方案.举个例子,如下:
#define ....
#define ....
....
....
#define a 100
....
此时,我们要检查a是否定义(假设我们已经记不着这点了),或者我们要给a一个不同的值,就加入如下句子
#if defined a
#undef a
#define a 200
#endif
上述语句检验a是否被定义,如果被定义,则用#undef语句解除定义,并重新定义a为200
同样,检验a是否定义:
#ifndef a //如果a没有被定义
#define a 100
#endif
以上所用的宏中:#undef为解除定义,#ifndef是if not defined的缩写,即如果没有定义。
这就是#if defined 的唯一作用!
#define a 100
#endif
以上所用的宏中:#undef为解除定义,#ifndef是if not defined的缩写,即如果没有定义。
这就是#if defined 的唯一作用!
1)
#if defined XXX_XXX
#endif
是条件编译,是根据你是否定义了XXX_XXX这个宏,而使用不同的代码。
一般.h文件里最外层的
#if !defined XXX_XXX
#define XXX_XXX
#endif
是为了防止这个.h头文件被重复include。
2)
#error XXXX
是用来产生编译时错误信息XXXX的,一般用在预处理过程中;
例子:
#if !defined(__cplusplus)
#error C++ compiler required.
#endif
1 防止一个头文件被重复包含
- #ifndef BODYDEF_H
- #define BODYDEF_H
- //头文件内容
- #endif
2 得到指定地址上的一个字节或字
- #define MEM_B( x ) ( *( (byte *) (x) ) )
- #define MEM_W( x ) ( *( (word *) (x) ) )
用法如下:
- #include <iostream>
- #include <windows.h>
- #define MEM_B(x) (*((byte*)(x)))
- #define MEM_W(x) (*((WORD*)(x)))
- int main()
- {
- int bTest = 0x123456;
- byte m = MEM_B((&bTest));/*m=0x56*/
- int n = MEM_W((&bTest));/*n=0x3456*/
- return 0;
- }
3 得到一个field在结构体(struct)中的偏移量
- #define OFFSETOF( type, field ) ( (size_t) &(( type *) 0)-> field )
(type *)0:把0地址当成type类型的指针。
((type *)0)->field:对应域的变量。
&((type *)0)->field:取该变量的地址,其实就等于该域相对于0地址的偏移量。
(size_t)&(((type *)0)->field):将该地址(偏移量)转化为size_t型数据。
4 得到一个结构体中field所占用的字节数
- #define FSIZ( type, field ) sizeof( ((type *) 0)->field )
5 得到一个变量的地址(word宽度)
- #define B_PTR( var ) ( (byte *) (void *) &(var) )
- #define W_PTR( var ) ( (word *) (void *) &(var) )
6 将一个字母转换为大写
- #define UPCASE( c ) ( ((c) >= ''a'' && (c) <= ''z'') ? ((c) - 0x20) : (c) )
7 判断字符是不是10进值的数字
- #define DECCHK( c ) ((c) >= ''0'' && (c) <= ''9'')
8 判断字符是不是16进值的数字
- #define HEXCHK( c ) ( ((c) >= ''0'' && (c) <= ''9'') ||((c) >= ''A'' && (c) <= ''F'') ||((c) >= ''a'' && (c) <= ''f'') )
9 防止溢出的一个方法
- #define INC_SAT( val ) (val = ((val)+1 > (val)) ? (val)+1 : (val))
10 返回数组元素的个数
- #define ARR_SIZE( a ) ( sizeof( (a) ) / sizeof( (a[0]) ) )
11 使用一些宏跟踪调试
ANSI标准说明了五个预定义的宏名。它们是:
- _LINE_ /*(两个下划线),对应%d*/
- _FILE_ /*对应%s*/
- _DATE_ /*对应%s*/
- _TIME_ /*对应%s*/
一。需要注意的是凡宏定义里有用'#'或'##'的地方宏参数是不会再展开.
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 #i nclude<climits>
这行会被展开为:
printf("int max: %s/n", "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型的最大值,为一个变量 #i nclude<climits>
输出为: 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))
二。'#'和'##'的一些应用特例
1、合并匿名变量名
#define ___ANONYMOUS1(type, var, line) type var##line
#define __ANONYMOUS0(type, line) ___ANONYMOUS1(type, _anonymous, line)
#define ANONYMOUS(type) __ANONYMOUS0(type, __LINE__)
例:ANONYMOUS(static int); 即: static int _anonymous70; 70表示该行行号;
第一层:ANONYMOUS(static int); --> __ANONYMOUS0(static int, __LINE__);
第二层: --> ___ANONYMOUS1(static int, _anonymous, 70);
第三层: --> static int _anonymous70;
即每次只能解开当前层的宏,所以__LINE__在第二层才能被解开;