使用#define定义字面值和伪函数
#define是C语言提供的宏命令,其主要目的是:在编程时,为程序员提供一定方便,并能在一定程度上提高程序的执行效率。#define将一个标示符定义为一个字符串,该标示符被称为宏,被定义的字符串称为字符替换文本。宏定义有两种形式:一种是简单宏定义(即字面值),另一种是带参数宏定义(即通常说的伪函数)
第一种:#define <宏名> <字符串> #define PI 3.1415926
一般,宏名用大写字母表示,但这并非规定,也可以小写,从编码一致性,可读性角度考虑,强烈建议宏名统一采用大写字母表示
使用宏名代替一个字符串,可以减少程序中重复书写某些字符串的工作量,同时也避免了由于疏忽导致的字符串书写错误问题
宏定义是用宏名代替一个字符串,也就是做简单的置换,并不做正确性检查。如写成:#define PI 3.1415926(把1错写成l),在预编译时,不会做任何语法检查,在编译宏展开后的源程序时,才会发现语法错误并报告
宏定义不是C语句,不必在行尾加分号,如果加了分号,将连同分号一起被替换
在程序中,如果#define出现在函数外面,宏的有效范围为宏定义到本源文件结束,通常宏定义写在文件开头,在此文件范围内有效。另外,用#undef可终止宏定义的作用域,这样可以灵活控制宏定义的作用范围
在进行宏定义时,可以用已经定义的宏名,可层层置换,但对于用引号引起来的字符串内的字符,即使与宏名相同,也不进行替换
宏定义是专门用于预处理的专用名词,它与定义变量的含义不同,只做字符替换,不分配内存空间
在C语言中,通过简单的宏定义恒值常量,是C语言中定义恒值的唯一手段,但在C++中这并不是唯一的方法,C++中的const也可以定义一个恒值常量。
二者区别如下
1.const定义的常量有数据类型,而#define定义的常量无数据类型
2.有些高度程序可以对const进行调试,但无法对#define进行调试
3.当定义局部变量时,const作用域仅限于定义局部变量的函数体。但#define不是,而是从定义点到整个程序的结束点。但可用#undef取消其定义,从而限定其作用域范围
4.const还可修饰函数形式参数,返回值和类的成员函数等,从而提高函数的健壮性。因为const修饰的内容必须受C/C++的静态类型安全检查机制的强制保护,可防止意外修改
带参数的宏定义形式为:
#define<宏名> (<参数表>) <宏体> 例如:#define MAX(x,y) (x)>(y)?(x):(y)
标示符被定义为宏后,该标识符便是一个宏名,在程序中,出现宏名的地方在程序被编译前,先将宏名用被定义的字符串替换,称为宏替换,替换后才进行编译,宏替换是简单的字符替换
带参数宏展开,只是将语句中宏名后面括号内的实参字符串替换#define宏定义中的形式参数
宏定义时,宏名与包含宏参数的括号之间不应加空格,否则编译器会将空格后的字符都作为替换字符串的一部分
带参数的宏与函数区别如下
1.函数调用时,先求实参表达式的值,然后传递给形参,而使用带参数的宏只是进行简单的字符串替换,不进行表达式求值
2.函数调用是在程序运行时处理的,为形参分配临时的内存空间,而宏展开是在编译前进行的,在展开时并不分配内存单元,不进行值的传递,同样也没有“返回值”的概念
3.函数的实参和形参都必须定义类型,二者的类型要求一致,如不一致,则要进行类型转换,而宏不存在类型问题,因为宏名无类型,它的参数也无类型,仅是字符串替换,展开时代入指定的字符串即可。宏定义时,字符串可以是任何类型的数据
4.调用函数只会获得一个返回值,而宏则不然,可设法得到几个返回值
5.宏展开会促使源程序变长,因为每次展开都使程序增长,而函数调用不会增加代码长度
6.宏替换不占用运行时间,只占用编译时间,而函数调用则占用运行时间用于分配单元,保存现场,值传递,返回等。所以宏替换可以提供程序的执行效率,但编译时间会变长
宏的引入带来的好处
1.宏定义的引入可方便程序的修改。使用简单宏定义,可用宏替换那些在程序中经常使用的常量,当需要修改该常量时,不用对整个程序进行修改,只修改宏定义的字符串即可,而且当常量比较长时,可以用较短,有意义的标识符来编写程序,这样更方便
2.提高程序的运行效率
使用带参数的宏定义既可完成函数调用的功能,又能减少系统开销,提高运行效率。宏定义是在预处理阶段就进行了宏展开,在执行时不需要替换。宏定义可完成简单的操作,但复杂的操作还是要由函数来完成,而且宏定义所占用的目标代码空间相对较大,所以在使用时要依据具体情况决定是否使用宏定义。
3.#define预处理完全没有把C++的作用域纳入考量,绝大多数C++实现都是封装在命名空间里,这样的做法有很多优点。但不幸的是,#define的作用域并未被限定在名字空间里
4.宏还可以用于条件编译,例如
#ifdef WINDOWS
...
#endif
#ifdef LINUX
...
#endif
在应用时,如果要定义一个字面值常量建议使用const替换#define
宏要注意的问题
1.由操作符优先级引起的问题
由于宏只是简单的替换,宏的参数如果是复合结构,那么通过替换之后可能由于各个参数之间的操作符优先级高于单个参数内部各部分之间相互作用的操作符优先级,如果不用括号保护各个宏参数,就会产生意想不到的情况
如#define ceil_div(x,y) (x+y-1)/y
那么a = ceil_div(b&c,sizeof(int))将被转化为a=(b&c+sizeof(int)-1)/sizeof(int)//由于+/-的优先级高于&的优先级,那么上面的式子等同于
a= (b&(c+sizeof(int)-1))/sizeof(int);
所以应改为#define ceil_div(x,y) (((x)+(y)-1)/(y))
2.使用宏定义,不允许参数发生变化
例如:
#include<stdio.h> int main()
#define sqrt(a) ((a)*(a)) {
int fsqrt(int a) int a = 10,b=10;
{ int r1,r2;
return a*a; r1 = sqrt(a++);
} r2 = fsqrt(b++);
printf("a=%d,b=%d,r1=%d,r2=%d\n",a,b,r1,r2);
return 0;
}
这段程序的最终结果是a=12,b=11,r1=100,r2=100,这所以a=12,是因为在替换的时候,a++被执行了两次。要避免这种行为,就要使宏参数不发生变化,如a++;r1=sqrt(a)
使用#define定义字面值和伪函数的更多相关文章
- const define 定义常量的区别
1.用const定义常量在编译的时候,提供了类型安全检查,而define 只是简单地进行字符串的替换 2.const定义的常量,会分配相应的内存空间.而define没有分配空间,只是在程序中与处理的时 ...
- const变量与define定义常量的区别
一.概念性区别 const 变量就是在普通变量前边加上一个关键字const,它赋值的唯一机会就是“定义时”,此变量不能被程序修改,存储在rodata区. define定义的是常量,不是变量,所以编译器 ...
- C/C++中define定义的常量与const常量
常量是在程序中不能更改的量,在C/C++中有两种方式定义常量,一种是利用define宏定义的方式,一种是C++中新提出来的const型常变量,下面主要讨论它们之间的相关问题: define定义的常量: ...
- UIKIT_EXTERN和define定义常量
看过我其他的博客的人都知道,我喜欢用define定义常量,最近看了一个开源的轮子,使用UIKIT_EXTERN这个定义的常量,了解了一下,发现使用宏定义的常量会在内存中临时开辟一份内存空间,而使用UI ...
- PHP中const和define()定义常量的细节区别
转自:http://www.365mini.com/page/difference-of-define-and-const.htm 众所周知,在PHP中(php 4及以后),我们可以使用函数defin ...
- 【转】#define 定义别名和 typedef 声明类型的区别
下面一段程序的执行结果是: #include <stdio.h>#define CHAR2 char*int main(){ typedef char* CHAR; CHAR ...
- 结构体中使用#define定义宏
struct hostent { char *h_name; /* official name of host */ char **h_aliases; /* ...
- 高端技巧:如何使用#define定义变量
Introduction 想在源文件中定义一个跟行号有关的变量,每次都手动输入实在是太慢了,本文介绍如何使用宏定义来定义与行号有关的变量. 例如:我们想在源代码的第10行定义A_10这样的一个整形变量 ...
- php----------const 定义的常量和define()定义的常量的区别?
用法一:const用于类成员变量,一经定义不可修改,define用于全局常量,不可用于类成员变量的定义,const可在类中使用也可以在类外面使用,define不能. 定义:const 常量名=值; 没 ...
随机推荐
- position:relative可以默认提升元素的z-index;
position:relative可以默认提升元素的z-index; 相对没有添加position的元素来说:
- Android常用控件之GridView使用BaseAdapter
我们可以为GridView添加自定义的Adapter,首先看下用自定义Adapter的显示效果 在布局文件main.xml文件中定义一个GridView控件 <RelativeLayout xm ...
- Schema-based AOP support
本文参考至:spring-framework-reference.pdf的7.3 章节 [Schema-based AOP support] If you are unable to use Java ...
- iOS8的新特性
iOS8的几个重要变化: 家庭分享.用户可以创建家庭分享,除创建者之外最多可以加入6个家庭成员.通过该功能,用户可以和家人分享位置.照片.日历.应用程序.音乐和视频等. 键盘.苹果在iOS8之后开放了 ...
- ORA-04031: 无法分配 共享内存
今天现场项目oracle系统定时器插入数据报错: --ORA-04031: 无法分配 3936 字节的共享内存 ("shared pool","truncate tabl ...
- xcode7 没有Empty Application
如果你想创建xcode6.01之前版本提供的空工程,其实很简单.1:选择模板 Single View Application2:选中 Main.storyboard,将其删除3:选择项目的 plist ...
- C/C++中的far和near两个指针
Dos 的设计是基于16位的CPU的,也就是CPU中的每个寄存器(Register)只有16位,只能存放0-65535(64K)的值.为了能访问大于64K的内存,人们用了分段的方法,用两个16位的数来 ...
- JavaWeb核心编程之Tomcat安装和配置
什么是JavaWeb 在Sun的Java Servlet规范中, 对Java Web应用做了这样的定义: "Java Web应用由一组Servlet, HTML页面, 类, 以及其他可以被绑 ...
- 【phpcms-v9】如何实现在含有子栏目的栏目下添加内容?
对于题目的解释: 假设现在有一个一级栏目 为:栏目1 其下有二级栏目 :栏目1=>栏目11,栏目1=>栏目12,栏目1=>栏目13 同时栏目1下有文章列表 : 栏目1-----文章 ...
- 转 git操作小结
UNDER MIT LICENSE. 公司几乎所有的项目都是使用 git 仓库来管理代码,以前对 git 只有些肤浅的了解,每次提交代码或者上线的时候总是会提心吊胆,生怕出现一些未知的问题.经过三个月 ...