宏定义及相关用法

宏定义及相关用法

欢迎各位补充

目录

  • 一些成熟软件中常用的宏定义:
  • 使用一些内置宏跟踪调试:
  • 宏定义防止使用时错误:
  • 宏与函数
    • 带副作用的宏参数
  • 特殊符号:’#’、’##’
    • 1、一般用法
    • 2、当宏参数是另一个宏的时候
  • __VA_ARGS__与##__VA_ARGS__

一些成熟软件中常用的宏定义:

1,防止一个头文件被重复包含

#ifndef COMDEF_H
#define COMDEF_H//头文件内容 …
#endif

2,重新定义一些类型,防止由于各种平台和编译器的不同,而产生的类型字节数差异,方便移植。

typedef  unsigned long int  uint32;      	/* Unsigned 32 bit value */

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) )

5,得到一个field在结构体(struct)中的偏移量

#define FPOS( type, field )   ( (dword) &(( type *) 0)-> field )

6, 得到一个结构体中field所占用的字节数

#define FSIZ( type, field ) sizeof( ((type *) 0)->field )

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)

9,得到一个变量的地址(word宽度)

#define  B_PTR( var )  ( (byte *) (void *) &(var) )
#define  W_PTR( var )  ( (word *) (void *) &(var) )

10,得到一个字的高位和低位字节

#define  WORD_LO(xxx)  ((byte) ((word)(var) & 255))
#define  WORD_HI(xxx)  ((byte) ((word)(var) >> 8))

11,返回一个比X大的最接近的8的倍数

#define RND8( x )       ((((x) + 7) / 8 ) * 8 )

12,将一个字母转换为大写

#define  UPCASE( ch ) ( ((ch) >= ’a' && (ch) <= ’z') ? ((ch) - 0×20) : (ch) )

13,判断字符是不是10进值的数字

#define  DECCHK( ch ) ((ch) >= ’0′ && (ch) <= ’9′)

14,判断字符是不是16进值的数字

#define  HEXCHK( ch ) \
(((ch) >= ’0′ && (ch) <= ’9′) || \
((ch) >= ’A' && (ch) <= ’F') || \
((ch) >= ’a' && (ch) <= ’f') )

15,防止溢出的一个方法

#define  INC_SAT( val )  (val = ((val)+1 > (val)) ? (val)+1 : (val))

16,返回数组元素的个数

#define  ARR_SIZE( a )  ( sizeof( (a) ) / sizeof( (a[0]) ) )

17,对于IO空间映射在存储空间的结构,输入输出处理

#define inp(port)         (*((volatile byte *) (port)))
#define inpw(port)        (*((volatile word *) (port)))
#define inpdw(port)       (*((volatile dword *)(port)))#define outp(port, val)   (*((volatile byte *) (port)) = ((byte) (val)))
#define outpw(port, val)  (*((volatile word *) (port)) = ((word) (val)))
#define outpdw(port, val) (*((volatile dword *) (port)) = ((dword) (val)))

18,字符串拼接打印日志
两个相邻字符串会被拼接成一个字符串的技巧。每行都需要使用双引号将字符串引起来再加反斜杠,否则字符串将会把每一行前面的空格包含进去。

#define DEBUG_PRINT printf("File %s line %d :"\"x=%d,y=%d,z=%d",\__FILE__,__LINE,x,y,z)

使用一些内置宏跟踪调试:

ANSI标准定义了几个个预定义的宏名。它们包括但不止于:

__LINE__
__FILE__
__DATE__
__TIME__
__STDC__

注: 常用的还有__FUNCTION__等【非标准】,详细信息可查看: Predefined Macros,如果编译不是标准的,则可能仅支持以上宏名中的几个,或根本不支持。记住编译程序 也许还提供其它预定义的宏名。可以定义宏,例如:当定义了_DEBUG,输出数据信息和所在文件所在行

#ifdef _DEBUG
#define DEBUGMSG(msg,date) printf(msg);printf("%d%d%d", date, __LINE__, __FILE__)
#else
#define DEBUGMSG(msg,date)
#endif

宏定义防止使用时错误:

用小括号包含。

//例如:
#define ADD(a,b) ((a)+(b))

用do{}while(0)语句包含多语句防止错误(注意while(0)后无分号).

