const的正确性是否给编译器更多的空间来优化?

我知道它提高了可读性,使得程序不易出错,但是提高性能有多less?

另外,注意,引用和const指针之间的主要区别是什么? 我会假设他们以不同的方式存储在内存中,但是如何呢?

谢谢。

[编辑:好,所以这个问题比我想象的更微妙。]

声明const的指针或const的引用永远不会帮助任何编译器优化任何东西。 (虽然看到这个答案底部的更新。)

const声明只是表示如何在声明的范围内使用标识符; 它并不是说底层对象不能改变。

例:

 int foo(const int *p) { int x = *p; bar(x); x = *p; return x; } 

编译器不能假定*p没有被调用bar() ,因为p可能是(例如)指向全局int的指针, bar()可能会修改它。

如果编译器足够了解foo()的调用者和foo()的内容,它可以certificatebar()不会修改*p ,那么它也可以在没有const声明的情况下执行该certificate

但总的来说这是事实。 因为const只在声明的范围内有效果,所以编译器已经可以看到你是如何处理该范围内的指针或引用的; 它已经知道你没有修改底层对象。

所以总而言之,在这个背景下,所有的const都会阻止你犯错误。 它不会告诉编译器它不知道的任何东西,因此与优化无关。

怎么样的函数调用foo() ? 喜欢:

 int x = 37; foo(&x); printf("%d\n", x); 

编译器是否可以certificate这是打印37,因为foo()需要一个const int *

不。尽pipefoo()需要一个指向const的指针,它可能会抛出这个常量并修改int。 (这不是未定义的行为。)这里再一次,编译器一般不能做任何的假设; 如果它足够了解foo()做出这样的优化,那么即使没有const也会知道。

const可能允许优化的唯一时间是这样的情况:

 const int x = 37; foo(&x); printf("%d\n", x); 

在这里,通过任何机制来修改x (例如,通过取一个指向它的指针和抛出const )来调用Undefined Behavior。 所以编译器可以自由地假设你不这样做,并且它可以将常量37传播到printf()中。 这种优化对于你声明const任何对象都是合法的。 (实际上,你永远不会参考的局部variables不会受益,因为编译器已经可以看到你是否在其范围内修改它。)

为了回答你的“旁注”问题,(a)const指针是一个指针; 和(b)一个const指针可以等于NULL。 你是正确的,内部表示(即地址)最有可能是相同的。

[更新]

正如Christoph在评论中指出的那样,我的回答是不完整的,因为它没有提到restrict

C99标准第6.7.3.1(4)条规定:

在B的每个执行过程中,令L是任何具有基于P的L的左值。如果L用于访问它所指定的对象X的值,并且X也被修改(通过任何方式),则以下要求适用T不能是const限定的。 …

(这里B是一个基本块,其中P是一个限制指针对T的范围。)

所以如果一个C函数foo()是这样声明的:

 foo(const int * restrict p) 

…那么编译器可能会假定在*p的生命周期(即执行foo()期间foo()不会对*p任何修改,否则行为将是未定义的。

所以原则上,将const与const指针结合起来可以实现上面所讨论的两个优化。 难道编译器是否真的实现了这样的优化? (GCC 4.5.2,至less不是。)

请注意, restrict只存在于C中,而不是C ++(甚至不包括C ++ 0x),除了编译器特定的扩展。

在我的头顶,我可以想到两种情况,在适当的const限定允许额外的优化(在全程序分析不可用的情况下):

 const int foo = 42; bar(&foo); printf("%i", foo); 

在这里,编译器知道要打印42而不必检查bar() (它可能在curent翻译单元中不可见bar()的主体,因为对foo所有修改都是非法的( 这与Nemo的例子相同 )。

但是,这也可以通过声明bar() as来将foo标记为const

 extern void bar(const int *restrict p); 

在许多情况下,程序员实际上想要restrict const restrict指针,而不是const作为函数参数的const指针,因为只有前者能够保证指向对象的可变性。

至于你的问题的第二部分:出于所有的实际目的,一个C ++引用可以被认为是一个常量指针(不是指向常量值的指针!),具有自动间接 – 它不是“更安全”或“更快”比一个指针,只是更方便。

在C ++中有两个const问题(就优化而言):

  • const_cast
  • mutable

const_cast意味着即使通过const引用或const指针传递一个对象,该函数也可以将该const转移并修改该对象(如果该对象不是以const开头的话,则允许)。

mutable意味着即使一个对象是const ,它的一些部分也可能被修改(caching行为)。 另外,指向(而不是被拥有的)对象可以在const方法中修改,即使它们在逻辑上是对象状态的一部分。 最后,全局variables也可以修改…

const在这里可以帮助开发人员及早发现逻辑错误。

常量正确性通常不利于性能; 大多数编译器甚至不会去追踪超出前端的常量。 将variables标记为const可能会有所帮助,具体情况取决于情况。

引用和指针在内存中的存储方式完全相同。

这真的取决于编译器/平台(它可能有助于优化一些还没有编写的编译器,或者在一些你从不使用的平台上)。 C和C ++标准除了为某些function提供复杂性要求之外,没有提到性能。

对const的指针和引用通常不会帮助优化,因为const限定在某些情况下可以被合法地抛弃,并且有可能通过不同的非const引用来修改对象。 另一方面,将对象声明为const是有帮助的,因为它保证对象不能被修改(即使传递给编译器不知道定义的函数)。 这允许编译器将const对象存储在只读存储器中,或者将其值存储在一个集中的位置,从而减less了复制和检查修改的需要。

指针和引用通常以完全相同的方式实现,但是这又完全取决于平台。 如果你真的有兴趣,那么你应该看看你的平台和编译器生成的机器代码在你的程序(如果你真的使用编译器,生成机器码)。

一件事是,如果你声明一个全局variablesconst,它可能会被放在一个库或可执行文件的只读部分,从而在一个只读mmap的多个进程间共享它。 至less如果你在全局variables中声明了很多数据的话,这在Linux上可能是一个很大的记忆。

另一种情况是,如果你声明一个常量的全局整数,或浮点型或枚举型,编译器可能只是将内联常量,而不是使用variables引用。 我相信虽然我不是编译器专家,但速度有点快。

引用仅仅是实现方式下面的指针。

它可以有助于性能,但只有通过声明直接访问对象。 引用参数等不能被优化,因为可能有其他path不是最初声明为const的对象,编译器通常不能分辨出你所引用的对象是否被实际声明为const,除非这是你正在使用的声明。

如果你使用的是一个const声明,编译器会知道外部编译的函数体等不能修改它,所以你在那里得到了好处。 当然像const int这样的东西是在编译时传播的,所以这是一个巨大的胜利(与int相比)。

引用和指针的存储方式完全相同,它们的行为在语法上是不同的。 引用基本上是renameings,所以相对安全,而指针可以指向许多不同的东西,因此更强大和容易出错。

我猜const指针在结构上与引用是一样的,所以机器代码和效率是一样的。 真正的区别是语法 – 引用是一个更干净,更容易阅读的语法,因为你不需要指针提供的额外的机制,引用将是风格上的首选。