考虑使用宏,枚举常量和const对象的符号常量。 在宏范围内是全局的,不能局限于局部范围,这是一个主要的缺点。 枚举常量不能在整数以外的情况下使用,枚举常量不能用float或long表示。 Const对象可以具有局部作用域,可以用不同的数据类型表示。 但是在c中声明“int const a”或“const int a”使得值保持不变并且int缓冲区[a]在c中是不允许的。但是在c ++ int中允许缓冲区[a],因为它需要“const a” “仅作为编译器常量。
尽管提到了缺点,但大多数人都喜欢将符号常量定义为枚举常量而不是const对象。
我无法理解下面的语句告诉const对象会导致性能损失。它是如何导致的。请帮助我理解..
const对象的问题在于它们可能会产生性能 惩罚,枚举常数避免。
答案 0 :(得分:5)
使用const
声明的对象不是常量(更准确地说,它的名称不是常量表达式)。 const
关键字并不代表“常量”,它意味着“只读”。所以给出:
const int answer = 42;
printf("The answer is %d\n", answer);
原则上,printf
调用的评估需要从存储中获取answer
的值,然后再将其传递给printf
函数。
但实际上,任何值得为它支付费用的编译器(即使它是免费的)都会优化对answer
的引用,以便printf调用产生与
printf("The answer is %d\n", 42);
(gcc使用-O1
或更好的方式执行此操作。如果未指定-O...
,则代码实际上会获取对象的值 - 但如果您不请求优化,你告诉编译器你不关心性能。)
(一个非常聪明的编译器可以生成相当于
的代码puts("The answer is 42");
)
真正的区别在于名称answer
不能在需要常量表达的上下文中使用。例如,case answer: ...
在C ++中是合法的,但在C中是非法的。
请注意,int arr[answer];
实际上是合法的,至少在C99中,它允许可变长度数组。如果你写了
const int answer = rand() % 100 + 1;
但是,VLA只能具有自动存储持续时间,因此无法在文件范围或使用static
关键字声明它们。
关于enum
技巧:
enum { answer = 42; }
确实使answer
成为常量表达式,但它仅限于int
类型的值(C枚举常量始终为int
类型)。有些人可能会认为这是滥用enum
功能。是的,但我不会让我感到烦恼。
因此可能会导致const int answer = 42;
而不是#define answer 42
的性能损失,但实际上它只会限制您可以使用它的上下文。< / p>
答案 1 :(得分:4)
您看到一些旧信息在过去的某个时刻是正确的,但不再完全准确。不幸的是,定义常量的以下简单方法之间存在许多差异:
static const int CONSTANT = 10;
缺点:它不能用于“常量表达式”,因此您无法将其用于具有静态存储持续时间的数组的数组大小或switch语句中的情况。
enum { CONSTANT = 10 };
缺点:它不能具有int
以外的类型。例如,1U << 31
不能严格移植到具有32位int的系统,1ULL << 48
可能会在许多系统上中断。没有地址。
#define CONSTANT 10
缺点:它与调试器交互不良,并且干扰了正常的C作用域规则。没有地址。
在现代编译器中,所有这三个应该具有完全相同的运行时性能属性。所以,不要担心性能。
注意:这不适用于以下情况:
const int CONSTANT = 10;
这可能会在内存中获得一个地址,无论你是否使用它,除非你有一个非常花哨的链接器时间链接优化(大多数人没有)。
答案 2 :(得分:1)
我想我理解你所引用的那一行。请考虑这一点:枚举常量是生成的机器代码中的立即值,这些值通常比必须实际从内存中读取的值更快读取。
const int a = 7;
实际上是一个存储在某处的int,必须从那里读取。在例如x86机器码,读作:
MOV EAX,[a]
这是一个真正的内存访问。 OTOH,像一个枚举
enum X { a=7, b, c };
被视为立即值
MOV EAX,7
通常更快,也在其他CPU中。
我猜这就是您所引用的行的含义。
当然,一个好的编译器实际上可以推断出const int a = 7;
的值永远不会改变,所以它可以取值一个实际执行MOV EAX,7
的发射机器代码,但是不能保证。枚举是立即值,因此可以保证它们可以像这样使用。