#define与常量的优缺点?
有人可以指出使用#define
与常量的优缺点吗? 我的大部分工作都是在C和Objective-C中完成的。
常量允许你指定一个数据types,这通常是一个优点。 macros是非常灵活的,因此如果你不小心的话会让你陷入更麻烦的地步。
最好的做法是尽量使用常量,只有当你真的需要一个macros时,才使用#define,而不仅仅是一个命名的文字值。
如0A0D所提到的 ,有#defines , enums
和const
variables。 值得注意的是const
限定variables不被认为是C语言中的编译时常量,因此在某些情况下(例如声明数组大小时)不能使用。
enum
常量是编译时常量,但是。 对于整数值,IMO通常比在#define
上的const
variablesenums
更好。
实际上有三种方法来定义这些常数,
-
定义
- 枚举
- constvariables
在C中,除非另有说明,所有东西都是int。 当我有一些相关的整数常量时,我更喜欢枚举。 当你不关心什么是值时,枚举显然是可取的。 但即使你确实需要指定所有常量的值,我也喜欢枚举的精神分组。 代码文件本身更好,当你有types,例如
Error MyFunc();
明确地返回一组特定的错误代码之一,而
int MyFunc()
可能会返回Unix errno的#defined'd列表中的一个,或者其他的东西,或者那些加上一些特殊的值 – 谁知道? 如果你有多组返回码,这个function使用哪一组?
更具体的枚举types名称可以帮助编辑器中的标签工具,greps,debugging等等。
一个严格的棉绒可能会给你一些关于使用枚举作为整数的警告,例如,如果你添加或或他们,或将枚举传递给一个int。
const对象不同于enum或#define,特别是C中。在ANSI C中,const int占用的空间就像普通的int; 大多数编译器也会生成这个地址的指针引用,而不是内联值。 因此,我很less在C中使用const int(C ++有稍微不同的语义,所以select不同)。
我曾经使用过的每个编译器都可以select将枚举存储在尽可能小的空间中。 通常甚至是默认选项。 在使用这样一个选项时,为了强制更大的枚举,我通常会抛出额外的无符号值:
typedef enum { MyEnumA, MyEnumB, MyEnumForce16 = 7fff } MyEnum;
使用枚举常量(枚举)比使用#define的传统符号常量样式有许多优点。 这些优点包括较低的维护要求,改进的程序可读性和较好的debugging能力。
1)第一个优点是枚举常量由编译器自动生成。 相反,符号常量必须由程序员手动赋值。
例如,如果您的程序中可能出现错误代码的枚举常量types,则您的枚举定义可能如下所示:
enum Error_Code { OUT_OF_MEMORY, INSUFFICIENT_DISK_SPACE, LOGIC_ERROR, FILE_NOT_FOUND };
在前面的例子中,OUT_OF_MEMORY被编译器自动赋值为0(零),因为它首先出现在定义中。 然后编译器继续为枚举常量自动赋值,使INSUFFICIENT_DISK_SPACE等于1,LOGIC_ERROR等于2,FILE_NOT_FOUND等于3,依此类推。 如果使用符号常量来处理相同的示例,则代码如下所示:
#define OUT_OF_MEMORY 0 #define INSUFFICIENT_DISK_SPACE 1 #define LOGIC_ERROR 2 #define FILE_NOT_FOUND 3
两种方法中的每一种都得到相同的结果:四个常量分配数值来表示错误代码。 考虑需要维护,但是,如果要添加两个常量来表示错误代码DRIVE_NOT_READY
和CORRUPT_FILE
。 使用枚举常量方法,您只需将这两个常量放在枚举定义的任何位置。 编译器会为这些常量生成两个唯一的值。 使用符号常量方法,您将不得不手动分配两个新的数字到这些常量。 此外,你会想确保你分配给这些常量的数字是唯一的。
2)使用枚举常量方法的另一个好处是你的程序更具可读性,因此其他人可能会更好地理解,以后可能需要更新你的程序。
3)使用枚举常量的第三个优点是一些符号debugging器可以打印一个枚举常量的值。 相反,大多数符号debugging器不能打印符号常量的值。 这对debugging程序可能是一个巨大的帮助,因为如果你的程序停在一个使用枚举的行上,你可以简单地检查这个常量,并立即知道它的值。 另一方面,因为大多数debugging器不能打印#define
值,所以你很可能必须通过在头文件中手动查找来search该值。
#define
语句是一个预编译器指令。 从技术上讲,任何以#开头的行都是预编译器要执行的行。 预编译器将用定义replace已定义标记的所有实例。 所以这样做:
#define DELAY 40 for (i=0;i<DELAY;i++) { for (j=0;j<DELAY;j++) { asm NOP; } }
和这个完全一样(就编译器而言):
for (i=0;i<40;i++) { for (j=0;j<40;j++) { asm NOP; } }
编译器生成机器码时,会看到数字40,并使用立即寻址模式,以便与累加器进行比较。 数字40将被存储在代码中,正如你所引用的那样。 在这种情况下,它是两次。 这是CodeWarrior Ver5生成的程序集:
7: char i,j; 8: for (i=0;i<DELAY;i++) { 0002 95 [2] TSX 0003 7f [2] CLR ,X 0004 [5] L4: 9: for (j=0;j<DELAY;j++) { 0004 6f01 [3] CLR 1,X 0006 [5] L6: 10: asm NOP; 0006 9d [1] NOP 0007 6c01 [4] INC 1,X 0009 e601 [3] LDA 1,X 000b a128 [2] CMP #40 ;<---- notice opcode a1 and immediate constant 40, which is $28 in hexadecimal 000d 25f7 [3] BCS L6 000f 7c [3] INC ,X 0010 f6 [2] LDA ,X 0011 a128 [2] CMP #40 ;<---- and here it is again. 0013 25ef [3] BCS L4 11: } 12: } 13: }
常量具有键入的优点,因此可以在编译时使用它们。 对你来说可能没有关系,但是常量会占用内存中的空间,而#define不会(因为在实际编译之前被replace)。
常量遵循types安全措施,直接replace#defines。 也正如GMan所说,#define不尊重范围。
#define的解释 :#define是一个立即值或一个macros。
常数解释 :常量是任何types的值永远不能改变。
#define可以是一个指针,例如:#define ADDRESS((int *)0x0012)可以删除一个指向const的指针,但不是#define,
那么为什么我们应该使用常量如下:
- 他们服从语言的范围规则
- 你可以在debugging器中看到它们
- 如果你需要,你可以把他们的地址
- 你可以通过const引用来传递它们,如果你需要的话
- 他们不会在您的程序中创build新的“关键字”。
简而言之,const标识符就像是语言的一部分,因为它们是语言的一部分。
在模块中,如果没有指向常量的指针,C编译器可以优化一个常量,就好像它是一个#define一样。 在CPU方面,const将成为“立即”值。 其他的select是constvariables可以放在代码区域,而不是数据区域,因为它不会改变。 在某些机器上,如果试图通过指针修改常量,则将ponter声明为常量可能会导致exception。
有些情况下需要定义#define,但是当你有select时通常应该避免它。 您应该根据业务价值来评估是否使用const或#define:时间,金钱,风险。
例如,Const是一个可以取其地址的对象。 它也是types安全的,即编译器知道常量的types是什么。 以上不适用于#define。
-
const
产生一个左值,这意味着它的地址可以被采用。#define
不。 -
#define
可能会导致无意的macros扩展,这可能是一个PITA来debugging。 - 正如其他人所提到的,#
#define
没有与之关联的types。
一般来说,我会避免像瘟疫这样的预处理器,因为我不需要使用它,主要是因为可能会无意中扩展,因为减轻这个问题的ALL_CAPS惯例是难以置信的丑陋。
1)#define可以被认为是独立于数据types的可调参数,而常数允许我们提及数据types。
2)#define取代了主程序中所有被引用的代码。 除此之外,我们甚至可以通过macrosfunction来执行特定的任务,这可以通过单独传递参数来调用。 在常数情况下,这显然是不可能的。
所以,他们是基于相关性使用的。
使用define的好处是一旦你定义了exp的variables:#define NUMBER 30,main中的所有代码都会使用那个代码的值为30.如果你把30改成40,它将直接改变main中的所有值,variables(NUMBER)。