【问题标题】:Symbolic constants in CC中的符号常量
【发布时间】:2011-09-02 15:27:38
【问题描述】:

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

尽管提到了这些缺点,但它们中的大多数通常更喜欢将符号常量定义为枚举常量而不是 const 对象。

我无法理解下面的说法,即 const 对象会导致性能下降。它是如何导致的。请帮助我理解..

const 对象的问题是它们可能会导致性能 枚举常量避免的惩罚。

【问题讨论】:

标签: c


【解决方案1】:

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 相比可能有性能损失,但实际上它只会限制您可以使用它的上下文。

【讨论】:

  • “用 const 声明的对象不是常量(更准确地说,它的名称不是常量表达式)”我不完全理解这一点。您是说引用 answer 可以更改为其他名称,但在此 42 中引用的值是只读的?
  • @Ungeheuer:不,answer 是一个对象的名称(不是 C++ 术语意义上的“引用”)。它不能被更改为引用其他对象——即使没有const 也是如此。但该名称不能用于需要常量表达式的上下文中。
【解决方案2】:

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

  1. static const int CONSTANT = 10;

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

  2. enum { CONSTANT = 10 };

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

  3. #define CONSTANT 10

    缺点:它与调试器的交互很差,并且会干扰正常的 C 范围规则。没有地址。

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

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

const int CONSTANT = 10;

无论你是否使用它,它都可能会在内存中获得一个地址,除非你有一个非常花哨的链接器,带有链接时优化(大多数人没有)。

【讨论】:

    【解决方案3】:

    我想我理解你引用的那句话。考虑一下:枚举常量是生成的机器代码中的立即值,并且这些值通常比必须从内存中实际读取的值更快地读取。

    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 的发出机器代码,但不能保证这一点。枚举直接值,因此可以保证这样使用。

    【讨论】:

    • 对此不确定。这听起来很合理,但如果编译器优化对 const 的引用(据我所知确实发生了),这不是问题/否定。
    • 枚举肯定是直接值,它们只是文字的符号名称。 const 对象实际上确实占用了内存,并且不确定编译器是否会推断出它的值可以用作立即值。
    • @Rudy:现在很难想象编译器不进行持续传播。
    • 我不太确定所有编译器都这样做。 Windows、Linux 和 Mac 的大版本可能会这样做,但在引用的文章(嵌入式处理器)的上下文中,我不确定所有编译器都会这样做。请注意,例如在 C 中,const int a = 7; 不能是常量表达式的一部分,例如 int c[a];
    猜你喜欢
    • 1970-01-01
    • 2011-06-11
    • 1970-01-01
    • 1970-01-01
    • 2013-10-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-09-19
    相关资源
    最近更新 更多