有人能指出使用#define
与常量相比的优缺点吗?我的大部分工作都是用C和Objective-C完成的。
答案 0 :(得分:25)
As 0A0D mentioned,有#defines
,enums
和const
个变量。值得注意的是const
- 限定变量在C中不被认为是编译时常量,因此在某些情况下不能使用(例如,在声明数组的大小时)。
enum
常量是编译时常量。对于整数值,IMO通常比enums
更喜欢const
而不是#define
变量。
答案 1 :(得分:18)
实际上有三种方法可以定义这些常量,
在C中,除非另有说明,否则一切都是int。当我有许多相关的整数常量时,我更喜欢枚举。当您不关心值是什么时,枚举显然更可取。但即使你确实需要为所有常量指定值,我也喜欢枚举的心理分组。当您拥有类型时,代码文档本身会更好,例如
Error MyFunc();
清楚地返回一组特定的错误代码,而
int MyFunc()
可能会返回Unix errno的#define'd列表中的一个,或者可能返回其他内容,或者可能返回一些特殊值 - 谁知道?如果您有多组返回代码,该功能使用哪个集合?
更具体的枚举类型名称有助于编辑器中的标签设置,greps,调试等。
严格的lint可能会给你一些关于使用枚举作为整数的警告,例如,如果你添加或者它们,或者将枚举传递给int。
const对象不同于枚举或#define,特别是在C中。在ANSI C中,const int占用空间就像常规int一样;大多数编译器也会生成指向此地址的指针,而不是内联值。因此,我很少在C中使用const int。(C ++的语义略有不同,因此选择不同。)
我曾经使用的每个编译器都可以选择在尽可能小的空间中存储枚举。通常它甚至是默认选项。要在使用这样的选项时强制使用更宽的枚举,我通常会抛出一个额外的无符号值:
typedef enum
{
MyEnumA,
MyEnumB,
MyEnumForce16 = 7fff
} MyEnum;
使用枚举常量(enum)比使用传统的符号常量样式#define有许多优点。这些优势包括降低维护要求,提高程序可读性和更好的调试能力。
1)第一个优点是枚举常量由编译器自动生成。相反,符号常量必须由程序员手动赋值。
例如,如果您的程序中可能出现错误代码的枚举常量类型,那么您的枚举定义可能如下所示:
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)使用枚举常量的第三个好处是某些符号调试器可以打印枚举常量的值。相反,大多数符号调试器无法打印符号常量的值。这对调试程序来说是一个巨大的帮助,因为如果你的程序在使用枚举的行停止,你可以简单地检查该常量并立即知道它的值。另一方面,由于大多数调试器无法打印#define
值,因此您很可能必须通过在头文件中手动查找来搜索该值。
#define
语句是预编译器指令。从技术上讲,任何以#开头的行都是预编译器可以执行的操作。预编译器将使用其定义替换定义的标记的所有实例。所以这样做:
#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: }
答案 2 :(得分:17)
常量允许您指定数据类型,这通常是一种优势。宏更灵活,因此如果你不小心,可能会让你遇到更多麻烦。
最佳做法是尽可能使用常量,并且只在真正需要宏时使用#define,而不仅仅是命名的文字值。
答案 3 :(得分:4)
常量具有键入的优点,因此可以在编译时发现错误地使用它们。它可能对您无关紧要,但常量占用内存空间而#defines则不占用(因为它们在实际编译发生之前被替换)。
答案 4 :(得分:3)
常量遵循类型安全措施,#define直接替换。同样,GMan说,#define不尊重范围。
答案 5 :(得分:1)
#define 的说明:#define是立即值或宏。
常量的解释:常量是任何永远不会改变的类型的值。
虽然#define可能是指针,但您可以将指针指向const,但不能指向#define。 例如:#define ADDRESS((int *)0x0012)
那么为什么我们应该使用常数如下:
简而言之,const标识符就像它们是语言的一部分一样,因为它们是语言的一部分。
在模块中,如果没有向常量声明指针,C编译器可以优化const,就像它是#define一样。 在CPU术语中,const将成为“立即”值。 其他替代方案是const变量可以 放置在代码区域而不是数据区域,因为它不会改变。 在某些机器上,如果你试图通过指针修改常量,则将一个ponter声明为常量可能会导致异常。
有些情况需要#define,但是一般情况下你应该选择它。您应根据业务价值评估是否使用const或#define:时间,金钱,风险。
答案 6 :(得分:0)
Const是一个可以获取其地址的对象。 它也是类型安全的,即编译器知道常量类型是什么。 以上不适用于#define。
答案 7 :(得分:0)
const
产生左值,意味着可以获取其地址。 #define
没有。#define
会导致无意的宏扩展,这可能是PITA的调试。#define
没有与之关联的类型。一般情况下,我会避免使用瘟疫这样的预处理器来处理任何我不必使用它的事情,主要是因为无意中扩展的可能性以及因为减轻这种情况的ALL_CAPS惯例令人难以置信的丑陋。
答案 8 :(得分:0)
1)#define可以被认为是独立于数据类型的可调参数,而常量允许我们提到数据类型。
2)#define's替换主程序中所引用的任何代码。除此之外,我们甚至可以执行宏功能执行特定任务,可以通过单独传递参数来调用。在常数的情况下,这些显然是不可能的。
因此,它们是根据相关性使用的。
答案 9 :(得分:0)
使用define的好处是,一旦为exp定义变量:#define NUMBER 30,main中的所有代码都将使用值为30的代码。如果将30更改为40,它将直接更改main中的所有值使用此变量(NUMBER)。