//例如:
#difne DO(a,b) a+b; a++;
//应写成:
#difne DO(a,b) do{a+b; a++;}while(0)

为什么需要do{…}while(0)形式?大致有以下几个原因:

  • 空的宏定义避免warning:
    #define foo() do{}while(0)

  • 存在一个独立的block,可以用来进行重复性变量定义,进行比较复杂的实现。

  • 如果出现在判断语句过后的宏,这样可以保证作为一个整体来是实现:

#define foo(x)
action1();
action2();
//在以下情况下:
if(NULL == pPointer)foo();
//就会出现action2必然被执行的情况,而这显然不是程序设计的目的。
  • 以上的第3种情况用单独的{}也可以实现,但是为什么一定要一个do{}while(0)呢,看以下代码:
#define switch(x,y) {int tmp; tmp=x;x=y;y=tmp;}
if(x>y)switch(x,y);
else        //error, parse error before elseotheraction();

在把宏引入代码中,会多出一个分号,从而会报错。使用do{….}while(0) 把它包裹起来,成为一个独立的语法单元,从而不会与上下文发生混淆。同时因为绝大多数的编译器都能够识别do{…}while(0)这种无用的循环并进行优化,所以使用这种方法也不会导致程序的性能降低,【但是并非所有情况都用这种形式,有些情况不需要,有些情况则不能够够】。


宏与函数

宏相比函数而言的优势主要在于:

  1. 宏因为是文本替换,没有函数栈的维护代价
  2. 宏参数不带类型,可以做函数不能做的工作。

解释一下,第一点 函数调用是利用函数栈帧来实现的,这个需要一定的系统资源。但是宏是直接文本替换,无函数调用过程。
第二点是因为函数的参数列表要求每个参数必须带类型,宏不需要(亦可代替函数重载),当我们想实现把类型也当作参数传入时,函数是无法做到。例如下面的宏:

#define MALLOC(size,type) ((type*)malloc(sizeof(type) * (size)))
int * p = MALLOC(10, int);

这个宏有点厉害了,传入大小和类型两个参数,然后动态分配出所需要的内存。这个肯定是无法用函数实现的。这个是不是与 C++ 的模版有点相像?模版能够实现的技术基础就是能够把参数类型当作一种参数,而宏正好就支持!!!

然而,宏的文本替换大法这么厉害,为什么还会有函数的存在嘞?
最主要的原因是:宏的文本替换大法在处理带副作用的宏参数时会导致不可预料的后果。

带副作用的宏参数

什么叫带副作用的宏参数?副作用是指某些运算符在求值的同时也会永久地改变操作数,比如:i+1

这个表达式求值没有副作用,这个式子不管运算多少次,式子的值不变。但是:
++i

却不一样了,它运算 100 次与运算 1 次的结果是不一样的,因为每次运算都会永久改变 i 的值。

那当副作用遇上宏参数会发生什么呢?看一个经典例子:

#define MAX(a,b) ((a)>(b)?(a):(b))
int a = 0;
int b = 1;
int c = MAX(a++, b++);

根据宏替换大法,我们看c被预编译器处理后的样子:

int c = ((a++)>(b++)?(a++):(b++));

ok,副作用一目了然。因为 a++

int max(a, b)
{return a>b?a:b;
}
int a = 0;
int b = 1;
int c = max(a++, b++);

这么执行的结果就是
c = 1,a = 1,b = 2;

这是我们想要的。
上面两者的区别在于,使用函数时,只在参数列表里面进行一次带副作用的运算;使用宏时,在宏的列表,宏的内容都执行了带副作用的运算。而我们通常只希望副作用运算执行一次。


特殊符号:’#’、’##’

1、一般用法

我们使用#把宏参数变为一个字符串,用##把两个宏参数贴合在一起.例如:

#include<cstdio>
#include<climits>
using namespace std;#define STR(s)     #s
#define CONS(a,b)  int(a##e##b)int main()
{// 输出字符串vckprintf(STR(vck));						// 2e3 输出:2000,科学计数法printf("%d\n", CONS(2,3));					return 0;
}

2、当宏参数是另一个宏的时候

需要注意的是凡宏定义里有用#或##的地方宏参数是不会再展开.

没有’#'和’##’的情况

#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).

当有’#'或’##’的时候

#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<climits>
printf("%s\n", CONS(A, A));               // compile error

