转自:http://faq.csdn.net/read/178731.html
问题内容:关于const和#define的区别?
- 原讨论链接:http://community.csdn.net/expert/topicview1.asp?id=1993402
- 所属论坛:C语言 审核组:C/C++
- 提问者:wanghuan1983 解决者:zhouyong0371
- 感谢:steedhorse、zhouyong0371、BuleFreezing、seu2002、flyfreely、Meyer、fierygnu、web_spider、mechgoukiteng
- 关键字:
- 答案:
为什么在C中经常用的是#DEFINE而不是CONST?而在C++中用CONST而不用#define?
-----------------------------------小π的分隔符---------------------------------------
为什么人们习惯在C中声明常量的时候很少用const呢?
---------------------------------------------------------------
C里也可以用const,可能是习惯问题。
C++里倡导使用const,因为const是类型安全的,宏定义没有类型。
---------------------------------------------------------------
尽量用const和inline而不用#define
这个条款最好称为:“尽量用编译器而不用预处理”,因为#define经常被认为好象不是语言本身的一部分。这是问题之一。再看下面的语句:
#define ASPECT_RATIO 1.653
编译器会永远也看不到ASPECT_RATIO这个符号名,因为在源码进入编译器之前,它会被预处理程序去掉,于是ASPECT_RATIO不会加入到符号列表中。如果涉及到这个常量的代码在编译时报错,就会很令人费解,因为报错信息指的是1.653,而不是ASPECT_RATIO。如果ASPECT_RATIO不是在你自己写的头文件中定义的,你就会奇怪1.653是从哪里来的,甚至会花时间跟踪下去。这个问题也会出现在符号调试器中,因为同样地,你所写的符号名不会出现在符号列表中。
解决这个问题的方案很简单:不用预处理宏,定义一个常量:
const double ASPECT_RATIO = 1.653;
这种方法很有效。但有两个特殊情况要注意。
首先,定义指针常量时会有点不同。因为常量定义一般是放在头文件中(许多源文件会包含它),除了指针所指的类型要定义成const外,重要的是指针也经常要定义成const。例如,要在头文件中定义一个基于char*的字符串常量,你要写两次const:
const char * const authorName = "Scott Meyers";
关于const的含义和用法,特别是和指针相关联的问题,参见条款21。
另外,定义某个类(class)的常量一般也很方便,只有一点点不同。要把常量限制在类中,首先要使它成为类的成员;为了保证常量最多只有一份拷贝,还要把它定义为静态成员:
class GamePlayer {
private:
static const int NUM_TURNS = 5; // constant eclaration
int scores[NUM_TURNS]; // use of constant
...
};
还有一点,正如你看到的,上面的语句是NUM_TURNS的声明,而不是定义,所以你还必须在类的实现代码文件中定义类的静态成员:
const int GamePlayer::NUM_TURNS; // mandatory definition;
// goes in class impl.file
你不必过于担心这种小事。如果你忘了定义,链接器会提醒你。
旧一点的编译器会不接受这种语法,因为它认为类的静态成员在声明时定义初始值是非法的;而且,类内只允许初始化整数类型(如:int, bool, char 等),还只能是常量。
在上面的语法不能使用的情况下,可以在定义时赋初值:
class EngineeringConstants { // this goes in the class
private: // header file
static const double FUDGE_FACTOR;
...
};
// this goes in the class implementation file
const double EngineeringConstants::FUDGE_FACTOR = 1.35;
大多数情况下你只要做这么多。唯一例外的是当你的类在编译时需要用到这个类的常量的情况,例如上面GamePlayer::scores数组的声明(编译过程中编译器一定要知道数组的大小)。所以,为了弥补那些(不正确地)禁止类内进行整型类常量初始化的编译器的不足,可以采用称之为“借用enum”的方法来解决。这种技术很好地利用了当需要int类型时可以使用枚举类型的原则,所以GamePlayer也可以象这样来定义:
class GamePlayer {
private:
enum { NUM_TURNS = 5 } // "the enum hack" — makes
// NUM_TURNS a symbolic name
// for 5
int scores[NUM_TURNS];// fine
};
除非你正在用老的编译器(即写于1995年之前),你不必借用enum。当然,知道有这种方法还是值得的,因为这种可以追溯到很久以前的时代的代码可是不常见的哟。
回到预处理的话题上来。另一个普遍的#define指令的用法是用它来实现那些看起来象函数而又不会导致函数调用的宏。典型的例子是计算两个对象的最大值:
#define max(a,b) ((a) > (b) ? (a) : (b))
这个语句有很多缺陷,光想想都让人头疼,甚至比在高峰时间到高速公路去开车还让人痛苦。
无论什么时候你写了象这样的宏,你必须记住在写宏体时对每个参数都要加上括号;否则,别人调用你的宏时如果用了表达式就会造成很大的麻烦。但是即使你象这样做了,还会有象下面这样奇怪的事发生:
int a = 5, b = 0;
max(++a, b);// a 的值增加了2次
max(++a, b+10); // a 的值只增加了1次
这种情况下,max内部发生些什么取决于它比较的是什么值!
幸运的是你不必再忍受这样愚笨的语句了。你可以用普通函数实现宏的效率,再加上可预计的行为和类型安全,这就是内联函数(见条款33):
inline int max(int a, int b) { return a > b ? a : b; }
不过这和上面的宏不大一样,因为这个版本的max只能处理int类型。但模板可以很轻巧地解决这个问题:
template<class T>
inline const T& max(const T& a, const T& b)
{ return a > b ? a : b; }
这个模板产生了一整套函数,每个函数拿两个可以转换成同种类型的对象进行比较然后返回较大的(常量)对象的引用。因为不知道T的类型,返回时传递引用可以提高效率(见条款22)。
顺便说一句,在你打算用模板写象max这样有用的通用函数时,先检查一下标准库(见条款49),看看他们是不是已经存在。比如说上面说的max,你会惊喜地发现你可以后人乘凉:max是C++标准库的一部分。
有了const和inline,你对预处理的需要减少了,但也不能完全没有它。抛弃#include的日子还很远,#ifdef/#ifndef在控制编译的过程中还扮演重要角色。预处理还不能退休,但你一定要计划给它经常放长假。
---------------------------------------------------------------
楼上的真强!如果是c的化,我也有个建议:实用define命名简单内建类型的常量,const定义要依赖其他常量的对象;如:
#define rate .125
const double mrate=rate/12
另外,iso c中有const关键词
---------------------------------------------------------------
const较安全
#define一般在等级考试中较多
我是学C++的,一般很少用的
呵呵!
---------------------------------------------------------------
C语言第二版 不支持 const 关键字,99版才支持。
gcc 早就支持 const。
为什么不用const 代替 #define
因为很多c语言编译器不支持 const和inline,如果想做可以移植的程序
就只能用 #define
---------------------------------------------------------------
虽然 C的const是受C++的影响加入的
但早在 Ansi C 就加入了const
不过C的Const没有C++中那么强的支持所以少用
---------------------------------------------------------------
C里用#define多而不是const是历史原因(编译器支持、库支持等)。现在有了const,一般地应该使用const而不是#define。另一个可以选择的是enum。
另外const是修饰符,不只支持常量声明,还可以修饰类型、函数等,用处多多。
当然某些宏定义(只要是有些带参数的宏)是没有办法用const来取代typedef和#define的用法与区别,以及consttypedef和#define的用法与区别
一、typedef的用法
在C/C++语言中,typedef常用来定义一个标识符及关键字的别名,它是语言编译过程的一部分,但它并不实际分配内存空间,实例像:
typedef int INT;
typedef int ARRAY[10];
typedef (int*) pINT;typedef可以增强程序的可读性,以及标识符的灵活性,但它也有“非直观性”等缺点。
二、#define的用法
#define为一宏定义语句,通常用它来定义常量(包括无参量与带参量),以及用来实现那些“表面似和善、背后一长串”的宏,它本身并不在编译过程中进行,而是在这之前(预处理过程)就已经完成了,但也因此难以发现潜在的错误及其它代码维护问题,它的实例像:
#define INT int
#define TRUE 1
#define Add(a,b) ((a)+(b));
#define Loop_10 for (int i=0; i<10; i++)在Scott Meyer的Effective C++一书的条款1中有关于#define语句弊端的分析,以及好的替代方法,大家可参看。
三、typedef与#define的区别
从以上的概念便也能基本清楚,typedef只是为了增加可读性而为标识符另起的新名称(仅仅只是个别名),而#define原本在C中是为了定义常量,到了C++,const、enum、inline的出现使它也渐渐成为了起别名的工具。有时很容易搞不清楚与typedef两者到底该用哪个好,如#define INT int这样的语句,用typedef一样可以完成,用哪个好呢?我主张用typedef,因为在早期的许多C编译器中这条语句是非法的,只是现今的编译器又做了扩充。为了尽可能地兼容,一般都遵循#define定义“可读”的常量以及一些宏语句的任务,而typedef则常用来定义关键字、冗长的类型的别名。
宏定义只是简单的字符串代换(原地扩展),而typedef则不是原地扩展,它的新名字具有一定的封装性,以致于新命名的标识符具有更易定义变量的功能。请看上面第一大点代码的第三行:
typedef (int*) pINT;
以及下面这行:
#define pINT2 int*效果相同?实则不同!实践中见差别:pINT a,b;的效果同int *a; int *b;表示定义了两个整型指针变量。而pINT2 a,b;的效果同int *a, b;
表示定义了一个整型指针变量a和整型变量b。
注意:两者还有一个行尾;号的区别哦!
我查到一个讲const与#define的差别的帖子,里面谈到const与#define最大的差别在于:前者在堆栈分配了空间,而后者只是把具体数值直接传递到目标变量罢了。或者说,const的常量是一个Run-Time的概念,他在程序中确确实实的存在并可以被调用、传递。而#define常量则是一个Compile-Time概念,它的生命周期止于编译期:在实际程序中他只是一个常数、一个命令中的参数,没有实际的存在。
const常量存在于程序的数据段,#define常量存在于程序的代码段。
至于两者的优缺点,要看具体的情况了。一般的常数应用,笔者个人认为#define是一个更好的选择:
i.从run-time的角度来看,他在空间上和时间上都有很好优势。
ii.从compile-time的角度来看,类似m=t*10的代码不会被编译器优化,t*10的操作需要在run-time执行。而#define的常量会被合并(在上例中T*10将被0x82取代)。
但是:如果你需要粗鲁的修改常数的值,那就得使用const了,因为后者在程序中没有实际的存在。(其实应该说修改数据段比代码段要简单^_^)。