当然宏定义非常重要的,它可以帮助我们防止出错,提高代码的可移植性和可读性等。下面列举一些成熟软件中常用得宏定义1,防止一个头文件被重复包含
#ifndef COMDEF_H
#define COMDEF_H//头文件内容 …
#endif2,重新定义一些类型,防止由于各种平台和编译器的不同,而产生的类型字节数差异,方便移植。
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( c ) ( ((c) >= ’a' && (c) <= ’z') ? ((c) - 0×20) : (c) )13,判断字符是不是10进值的数字
#define  DECCHK( c ) ((c) >= ’0′ && (c) <= ’9′)14,判断字符是不是16进值的数字
#define  HEXCHK( c ) ( ((c) >= ’0′ && (c) <= ’9′) ||
((c) >= ’A' && (c) <= ’F') ||
((c) >= ’a' && (c) <= ’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,使用一些宏跟踪调试
ANSI标准说明了五个预定义的宏名。它们是:
__LINE__
__FILE__
__DATE__
__TIME__
__STDC__如果编译不是标准的,则可能仅支持以上宏名中的几个,或根本不支持。记住编译程序
也许还提供其它预定义的宏名。
是行连接符,会将下一行和前一行连接成为一行,即将物理上的两行连接成逻辑上的一行
__FILE__ 是内置宏 代表源文件的文件名
__LINE__ 是内置宏,代表该行代码的所在行号
__DATE__宏指令含有形式为月/日/年的串,表示源文件被翻译到代码时的日期。
源代码翻译到目标代码的时间作为串包含在__TIME__ 中。串形式为时:分:秒。
如果实现是标准的,则宏__STDC__含有十进制常量1。如果它含有任何其它数,则实现是非标准的。可以定义宏,例如:
当定义了_DEBUG,输出数据信息和所在文件所在行#ifdef _DEBUG
#define DEBUGMSG(msg,date) printf(msg);printf(“%d%d%d”,date,_LINE_,_FILE_)
#else
#define DEBUGMSG(msg,date)
#endif19,宏定义防止使用是错误
用小括号包含。
例如:#define ADD(a,b) (a+b)用do{}while(0)语句包含多语句防止错误例如:#difne DO(a,b) a+b;
a++;
应用时:if(….)
DO(a,b); //产生错误
else
解决方法: #difne DO(a,b) do{a+b;
a++;}while(0)为什么需要do{…}while(0)形式?总结了以下几个原因:1),空的宏定义避免warning:
#define foo() do{}while(0)2),存在一个独立的block,可以用来进行变量定义,进行比较复杂的实现。
3),如果出现在判断语句过后的宏,这样可以保证作为一个整体来是实现:
#define foo(x)
action1();
action2();
在以下情况下:
if(NULL == pPointer)foo();
就会出现action2必然被执行的情况,而这显然不是程序设计的目的。4),以上的第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)这种无用的循环并进行优化,
所以使用这种方法也不会导致程序的性能降低。为什么很多linux内核中宏#defines用do { … } while(0)?有很多原因:(Dave Miller的说法):编译器对于空语句会给出告警,这是为什么#define FOO do{ }while(0);给定一个基本块(局部可视域),定义很多局部变量;(Ben Collins的说法):在条件代码中,允许定义复杂的宏。可以想像有很多行宏,如下代码#define FOO(x)
printf(“arg is %sn”, x);
do_something_useful(x);
现在,想像下面的应用:
if (blah == 2)
FOO(blah);
展开后代码为:
if (blah == 2)
printf(“arg is %sn”, blah);
do_something_useful(blah);;
就像你看到的,if仅仅包含了printf(),而do_something_useful()调用是无条件调用。
因此,如果用do { … } while(0),结果是:
if (blah == 2)
do {
printf(“arg is %sn”, blah);
do_something_useful(blah);
} while (0);
这才是所期望的结果。
(Per Persson的说法):
像 Miller and Collins指出的那样,需要一个块语句包含多个代码行和声明局部变量。
但是,本质如下面例子代码:
#define exch(x,y) { int tmp; tmp=x; x=y; y=tmp; }
上面代码在有些时候却不能有效工作,下面代码是一个有两个分支的if语句:
if (x > y)
exch(x,y);               // Branch 1
else
do_something();      // Branch 2
展开后代码如下:
if (x > y){                // Single-branch if-statement!!!
int tmp;            // The one and only branch consists
tmp = x;           // of the block.
x = y;
y = tmp;
}
;                // empty statement
else                             // ERROR!!! “parse error before else”
do_something();
问题是分号(;)出现在块后面。解决这个问题可以用do{}while(0):
if (x > y)
do {
int tmp;
tmp = x;
x = y;
y = tmp;
} while(0);
else
do_something();
( Bart Trojanowski的说法):
Gcc加入了语句解释,它提供了一个替代do-while-0块的方法。
对于上面的解决方法如下,并且更加符合常理
#define FOO(arg) ({
typeof(arg) lcl;
lcl = bar(arg);
lcl;
})
这是一个奇怪的循环,它根本就只会运行一次,为什么不去掉外面的do{..}while结构呢?
我曾一度在心里把它叫做“怪圈”。原来这也是非常巧妙的技巧。在工程中可能经常会引起麻烦,
而上面的定义能够保证这些麻烦不会出现。
下面是解释:
假设有这样一个宏定义
#define macro(condition)
if(condition) dosomething()
现在在程序中这样使用这个宏:
if(temp)
macro(i);
else
doanotherthing();
一切看起来很正常,但是仔细想想。这个宏会展开成:
if(temp)
if(condition) dosomething();
else
doanotherthing();
这时的else不是与第一个if语句匹配,而是错误的与第二个if语句进行了匹配,编译通过了,
但是运行的结果一定是错误的。为了避免这个错误,我们使用do{….}while(0) 把它包裹起来,
成为一个独立的语法单元,从而不会与上下文发生混淆。同时因为绝大多数的编译器都能够识别do{…}while(0)
这种无用的循环并进行优化,所以使用这种方法也不会导致程序的性能降低。另一个讲解
这是为了含多条语句的宏的通用性
因为默认规则是宏定义最后是不能加分号的,分号是在引用的时候加上的
比如定义了一个宏fw(a,b),那么在c文件里一定是这样引用
fw(a,b);
如果不用do…while,那么fw就得定义成:
#define fw(a,b) {read((a));write((b));}
那这样fw(a,b);展开后就成了:
{read(a);write(b);};
最后就多了个分号,这是语法错误
而定义成do…while的话,展开后就是:
do{read(a);write(b);}while(0);    完全正确
所以要写一个包含多条语句的宏的话,不用do…while是不可能的宏中#和##的用法一、一般用法
我们使用#把宏参数变为一个字符串,用##把两个宏参数贴合在一起.
用法:
#include<cstdio>
#include<climits>
using namespace std;#define STR(s)     #s
#define CONS(a,b)  int(a##e##b)int main(){printf(STR(vck));               // 输出字符串vck
printf(%dn, CONS(2,3));  // 2e3 输出:2000
return 0;
}二、当宏参数是另一个宏的时候
需要注意的是凡宏定义里有用’#'或’##’的地方宏参数是不会再展开.1, 非’#'和’##’的情况
#define TOW      (2)
#define MUL(a,b) (a*b)printf(%d*%d=%dn, TOW, TOW, MUL(TOW,TOW));
这行的宏会被展开为:
printf(%d*%d=%dn, (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: %sn”,  STR(INT_MAX));    // INT_MAX #include<climits>
这行会被展开为:
printf(“int max: %sn”, #INT_MAX);printf(%sn, CONS(A, A));               // compile error
这一行则是:
printf(%sn, 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: %sn, STR(INT_MAX));
//INT_MAX,int型的最大值,为一个变量 #include<climits>
输出为: int max: 0x7fffffff
STR(INT_MAX) –>  _STR(0x7fffffff) 然后再转换成字符串;printf(%dn, 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__在第二层才能被解开;2、填充结构
#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“}};3、记录文件名
#define  _GET_FILE_NAME(f)   #f
#define  GET_FILE_NAME(f)    _GET_FILE_NAME(f)
static char  FILE_NAME[] = GET_FILE_NAME(__FILE__);4、得到一个数值类型所对应的字符串缓冲大小
#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];

C语言~宏操作大全(宏定义、内置宏、__FILE__、__LINE__、##用法)相关推荐

  1. C语言宏定义、宏函数、内置宏与常用宏

    前言: 在C语言中,变量类型.循环控制.基础语法等与其他高级语言基本无异:而C语言(C++)特有的两把双刃剑指针和宏定义/宏函数使得C语言在底层开发中披荆斩棘.无所不能.这两个概念涉及范围比较广,其分 ...

  2. unity android 宏定义,Unity各平台内置宏定义

    属性 方法 UNITY_EDITOR #define directive for calling Unity Editor scripts from your game code. UNITY_EDI ...

  3. linux系统中查看gcc内置宏定义的命令方法

    GNU C预定义了一系列的宏,这些宏都是以双下划线开始的 查看内置宏的命令 gcc -dM -E - < /dev/null [qipa250@localhost ~]$ gcc -dM -E ...

  4. 搜索一个问题 C、C++判断操作系统 是 Linux还是windows 还是Unix【编译器内置宏 探索(不是特别满意)】...

    得到的答案 都不好.为什么,因为他们都不知道原理.其实原理很简单,编译器 参数或Makefile添加了宏定义,你才可以这样去判断. 比如编译器设置了宏 is_windows,你才能去用.不设置没法用. ...

  5. 巧用可变参数宏、编译器内置宏和printf输出调试信息

    前言: 我们在写程序的时候,总是或多或少会加入一些printf之类的语句用于输出调试信息,但是printf语句有个很不方便的地方就是当我们需要发布程序的时候要一条一条的把这些语句删除,而一旦需要再次调 ...

  6. __iLINE__和__FILE__等内置宏的含义

    编译器内置宏: 先介绍几个编译器内置的宏定义,这些宏定义不仅可以帮助我们完成跨平台的源码编写,灵活使用也可以巧妙地帮我们输出非常有用的调试信息. ANSI C标准中有几个标准预定义宏(也是常用的): ...

  7. word中套用表格样式在哪里_在Word2010中,关于“套用内置表格样式”的用法,下列说法正确的是_学小易找答案...

    [简答题]1.5%的琼脂溶胶在32~39°C之间可以形成坚实而有弹性的____,并在85°C以下不融化为____,可用以区别于其他海藻胶. [单选题]云南省的简称为? [单选题]在Word2010编辑 ...

  8. python内置函数 enumerate()的用法

    python内置函数 enumerate()的用法 enumerate()对于一个可迭代的(iterable)/可遍历的对象(如列表.字符串),enumerate将其组成一个索引序列(元组),利用它可 ...

  9. mysql+curdate+昨天_MySQL内置时间curdate查询用法

    MySQL内置时间curdate查询用法 文章作者:网友投稿 发布时间:2010-08-15 10:02:19 来源:网络 总结一下MySQL内置时间查询用法 mysql SELECT year(cu ...

最新文章

  1. Human Pose Estimation人体姿态估计综述调研
  2. 操作与配置CISCO设备
  3. 网络推广——网络推广专员是如何进行图像优化的?
  4. 电脑鼠标自己乱跳乱点_无线鼠标VS有线鼠标,二者区别何在?你可知晓?不妨来了解一下...
  5. python tkinter火柴人_趣学Python编程
  6. error: cannot read: aaa.java_Java期末考试习题库[选择题填空题改错题学生].doc
  7. Keli Linux与网络安全(2)——初探Keli
  8. eclipse汉化教程及汉化包
  9. 计算机excel试题各科学员,计算机Excel练习题一及答案.doc
  10. 安装vue最新脚手架
  11. git stash '储藏'当前工作状态
  12. 爬虫实例:唐诗宋词爬虫
  13. Microsoft Word 教程「6」,如何在 Word 中插入艺术字?
  14. Springboot-软件授权License
  15. centos7设置基础软件仓库时出错
  16. 数学无敌—王老菊教你当典狱长
  17. Android手机拍照APP闪退问题及应对措施
  18. Acme Corporation UVA11613 网络流
  19. mask rcnn 超详细代码解读(一)
  20. Vue 安装 Element UI时报错 code ERESOLVE unable to resolve dependency tree

热门文章

  1. Live预告 | 地平线李星宇:智能汽车电子构架如何变革迎接数字化重塑?
  2. FPGA学习日志——三八译码器Decoder
  3. 越南籍学生如何进行计算机教学,越南三年级数学神题太难博士动用计算机(图)...
  4. android videoview 拉伸,手摸手带你用 VideoView 实现英语流利说炫酷引导页
  5. Practice—毛绒玩具名字(字典序排序)
  6. Flash 实验 遮罩层
  7. jflow-core 代码
  8. Git客户端msysGit的安装
  9. UltraISO软件下载安装及制作 Linux 系统 U 盘启动盘
  10. jquery实现下拉加载更多