一文解决常见宏定义
目录
@指令用途
# 空指令,无任何效果
#include 包含一个源代码文件
#define 定义宏
#undef 取消已定义的宏
#if 如果给定条件为真,则编译下面代码
#ifdef 如果宏已经定义,则编译下面代码
#ifndef 如果宏没有定义,则编译下面代码
#elif 如果前面的#if给定条件不为真,当前条件为真,则编译下面代码
#endif 结束一个#if……#else条件编译块
#error 停止编译并显示错误信息
@基础宏定义用法
#define N 100
#include <stdio.h>
#define N 100
int main(void) {
printf("N=%d\r\n",N);
return 0;
}
其中 N被100代替,N为宏定义的名,100为宏定义的内容
在编译器预处理的阶段会将所有宏定义名字都进行对应的替换。
如果是宏定义某两个数字相加,例如:
#define N 50+50
这种做法是很不建议的,如果将宏定义N乘以某一个数的话,会出现相乘除再加减的运算法则问题,所以建议以下写法:
#define N (50+50)
这样语意清晰。
@带参数的宏定义
#include <stdio.h>
#define MAX(a,b) (a>b?a:b)
int main(void) {
printf("MAX=%d\r\n",MAX(1,5));
return 0;
}
宏定义的参数要与定义的参数相同。
@宏定义撤销
#include <stdio.h>
#define MAX(a,b) (a>b?a:b)
int main(void) {
printf("MAX=%d\r\n", MAX(1, 5));
#undef MAX
printf("MAX=%d\r\n", MAX(1, 5));
return 0;
}
宏定义撤销的意思可以理解为,当执行完 #undef 后MAX(a,b) 与 (a>b?a:b) 就不存在关系了,所以再使用 **MAX(a,b)**时编译器就会报未定义的错误。
@跨行宏定义
#include <stdio.h>
#define TEST(x) if (x > 0) \
{ \
printf("X>0\r\n"); \
} \
else \
{ \
printf("X<=0\r\n"); \
} \
int main(void) {
TEST(5);
return 0;
}
跨行宏定义使用反斜杠进行分割,可以使用宏定义来定义一个函数,或者是一行写不下也可以用反斜杠来连接
@ 三个特殊符号:#,##,#@
#include <stdio.h>
#define CONVERT_STR(x) #x
void fun()
{
char *str = CONVERT_STR(123456789);
printf("str=%s\r\n", str);
}
int main(void) {
fun();
return 0;
}
#define CONVERT_STR(x) #x 可以理解为将传入的参数转换成字符串并返回指针 。
#include <stdio.h>
#define CONNECT(a,b) a##b
void fun()
{
char *str = CONNECT("123","456");
printf("str=%s\r\n", str);
int val = CONNECT(456, 123);
printf("val=%d\r\n", val);
}
int main(void) {
fun();
return 0;
}
a##b表示将a与b连接起来,不仅限于实例中的类型,a与b类型相同都可以连接起来 。
#include <stdio.h>
#define CHAR_STR(x) #@x
void fun()
{
char str = CHAR_STR(1);
printf("str=%c\r\n", str);
}
int main(void) {
fun();
return 0;
}
#@x表示将x字符化,一般只输出一个字符,如果输入多个人则与大小端模式有关,输入多个则只返回一个字符。
@根据宏定义是否存在,进行条件编译
//#define LOG_SW //是否打印开关
void fun()
{
#if defined(LOG_SW)
printf("hello world !");
#endif
}
/*
其中 LOG_SW为宏定义;
#if defined(LOG_SW) 语句用来判断是否定义了这个宏定义,与内容无关,至于是否被定义有关
*/
@常用宏定义
- log打印,可输出当前打印所在的行号函数以及要打印的内容。
#define PRINTF_INFO(fmt...) \
do{\
printf("[%s]-%d: ", __FUNCTION__, __LINE__);\
printf(fmt);\
}while(0)
printf("file=%s,func=%s,line=%d\n",__FILE__,__FUNCTION__,__LINE__);
- 避免头文件被重复包含
#ifndef TEST_H
#define TEST_H
//头文件的内容
#endif
- 得到一个field在结构体(struct)中的偏移量
#define OFFSETOF( type, member ) ( (unsigned int) &(( type *) 0)-> member )
#include <stdio.h>
//获取field 在结构体中的偏移量
#define OFFSETOF( type, member ) ( (unsigned int) &(( type *) 0)-> member )
struct test_t
{
int a; /*占4个字节*/
char b;/*占1个字节*/
short c;/*占2个字节*/
};
/*定义类型*/
typedef struct test_t test_typedef;
/*
主函数
*/
int main()
{
int offset_val = OFFSETOF(test_typedef, c);
printf("val = %d\r\n", offset_val);
getchar();
return 0;
}
/*
宏的运行机理:
- ((type *)0) 将零转型为type 类型指针;
- ((type *)0)->member 访问结构中的数据成员;
- &(((type *)0)->member)取出数据成员的地址;
- (size_t)(&(((type*)0)->member))结果转换类型。巧妙之处在于将0转换成(type*),结构以内存空间首地址0作为起始地址,则成员地址自然为偏移地址;
*/
- 得到一个结构体中field所占用的字节数
#define FSIZ( type, member ) sizeof( ((type *) 0)->member )
#include <stdio.h>
//获取member在结构体中的成员大小
#define FSIZ( type, member ) sizeof( ((type *) 0)->member )
struct test_t
{
int a; /*占4个字节*/
char b;/*占1个字节*/
short c;/*占2个字节*/
int ary[10];/*占40个字节*/
};
/*定义类型*/
typedef struct test_t test_typedef;
/*
主函数
*/
int main()
{
printf("FSIZ->member sizeof %d", FSIZ(test_typedef, ary));
getchar();
return 0;
}
/*
宏的运行机理:
- ((type *)0) 将零转型为type 类型指针;
- ((type *)0)->member 访问结构中的数据成员;
- sizeof( ((type *) 0)->member ) 调用库函数计算type类型的成员所占内存大小
*/
- 将一个字母转换为大写
#define UPCASE(c) ( ((c) >= 'a' && (c) <= 'z') ? ((c) - 0x20) : (c) )
- 判断字符是不是10进制的数字
#define DECCHK(c) ((c) >= '0' && (c) <= '9')
- **判断字符是不是16进*制*的数字
#define HEXCHK(c) (((c) >= '0' && (c) <= '9') ||((c) >= 'A' && (c) <= 'F') ||((c) >= 'a' && (c) <= 'f'))
- 返回数组元素的个数
#define ARR_SIZE(a) ( sizeof((a)) / sizeof((a[0])) )
- 求最大值和最小值
#define MAX(x,y) (((x)>(y)) ? (x) : (y))
#define MIN(x,y) (((x) < (y)) ? (x) : (y))
- 按照LSB格式把两个字节转化为一个Word
#define FLIPW(ray) ((((word)(ray)[0]) * 256) + (ray)[1])
- 按照LSB格式把一个Word转化为两个字节
#define FLOPW(ray,val) (ray)[0] = ((val)/256); (ray)[1] = ((val) & 0xFF)
- 得到一个字的高位和低位字节
#define WORD_LO(xxx) ((byte) ((word)(xxx) & 255))
#define WORD_HI(xxx) ((byte) ((word)(xxx) >> 8))
- 返回一个比X大的最接近的8的倍数
#define RND8(x) ((((x) + 7)/8) *
- 防止溢出的一个方法
#define INC_SAT(val) (val=((val)+1>(val)) ? (val)+1 : (val))
@使用一些宏跟踪调试
ANSI标准说明了五个预定义的宏名。它们是:
__LINE__
__FILE__
__DATE__
__TIME__
__STDC__
C++中还定义了 __cplusplus
如果编译器不是标准的,则可能仅支持以上宏名中的几个,或根本不支持。记住编译程序也许还提供其它预定义的宏名。
__LINE__ 及 __FILE__ 宏指示,#line指令可以改变它的值,简单的讲,编译时,它们包含程序的当前行数和文件名。
__DATE__ 宏指令含有形式为月/日/年的串,表示源文件被翻译到代码时的日期。
__TIME__ 宏指令包含程序编译的时间。时间用字符串表示,其形式为: 分:秒
__STDC__ 宏指令的意义是编译时定义的。一般来讲,如果__STDC__已经定义,编译器将仅接受不包含任何非标准扩展的标准C/C++代码。如果实现是标准的,则宏__STDC__含有十进制常量1。如果它含有任何其它数,则实现是非标准的。
__cplusplus 与标准c++一致的编译器把它定义为一个包含至少6为的数值。与标准c++不一致的编译器将使用具有5位或更少的数值。
可以定义宏,例如:
当定义了_DEBUG,输出数据信息和所在文件所在行
#ifdef _DEBUG
#define DEBUGMSG(msg,date) printf(msg);printf(“%d%d%d”,date,_LINE_,_FILE_)
#else
#define DEBUGMSG(msg,date)
#endif
本文作为自己的技术沉淀,后续还会陆续有技术沉淀相关的内容
记得关注点赞哈