我知道它可以提高可读性并使程序不易出错,但是它可以在多大程度上提高性能呢?
另外,引用和const
指针之间的主要区别是什么?我假设它们以不同的方式存储在内存中,但是怎么办呢?
我知道它可以提高可读性并使程序不易出错,但是它可以在多大程度上提高性能呢?
另外,引用和const
指针之间的主要区别是什么?我假设它们以不同的方式存储在内存中,但是怎么办呢?
Answers:
[编辑:好的,所以这个问题比起初我想的要微妙。]
声明指向const或const引用的指针永远不会帮助任何编译器优化任何内容。(尽管请参见此答案底部的“更新”。)
的const
声明仅指示如何的标识符将内使用范围其声明的; 它并不表示基础对象不能更改。
例:
int foo(const int *p) {
int x = *p;
bar(x);
x = *p;
return x;
}
编译器不能假定对*p
的调用未对其进行修改bar()
,因为它p
可能是(例如)指向全局int的指针,并且bar()
可能会对其进行修改。
如果编译器对调用方有足够的了解,foo()
并且它的内容bar()
可以证明bar()
没有被修改*p
,那么它也可以在没有const声明的情况下执行该证明。
但这通常是正确的。因为const
仅在声明的范围内起作用,所以编译器已经可以看到您如何处理该范围内的指针或引用。它已经知道您没有在修改基础对象。
简而言之,const
在这种情况下,所有要做的就是防止您犯错误。它不会告诉编译器它尚不知道的任何内容,因此与优化无关。
那调用的函数foo()
呢?喜欢:
int x = 37;
foo(&x);
printf("%d\n", x);
编译器可以证明此打印37,因为foo()
需要const int *
?
不。即使foo()
使用指向const的指针,也可能会舍弃const-ness并修改int。(这不是未定义的行为。)再次,编译器通常无法做出任何假设;如果知道足够多的信息foo()
可以进行这样的优化,即使没有,它也会知道const
。
唯一const
可能允许优化的情况是这样的情况:
const int x = 37;
foo(&x);
printf("%d\n", x);
在这里,要x
通过任何机制进行修改(例如,通过获取指向它的指针并丢弃const
),都将调用未定义行为。因此,编译器可以自由地假定您不这样做,并且可以将常量37传播到printf()中。对于您声明的任何对象,这种优化都是合法的const
。(实际上,您永远不会引用的局部变量将不会受益,因为编译器已经可以查看您是否在其范围内对其进行了修改。)
要回答您的“附带说明”问题,(a)const指针是指针;(b)const指针可以等于NULL。您是正确的,内部表示形式(即地址)很可能是相同的。
[更新]
正如克里斯多夫在评论中指出的那样,我的回答是不完整的,因为它没有提及restrict
。
C99标准的6.7.3.1(4)节规定:
在每次执行B的过程中,令L为具有基于P的&L的任何左值。如果使用L来访问它指定的对象X的值,并且还对X进行了修改(通过任何方式),则以下要求适用:T不得为const限定。...
(这里B是一个基本块,作用域T的限制指针P在其上。)
因此,如果这样foo()
声明C函数:
foo(const int * restrict p)
...然后,编译器可能会假设*p
在p
-的执行期间(即在-的执行期间)不会进行任何修改,foo()
因为否则行为将是未定义的。
因此,原则上,restrict
与指向常量的指针结合使用可以实现上述两种优化。我想知道是否有任何编译器实际上实现了这种优化?(至少不是GCC 4.5.2。)
请注意restrict
,除了作为编译器特定的扩展之外,它仅存在于C中,而不存在于C ++中(甚至不存在于C ++ 0x中)。
const
变量const_cast
,而不仅是全局变量const
...
const
有关,但它至少不完全-尼莫忘了restrict
:声明foo()
为int foo(const int *restrict p)
确实会告诉编译器将指向的值p
不会被调用来改变作者foo()
restrict
实际的意思是(1)如果restrict
修改了-qualified指针指向的对象,则所有访问(包括触发该修改的访问)必须基于该访问指针,以及(2)-与此处相关的部分-如果发生任何修改,则该指针一定不能被const
限定;底线:在该指针的有效期内修改由restrict
-qualified-to-指向的对象是非法的const
const
C ++存在两个问题(就优化而言):
const_cast
mutable
const_cast
这意味着即使您通过const引用或const指针传递对象,该函数也可能会舍弃const-ness并修改该对象(如果该对象不是const开头,则允许)。
mutable
表示即使一个对象是const
,它的某些部分也可能被修改(缓存行为)。另外,const
即使在逻辑上是对象状态的一部分,也可以在方法中修改指向(而不是被拥有)的对象。最后,全局变量也可以修改...
const
是为了帮助开发人员尽早发现逻辑错误。
const_cast
,然后修改该对象,则它是未定义的行为。编译器可以放心地假设这种情况永远不会发生。
const
对象是未定义的行为,但这const_cast
是丢弃const
引用或指针的属性来修改非const
对象的原因。因此,编译器可以从中获得一些性能提升const
,而没有这种性能提升的只是一小部分const_cast
。
我const
不禁想到了两种情况,适当的-qualification允许进行其他优化(在无法进行整个程序分析的情况下):
const int foo = 42;
bar(&foo);
printf("%i", foo);
在这里,编译器知道打印42
而不必检查主体bar()
(主体在当前翻译单元中可能不可见),因为对它的所有修改foo
都是非法的(这与Nemo的示例相同)。
但是,这也有可能无标记foo
的const
通过声明bar()
为
extern void bar(const int *restrict p);
在许多情况下,程序员实际上希望将restrict
合格的指向对象的指针const
而不是普通的指向对象的指针const
作为函数参数,因为只有前者才能保证所指向对象的可变性。
关于问题的第二部分:出于所有实际目的,可以将C ++引用视为具有自动间接指向的常量指针(而不是指向常量值的指针!)-它不是“更安全”或“更快”的比指针要方便。
这实际上取决于编译器/平台(它可能有助于对尚未编写的某些编译器或从未使用过的平台进行优化)。除了给出某些功能的复杂性要求外,C和C ++标准没有提及任何性能。
指向const的指针和引用通常无助于优化,因为在某些情况下可以合法地删除const限定,并且有可能可以通过其他非const引用来修改对象。另一方面,将对象声明为const可能会有所帮助,因为它可以保证不能修改该对象(即使将其传递给编译器不知道其定义的函数)。这允许编译器将const对象存储在只读存储器中,或将其值缓存在集中位置,从而减少了复制和检查修改的需要。
指针和引用通常以完全相同的方式实现,但是同样,这完全取决于平台。如果你真正感兴趣的,那么你应该看看在生成的机器码你的平台和编译器在你的程序(如果你确实使用的是生成机器代码的编译器)。
一件事是,如果声明全局变量const,则可以将其放在库或可执行文件的只读部分中,从而可以通过只读mmap在多个进程之间共享它。至少在全局变量中声明了许多数据的情况下,这在Linux上可能是一个巨大的内存胜利。
另一种情况,如果您声明一个常量全局整数,float或enum,则编译器也许能够将常量内联而不使用变量引用。尽管我不是编译器专家,但我认为这样做的速度要快一些。
引用只是在实现方面位于其下方的指针。
const
。
extern
变量。
extern
停止有意义时执行。
它可以帮助提高性能,但前提是您直接通过其声明访问该对象。引用参数等无法优化,因为可能存在指向对象的其他路径,而该路径最初并未声明为const,并且除非您使用的是声明,否则编译器通常无法确定您所引用的对象是否实际上已声明为const。
如果使用的是const声明,则编译器将知道外部编译的函数体等无法对其进行修改,因此您会从中受益。当然,像const int之类的东西会在编译时传播,所以这是一个巨大的胜利(相比于int)。
引用和指针的存储方式完全相同,只是语法上的行为不同。引用基本上是重命名的,因此是相对安全的,而指针可以指向许多不同的事物,因此更强大且更容易出错。
我猜想const指针在结构上将与引用相同,因此机器代码和效率将相同。真正的区别是语法-引用是一种更简洁,更易于阅读的语法,并且由于不需要指针提供的额外机制,因此从风格上考虑,首选引用。
const
很好,而无需您告知。(谈到最近的MSVC ++和GCC,它们都具有链接时间代码生成)
const
它也看不到的变量的限定条件。该内存可能被完全const
不存在的语言所困扰,甚至不存在,甚至可能是用原始汇编语言编写的。在这样的情况下,const
该库可以访问变量,则编译器必须忽略const限定符。因此,我不认为这与这个问题有什么关系。
const
,但大多数是人类的功能,以及我们编写的代码。编译器无法信任任何东西,因此无法以相同的方式合理地受益。