第一个 printf() 这行会被展开为:
printf(“int max: %s\n”, #INT_MAX);

第二个 printf() 则是:
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: 0x7fffffff
STR(INT_MAX) –> _STR(0x7fffffff) 然后再转换成字符串;printf("%d\n", CONS(A, A));
输出为:200
CONS(A, A) –> _CONS((2), (2)) –> int((2)e(2))

’#'和’##’的一些应用特例:

合并匿名变量名,例:
#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__在第二层才能被解开;

填充结构
#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“}
};
记录文件名
#define  _GET_FILE_NAME(f)   #f
#define  GET_FILE_NAME(f)    _GET_FILE_NAME(f)
static char  FILE_NAME[] = GET_FILE_NAME(__FILE__);
得到一个数值类型所对应的字符串缓冲大小
#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];

__VA_ARGS__与##__VA_ARGS__

\_\_VA_ARGS__ 是一个可变参数的宏,很少人知道这个宏,这个可变参数的宏是新的 C99 规范中新增的,目前似乎只有 gcc 支持(VC6.0的编译器不支持)。实现思想就是宏定义中参数列表的最后一个参数为省略号(也就是三个点)。
##\_\_VA_ARGS__ 宏,在 \_\_VA_ARGS__ 前面加上 ## 的作用在于,当可变参数的个数为0时,这里的 ## 起到把前面多余的逗号去掉的作用,否则会编译出错。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/448052.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

解决:Cannot read property ‘component‘ of undefined ( 即 vue-router 0.x 转化为 2.x)

前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到教程。 vue项目原本是用0.x版本的vue-router&#xff0c;但是去报出&#xff1a;Cannot read property component of undefined 这是因为版本问…

AMD Mantle再添新作,引发下代GPU架构猜想

摘要&#xff1a;今年秋天即将发布的《希德梅尔文明&#xff1a;太空》将全面支持AMD Mantle API&#xff0c;如此强大的功能背后离不开强大的CPU、GPU支持。上周AMD爆出了下一代海盗岛R9 300系列&#xff0c;据网友猜测海盗岛家族可能用上速度更快的HBM堆栈式内存。 小伙伴们…

不作35岁的程序员?

程序员三部曲--不作35岁的程序员?摩西2000 在中国&#xff0c;程序员不能超过35岁&#xff0c;似乎已经是不争的事实&#xff0c;软件开发工作就是青春饭&#xff0c;顶多靠毕业这十年的时间&#xff0c;超过这个年龄&#xff0c;要不成功跃身成为管理者&#xff0c;要不转…

linux下使用TC模拟弱网络环境

linux下使用TC模拟弱网络环境 模拟延迟传输简介 netem 与 tc: netem 是 Linux 2.6 及以上内核版本提供的一个网络模拟功能模块。该功能模块可以用来在性能良好的局域网中,模拟出复杂的互联网传输性能,诸如低带宽、传输延迟、丢包等等情 况。使用 Linux 2.6 (或以上) 版本内核…

CDN 是什么 、CDN 引入

前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到教程。 CDN 的全称是 Content Delivery Network&#xff0c;即内容分发网络。 CDN的基本原理是广泛采用各种缓存服务器&#xff0c;将这些缓存…

长寿的人会有的8个健康理念

长寿的人会有的8个健康理念。年轻的时候总是在挥霍身体健康&#xff0c;吸烟、喝酒没有节制&#xff0c;到老了之后身体会出现各种问题。老年人如果想要身体健康、长寿的话&#xff0c;就要从日常生活习惯做起。下面小编就来介绍长寿的人会有的8个健康理念&#xff1a; 1、少…

Ubuntu下selenium+Chrome的安装使用

Ubuntu下seleniumChrome的安装使用 安装 chrome 官网下载安装包 sudo dpkg -i google-chrome-stable_current_amd64.deb whereis google-chrome 安装selenium pip3 install selenium 下载chromedriver(火狐使用geckodriver)驱动 http://npm.taobao.org/mirrors/chromed…

shoot for用法

Look, there are people like Ross who need to shoot for the stars, with his museum, and his papers getting published.---《老友记》 而像罗斯这种人则追求卓越&#xff0c;博物馆&#xff0c;发表论文。 争取;为...而努力Were shooting this year for a 50% increase in…

