一文解决常见宏定义

一文解决常见宏定义

@指令用途

#					空指令,无任何效果
#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;

}

宏定义撤销的意思可以理解为,当执行完 #undefMAX(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

本文作为自己的技术沉淀,后续还会陆续有技术沉淀相关的内容

记得关注点赞哈

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值