宏替换是C/C++系列语言的技术特色,C/C++语言提供了强大的宏替换功能,源代码在进入编译器之前,要先经过一个称为“预处理器”的模块,这个模块将宏根据编译参数和实际编码进行展开,展开后的代码才正式进入编译器,进行词法分析、语法分析等等。
1.使用#define定义的伪函数(函数宏)
C语言中,#define经常被用来定义伪函数--当避免函数调用的开销带来的效率的重要性被置于安全性之上时。
如下:#define repeated(b, m) (b & m & (b & m)-1)
这个伪函数的定义是有问题的,如果b,m是一个表达式,b&m的值可能与想象的不同,我们应该给每一个参数加上括号(b)&(m).例如:b*m,如果b=2+3,m=3+5.我们想要的是5*8,但是现实却是2+3*3+5=16.
2.宏常量
#define MAX 1000
int array[MAX][MAX]
在经典著作《Effective C++》中,这种做法却并不提倡,书中更加推荐以const常量来代替宏常量。因为在进行词法分析时,宏的引用已经被其实际内容替换,因此宏名不会出现在符号表中。所以一旦出错,看到的将是一个无意义的数字,比如上文中的1000,而不是一个有意义的名称,如上文中的MAX。而const在符号表中会有自己的位置,因此出错时可以看到更加有意义的错误提示。
3.用于条件编译标识的宏
#ifndef _HEADER_INC_
#define _HEADER_INC_
……
……
#endif
这种宏标记在头文件中十分常见,用于防止头文件被反复包含。应该养成习惯在每个头文件中都添加这种标记。
4.宏函数
宏函数的语法有以下特点:
(1)、如果需要换行,则行末要加反斜杠“\”表示换行。宏函数体的最后一行不加反斜杠。
(2)、假设有参数ARGU,值为argu,则所有的ARGU被直接替换为argu,#ARGU被认为是字符串,会被替换成"argu"(带引号)。
(3)、由于宏函数是直接替换,所有一般情况下对宏函数的调用时行末不需要加分号。
宏函数的作用:
1)、避免函数调用,提高程序效率,其实宏是用空间效率换取了时间效率。
2)、#undef指令用于取消前面用#define定义的宏,取消后就可以重新定义宏。
3)、方便程序修改
实例:
#define SWAP_INT(a, b) do
{\
int tmp = a; \
a = b; \
b = tmp; \
}while(0)
int main( void )
{
int x = 3, y = 4;
if( x > y )
{
SWAP_INT(x, y);
}
return 0;
}
5.关于#和##
#define Conn(x,y) x##y
#define Tochar(x) #@x
#define ToString(x) #x
其中,x##y表示x连接y;int n=Conn(123,456);n=123456.
char *str=Conn("asdf","adf");str="asdfadf".
#@x表示给x加上单引号; char a=ToChar(1); a='1'.
#x表示给x加上双引号; char* str=ToString(123321); str="123321".
6.宏与内联函数的关系
1.内联函数可以调试,而宏定义是不可以调试的。
2.内联函数在可读性方面与函数是相同的,而在编译时是将函数直接嵌入调用程序的主体,省去了调用/返回指令,这样在运行时速度更快。
3.内联函数在可读性方面与函数是相同的,而在编译时是将函数直接嵌入调用程序的主体,省去了调用/返回指令,这样在运行时速度更快。
下例:
4.和宏不同的,还有内联函数的参数类型被检查,并且被正确地进行必要的转换。
7.经典宏
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 ) \
/*lint -e545 */ ( (dword) &(( type *) 0)-> field ) /*lint +e545 */
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(***) ((byte) ((word)(***) & 255))
#define WORD_HI(***) ((byte) ((word)(***) >> 8))
11,返回一个比X大的最接近的8的倍数
#define RND8( x ) ((((x) + 7) / 8 ) * 8 )
12,将一个字母转换为大写
#define UPCASE( c ) ( ((c) >= 'a' && (c) <= 'z') ? ((c) - 0x20) : (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,返回一个无符号数n尾的值MOD_BY_POWER_OF_TWO(X,n)=X%(2^n)
#define MOD_BY_POWER_OF_TWO( val, mod_by ) \
( (dword)(val) & (dword)((mod_by)-1) )
18,对于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)))
[2005-9-9添加]
19,使用一些宏跟踪调试
A N S I标准说明了五个预定义的宏名。它们是:
_ L I N E _
_ F I L E _
_ D A T E _
_ T I M E _
_ S T D C _
如果编译不是标准的,则可能仅支持以上宏名中的几个,或根本不支持。记住编译程序
也许还提供其它预定义的宏名。
_ L I N E _及_ F I L E _宏指令在有关# l i n e的部分中已讨论,这里讨论其余的宏名。
_ D AT E _宏指令含有形式为月/日/年的串,表示源文件被翻译到代码时的日期。
源代码翻译到目标代码的时间作为串包含在_ T I M E _中。串形式为时:分:秒。
如果实现是标准的,则宏_ S T D C _含有十进制常量1。如果它含有任何其它数,则实现是
非标准的。
可以定义宏,例如:
当定义了_DEBUG,输出数据信息和所在文件所在行
#ifdef _DEBUG
#define DEBUGMSG(msg,date) printf(msg);printf(“%d%d%d”,date,_LINE_,_FILE_)
#else
#define DEBUGMSG(msg,date)
#endif
20,宏定义防止使用是错误
用小括号包含。
例如:#define ADD(a,b) (a+b)
用do{}while(0)语句包含多语句防止错误
例如:#difne DO(a,b) a+b;\
a++;
应用时:if(….)
DO(a,b); //产生错误
else
发表评论
-
set容器的反向迭代器
2013-05-02 16:56 3741#include <iostream> #in ... -
对于CRITICAL_SECTION用法的介绍和理解[转]
2013-04-08 11:50 2178很多人对CRITICAL_SECTION ... -
二维数组知识
2012-09-15 17:20 814二维数组和指针⑴ 用 ... -
Realloc的使用
2012-08-14 11:04 826realloc 用过很多次了。 ... -
extern C的由来
2012-08-09 10:14 690时常在cpp的代码之中看到这样的代码: #ifdef ... -
C++类对象的创建过程
2012-07-26 16:02 936分配空间(Allocation) ... -
静态数据成员和静态成员函数
2012-07-26 15:04 3105静态类成员包括静态数据成员和静态函数成员两部分。 与 ... -
复制构造函数(拷贝构造函数)以及深浅拷贝
2012-07-25 22:39 1424对于普通对象而言复制是很简单的,一般是将变量或者常量赋值给某 ... -
cin、cin.get、cin.getline()、getline()、gets()的用法【转】
2012-07-24 20:05 828学C++的时候,这几个输入函数弄的有点迷糊;这里做个小结,为了 ... -
编程笔记(07-24)
2012-07-24 15:15 6651 #include < stdio.h ... -
堆、栈解疑
2012-07-12 21:53 580一、预备知识—程序的内存分配 一个由C/C++编译的程序 ... -
指针和内存分配的深度理解
2012-07-12 18:57 1028一 :关于指针和堆的内存分配 先来介绍一下指针: 指针一种 ... -
数组指针和指针数组
2012-07-12 18:56 1183先看一下基本的形式,我们从这里起步! ----------- ... -
const指针和指向const的指针
2012-07-12 10:30 2114指向const对象的指针 ... -
typedef的学习
2012-07-11 15:03 737typedef,顾名思义,为“类型定义”,可以解释为:将一种数 ... -
函数指针和指针函数
2012-07-11 11:21 589【函数指针】 ... -
sizeof 深研
2012-07-11 09:39 6651、什么是sizeof 首先看一下sizeof ... -
内存对齐问题
2012-07-10 22:35 10951.内存数据对齐的原因: 无论如何,为了提高程序的性 ... -
指针深究
2012-07-09 21:55 589在说指向指针的指针之前,不得不说指向变量的指针。先看如下示例: ... -
C语言文件使用方式详解
2012-07-04 10:23 763文件的打开(fopen函数) f ...
相关推荐
标题中的"DEFINE_DPM_EROSION.rar_DEFINE DPM EROSION_DPM_UDF EROSION_...通过分析提供的源代码 "DEFINE_DPM_EROSION.c",我们可以深入学习如何在 Fluent 中自定义复杂的物理模型,并实现对颗粒侵蚀行为的精确模拟。
### 构造函数与`return`对象学习 #### 一、引言 本文将深入探讨JavaScript中的构造函数以及如何在构造函数中返回一个对象。在JavaScript编程中,构造函数是一种特殊类型的函数,用于创建和初始化特定类型的新对象。...
这些源代码可以作为参考,学习如何使用FLUENT的UDF接口来定义复杂的金属材料属性,并进行热力学模拟。 总的来说,理解和编写FLUENT的UDFs是理解和掌握高级流体模拟技术的关键步骤。通过UDFs,我们可以更精确地模拟...
在CFD(计算流体动力学)领域,Fluent是一款广泛应用的商业软件,用于模拟...通过对"第四章 DEFINE宏_UDf宏_UDFdescription_"的学习,读者将能更好地理解和运用这些工具,从而在解决复杂的流体力学问题时更加得心应手。
### C++/C宏定义(define)...通过本文的学习,我们了解到`#`和`##`在C++/C宏定义中的作用以及如何运用它们来提高代码的灵活性和可读性。希望本文能够帮助读者更好地掌握这些宏定义技巧,并在实际项目中合理利用它们。
标题 "VIVUDF2_CG_MOTION_DEFINE_CG_MOTION_二维运动动网格_" 提供的信息表明,这个主题涉及到使用Fluent软件进行流体动力学(CFD)模拟时,利用CG_MOTION功能来定义二维物体的动态运动。描述中的 "fluent DEFINE_CG...
在数据分析或机器学习中,`define`可能用于明确指标或特征的计算方式,如“定义‘高收入’为月收入超过5000美元”,这有助于后续分析的标准化。 最后,`define`在讨论抽象概念时也十分有用,如第9个例句中定义“酷...
NX二次开发UF_DRAW_define_view_boundary1 函数介绍,Ufun提供了一系列丰富的 API 函数,可以帮助用户实现自动化、定制化和扩展 NX 软件的功能。无论您是从事机械设计、制造、模具设计、逆向工程、CAE 分析等领域的...
### #define宏定义的一些用法总结 ...通过对本文介绍的各种宏定义技巧的学习与应用,开发者能够编写出更加高效、易维护的代码。然而,由于宏的特殊性质,在使用时需格外小心,避免因不当使用而导致难以预料的问题。
【Huan】_傲視群雄的全塔式機殼!_支援18顆硬碟、420水冷!!_Fractal_Design_Define_7_XL深度評
经过各种尝试,居然成了,特此记录分享一下,方便大家学习。 char A_param=0; char B_pramm=0; //添加宏定义 #define OBJECT A #define DEFINE_(X) X##_param //一次定义 #define DEFINE(X) DEFINE_(X) //再次定义 ...
在C++编程中,`extern "C"`、`#ifndef`、`#define` 和 `#endif` 是四种非常重要的预处理器指令,它们在不同场景下有着特定的作用。...通过学习和掌握这些基础,开发者能够更好地编写跨平台、易于维护的软件。
学习了这么多年C语言,说实话对宏自以为了如指掌了,没想到看内核代码的时候还是那么吃力,设备驱动代码中有很多这样或者那样的宏定义,各种define,博主在学习的过程中将C语言中所出现的#define定义整理总结了一下...
通过以上内容的学习,我们可以了解到eCognition_define教程不仅涵盖了基本的概念介绍,还提供了实用的操作指南和案例分析。掌握了这些知识后,用户能够更加高效地利用eCognition软件进行遥感数据分析,提高研究工作...
【标题】"定义Tomcat"(define_tomcat.zip)是一个学习项目,旨在帮助开发者理解并模仿Apache Tomcat服务器的工作原理。这个压缩包包含了一个小型的案例,它封装了HTTP请求(Request)和响应(Response)对象,通过...
通过分析和学习`define_trace.c`,开发者可以获得对Linux内核动态跟踪系统更深入的理解,从而更好地进行内核级别的调试和优化工作。对于想要提升内核开发技能或者进行系统级问题排查的人来说,这份代码是宝贵的资源...
通过这个压缩包,我们可以学习到如何在IDL中使用面向对象编程来创建自定义类,理解类的属性、方法、构造函数、访问修饰符,以及如何实例化对象并调用其方法。这些都是IDL编程中非常重要的基础知识。
AMX学习是一个涵盖广泛的主题,尤其对于初学者来说,理解其基本概念和操作流程至关重要。AMX是一个先进的控制系统,主要用于智能家居、商业建筑、会议中心等环境的自动化管理。在这个过程中,我们首先需要了解如何...