VUE : 双重 for 循环写法、table 解析任意 list 、万能表格组件、解析一维数组、动态生成 table 所有数据

前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到教程。 1.需求&#xff1a; 我想要一个 table 组件能在实际调用时动态生成所有的 tr 、td 。 后端返回的只是一个 list &#xff0c; 前端页…

安全离职妙招

高招的离职&#xff0c;不但有可能让前老板帮你说好话&#xff0c;让前同事成为你的啦啦队&#xff0c;未来若有好机会&#xff0c;还会想到你&#xff0c;只要你学会克服离职流程中的五个尴尬情境。 情境一、离职怎么提&#xff1f; 口头请辞&#xff0c;最先告知上司。 有…

字节内推~

大佬们有兴趣来字节约饭么&#xff0c;下面是内推链接~ 社招内推链接&#xff1a;https://job.toutiao.com/s/LwpKWU8 校招内推链接&#xff1a;https://job.toutiao.com/s/LwsFw6g

使用编辑工具快速创建实体对象的方法

快速创建java类 (\w)\s(.) /** $2 */\nprivate String $1; search Mode 为 Reqular expression 转载于:https://www.cnblogs.com/otways/p/11283303.html

超详细 图解 : IntelliJ IDEA 逆向生成 JAVA 实体类

前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到教程。 1.配置数据库,&#xff0c;这里连接的是mysql。 2.填写 连接数据库的信息&#xff0c;填写完成后可以点击Test Connection,测试一下是否…

用面粉和醋洗头 让你的头发黑亮又浓密

用面粉和醋洗头发&#xff0c;别看这些最廉价、最普通的东西&#xff0c;却能带来意想不到的效果。调配这种洗头液很简单&#xff0c;取50&#xff5e;100克面粉&#xff0c;加入少许凉水调成稀面糊&#xff0c;倒入沸水中煮开&#xff0c;然后加入25&#xff5e;50克醋&#x…

leetcode练习——数组篇(1)(std::ios::sync_with_stdio(false);std::cin.tie(nullptr);)

题号1. 两数之和&#xff1a; 给定一个整数数组 nums 和一个目标值 target&#xff0c;请你在该数组中找出和为目标值的那 两个 整数&#xff0c;并返回他们的数组下标。 你可以假设每种输入只会对应一个答案。但是&#xff0c;你不能重复利用这个数组中同样的元素。 示例: …

intellij idea 中去除 @Autowired 注入对象带来的红色下划线报错提示

前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到教程。 PS&#xff1a; 有 2 种方法&#xff0c;第 2 种方法更简单&#xff0c;在此谢谢好心友人的评论。 方法1&#xff1a; idea中通过Autow…

根据目标选择减肥方法 少做无用功

不同的美体目标适合的减肥方法也是不同的&#xff0c;有些人想减去大部分体重&#xff0c;而有些人只是想让身体曲线更柔美&#xff0c;这就要求有相应的减肥方法&#xff0c;对症下药&#xff0c;才会让减肥少做无用功。 目标&#xff1a;我想穿上小一码的衣服 建议&#x…

Coolite动态加载CheckboxGroup,无法在后台中获取

Coolite在后台动态加载CheckboxGroup&#xff0c;页面显示都正常&#xff0c;但是在后台去获取选中的checkbox时&#xff0c;使用下方法&#xff1a; ///<summary>///获取所选权限 ///</summary>///<returns></returns>privatestringGetPermiss…

基于java的数据结构学习——动态数组C++类模板(含拷贝构造,重载常见运算符)

之前实现了java的动态数组&#xff0c;试着写了个C版的&#xff0c;同样对时间复杂度振荡进行了处理。纯手打&#xff0c;代码如下 &#xff1a; // // Created by PC-Saw on 2018/12/19. //#ifndef DATA_STRUCTURE_MYARRAY_H #define DATA_STRUCTURE_MYARRAY_H#include <i…

科目三考试过程详解

科目三是考驾照的最后一项考试&#xff0c;所以考生在这关都很注意&#xff0c;但是有可能是由于过于紧张都难免会有些失误&#xff0c;如果没过的话&#xff0c;那也就意味着您拿照的时间又延长了另外还要交补考费。因此很多学员都想一次性把这项考试通过&#xff0c;那么我们…