C语言宏定义和宏定义函数
要写好C语言,漂亮的宏定义是非常重要的。宏定义可以帮助我们防止出错,提高代码的可移植性和可读性等。
在软件开发过程中,经常有一些常用或者通用的功能或者代码段,这些功能既可以写成函数,也可以封装成为宏定义。那么究竟是用函数好,还是宏定义好?这就要求我们对二者进行合理的取舍。
我们来看一个例子,比较两个数或者表达式大小,首先我们把它写成宏定义:
#define MAX( a, b) ( (a) > (b) (a) : (b) )
其次,把它用函数来实现:
int max( int a, int b)
{
return (a > b a : b)
}
很显然,我们不会选择用函数来完成这个任务,原因有两个:首先,函数调用会带来额外的开销,它需要开辟一片栈空间,记录返回地址,将形参压栈,从函数返回还要释放堆栈。这种开销不仅会降低代码效率,而且代码量也会大大增加,而使用宏定义则在代码规模和速度方面都比函数更胜一筹;其次,函数的参数必须被声明为一种特定的类型,所以它只能在类型合适的表达式上使用,我们如果要比较两个浮点型的大小,就不得不再写一个专门针对浮点型的比较函数。反之,上面的那个宏定义可以用于整形、长整形、单浮点型、双浮点型以及其他任何可以用“>”操作符比较值大小的类型,也就是说,宏是与类型无关的。
和使用函数相比,使用宏的不利之处在于每次使用宏时,一份宏定义代码的拷贝都会插入到程序中。除非宏非常短,否则使用宏会大幅度增加程序的长度。
还有一些任务根本无法用函数实现,但是用宏定义却很好实现。比如参数类型没法作为参数传递给函数,但是可以把参数类型传递给带参的宏。
看下面的例子:
#define MALLOC(n, type) \
( (type *) malloc((n)* sizeof(type)))
利用这个宏,我们就可以为任何类型分配一段我们指定的空间大小,并返回指向这段空间的指针。我们可以观察一下这个宏确切的工作过程:
int *ptr;
ptr = MALLOC ( , int );
将这宏展开以后的结果:
ptr = (int *) malloc ( () * sizeof(int) );
这个例子是宏定义的经典应用之一,完成了函数不能完成的功能,但是宏定义也不能滥用,通常,如果相同的代码需要出现在程序的几个地方,更好的方法是把它实现为一个函数。
下面总结和宏和函数的不同之处,以供大家写代码时使用,这段总结摘自《C和指针》一书。
example:
define的单行定义
#define maxi(a,b) (a>;b?a:b)
define的多行定义
define可以替代多行的代码,例如MFC中的宏定义(非常的经典,虽然让人看了恶心)
#define MACRO(arg1, arg2) do { \
\
stmt1; \
stmt2; \
\
} while()
关键是要在每一个换行的时候加上一个 "\ "
//宏定义写出swap(x,y)交换函数
#define swap(x, y)\
x = x + y;\
y = x - y;\
x = x - y;
zigbee里多行define有如下例子
#define FillAndSendTxOptions( TRANSSEQ, ADDR, ID, LEN, TxO ) { \
afStatus_t stat; \
ZDP_TxOptions = (TxO); \
stat = fillAndSend( (TRANSSEQ), (ADDR), (ID), (LEN) ); \
ZDP_TxOptions = AF_TX_OPTIONS_NONE; \
return stat; \
}
C语言宏定义和宏定义函数的更多相关文章
- C语言利用va_list、va_start、va_end、va_arg宏定义可变参数的函数
在定义可变参数的函数之前,先来理解一下函数参数的传递原理: 1.函数参数是以栈这种数据结构来存取的,在函数参数列表中,从右至左依次入栈. 2.参数的内存存放格式:参数的内存地址存放在内存的堆栈段中,在 ...
- 【C语言】预处理、宏定义、内联函数 _
一.由源码到可执行程序的过程 1. 预处理: 源码经过预处理器的预处理变成预处理过的.i中间文件 1 gcc -E test.c -o test.i 2. 编译: 中间文件经过编译器编译形成.s的 ...
- 【C语言】预处理、宏定义、内联函数
一.由源码到可执行程序的过程 1. 预处理: 源码经过预处理器的预处理变成预处理过的.i中间文件 1 gcc -E test.c -o test.i 2. 编译: 中间文件经过编译器编译形成.s的汇编 ...
- C语言 预处理二(宏定义--#define)
//#define 宏定义(宏定义一般大写) //知识点一-->#define的作用域:从#define开始,从上往下,如果遇到#undef就到#undef处结束,如果没有就是作用于当前整个文件 ...
- __inline定义的内联函数和宏的区别
转自:http://blog.csdn.net/lw370481/article/details/7311668 函数与宏 #define TABLE_COMP(x) ((x)>0?(x):0) ...
- 预定义宏,C语言预定义的宏详解
1.预定义宏 对于预定义宏,相信大家并不陌生.为了方便处理一些有用的信息,预处理器定义了一些预处理标识符,也就是预定义宏.预定义宏的名称都是以"__"(两条下划线)开头和结尾的,如 ...
- C语言宏的定义和宏的使用方法(#define)
1.宏的功能介绍 在 C 语言中,可以采用命令 #define 来定义宏.该命令允许把一个名称指定成任何所需的文本,例如一个常量值或者一条语句.在定义了宏之后,无论宏名称出现在源代码的何处,预处理器都 ...
- linux 中C语言便于调试的宏定义编写及 __FILE__,__FUNCTION__, __LINE__参数使用
转自:http://blog.csdn.net/edonlii/article/details/8491342/ 在linux编程中,当文件数量变的众多之后,使用gdb调试就是一场灾难.因此在程序中加 ...
- 易出错的C语言题目之一:宏定义与预处理
1.写出下列代码的运行结果: #include<stdio.h> #include<string.h> #define STRCPY(a,b) strcpy(a##_p,#b) ...
随机推荐
- day4 递归二分法查找
现有一个序列,data=[for i in range(1,5000,3)],现在要求看一个数是否在列表中存在,我们知道,我们可以使用in或__contains__()的方法,判断一个值是否在列表中, ...
- int类中的方法
我们知道在python中,一切对象都是类,对象的方法都封装在类中,现在来探讨一下int类中的方法: 我们可以通过help(int)和dir(int)来查看int类中都封装了那些方法: 1.bi ...
- Android逆向之旅---反编译利器Apktool和Jadx源码分析以及错误纠正
Android逆向之旅---反编译利器Apktool和Jadx源码分析以及错误纠正 http://blog.csdn.net/jiangwei0910410003/article/details/51 ...
- Wannafly挑战赛7 B - codeJan与旅行
题目描述 codeJan 非常喜欢旅行.现在有 n 个城市排在一条线上,并且 codeJan 的位置不和任何一个城市的位置重叠.codeJan 想要游览 m 个城市,同时因为时间是不断变化的,游览一个 ...
- Blob和Clob在JDBC中的简介
数据库在当今的应用越来越广泛了,同样伴随着领域的广泛,存储的内容也不在是只有数值.字符.boolean几种类型,而是越来越多样化.在这样的前提下就出现了Blob和Clob两个类型.下面我将对这个两个类 ...
- 【C++初级】static用法总结、问题探讨及常见错误排查
static的基本用法: static的作用主要有两种第一个作用是限定作用域:第二个作用是保持变量内容持久化: 一.c语言中static的用法: 1.全局静态变量: 用法:在全局变量前加上关键字sta ...
- 前端获取Base64字符串格式图片Ajax到后端处理
前端获取到的Base64字符串格式图片一般都是经过处理的图片,例如:裁剪过后的,这里假设data为获取到的Base64字符串格式图片 Base64格式图片的格式为 “data:image/png;ba ...
- 【树形DP】BZOJ1596-[Usaco2008 Jan]电话网络
[题目大意] 在一棵有n个节点的树上建信号塔,每个节点的信号塔可以覆盖当前节点极其相连的节点.问要覆盖所有节点,至少需要多少座信号塔? [思路] 经典的树形DP,直接复制一下. f[i][0]:以i为 ...
- ZOJ 1015 弦图判定
一些定义: 弦图是一种特殊图:它的所有极小环都只有3个顶点. 单纯点:该顶点与其邻接点在原图中的导出子图是一个完全图. 图G的完美消去序列:一个顶点序列a1a2a3...an,使得对于每个元素ai,a ...
- Java 请求webServce接口 带参数
public String getWebServiceByParams(String param){ //获取基金缴付记录 // Post请求的url,与get不同的是不需要带参数 URL postU ...