#define与常量的优缺点?

有人可以指出使用#define与常量的优缺点吗? 我的大部分工作都是在C和Objective-C中完成的。

常量允许你指定一个数据types,这通常是一个优点。 macros是非常灵活的,因此如果你不小心的话会让你陷入更麻烦的地步。

最好的做法是尽量使用常量,只有当你真的需要一个macros时,才使用#define,而不仅仅是一个命名的文字值。

如0A0D所提到的 ,有#defines , enumsconstvariables。 值得注意的是const限定variables不被认为是C语言中的编译时常量,因此在某些情况下(例如声明数组大小时)不能使用。

enum常量是编译时常量,但是。 对于整数值,IMO通常比在#define上的constvariablesenums更好。

实际上有三种方法来定义这些常数,

  • 定义

  • 枚举
  • 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_READYCORRUPT_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。

  1. const产生一个左值,这意味着它的地址可以被采用。 #define不。
  2. #define可能会导致无意的macros扩展,这可能是一个PITA来debugging。
  3. 正如其他人所提到的,# #define没有与之关联的types。

一般来说,我会避免像瘟疫这样的预处理器,因为我不需要使用它,主要是因为可能会无意中扩展,因为减轻这个问题的ALL_CAPS惯例是难以置信的丑陋。

1)#define可以被认为是独立于数据types的可调参数,而常数允许我们提及数据types。

2)#define取代了主程序中所有被引用的代码。 除此之外,我们甚至可以通过macrosfunction来执行特定的任务,这可以通过单独传递参数来调用。 在常数情况下,这显然是不可能的。

所以,他们是基于相关性使用的。

使用define的好处是一旦你定义了exp的variables:#define NUMBER 30,main中的所有代码都会使用那个代码的值为30.如果你把30改成40,它将直接改变main中的所有值,variables(NUMBER)。