C中的符号常量

时间:2011-09-02 15:27:39

标签: c

考虑使用宏,枚举常量和const对象的符号常量。 在宏范围内是全局的,不能局限于局部范围,这是一个主要的缺点。 枚举常量不能在整数以外的情况下使用,枚举常量不能用float或long表示。 Const对象可以具有局部作用域,可以用不同的数据类型表示。 但是在c中声明“int const a”或“const int a”使得值保持不变并且int缓冲区[a]在c中是不允许的。但是在c ++ int中允许缓冲区[a],因为它需要“const a” “仅作为编译器常量。

尽管提到了缺点,但大多数人都喜欢将符号常量定义为枚举常量而不是const对象。

我无法理解下面的语句告诉const对象会导致性能损失。它是如何导致的。请帮助我理解..

  

const对象的问题在于它们可能会产生性能   惩罚,枚举常数避免。

3 个答案:

答案 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)

您看到一些旧信息在过去的某个时刻是正确的,但不再完全准确。不幸的是,定义常量的以下简单方法之间存在许多差异:

  1. static const int CONSTANT = 10;

    缺点:它不能用于“常量表达式”,因此您无法将其用于具有静态存储持续时间的数组的数组大小或switch语句中的情况。

  2. enum { CONSTANT = 10 };

    缺点:它不能具有int以外的类型。例如,1U << 31不能严格移植到具有32位int的系统,1ULL << 48可能会在许多系统上中断。没有地址。

  3. #define CONSTANT 10

    缺点:它与调试器交互不良,并且干扰了正常的C作用域规则。没有地址。

  4. 在现代编译器中,所有这三个应该具有完全相同的运行时性能属性。所以,不要担心性能。

    注意:这不适用于以下情况:

    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的发射机器代码,但是不能保证。枚举立即值,因此可以保证它们可以像这样使用。