[英]Does the 'private' access modifier give the compiler more room for optimization?
[英]Does const-correctness give the compiler more room for optimization?
我知道它提高了可读性并使程序更不容易出错,但是它对性能的提高有多大呢?
顺便说一句,引用和const
指针之间的主要区别是什么? 我会假设它们以不同的方式存储在 memory 中,但怎么会呢?
[编辑:好的,所以这个问题比我一开始想的要微妙。]
声明指向 const 或引用 const 的指针永远不会帮助任何编译器优化任何东西。 (尽管请参阅此答案底部的更新。)
const
声明仅指示标识符将如何在其声明的scope中使用; 并不是说底层 object 不能改变。
例子:
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
仅在声明的 scope 内有效,所以编译器已经可以看到您如何处理 scope 内的指针或引用; 它已经知道您没有修改底层 object。
所以简而言之,所有const
在这种情况下所做的都是防止你犯错误。 它不会告诉编译器任何它不知道的东西,因此它与优化无关。
调用foo()
的函数呢? 喜欢:
int x = 37;
foo(&x);
printf("%d\n", x);
编译器能否证明这打印了 37,因为foo()
需要一个const int *
?
不。即使foo()
采用指向 const 的指针,它也可能会丢弃 const 并修改 int。 (这不是未定义的行为。)在这里,编译器通常不能做出任何假设。 如果它对foo()
有足够的了解来进行这样的优化,它就会知道即使没有const
。
const
唯一可能允许优化的情况是这样的:
const int x = 37;
foo(&x);
printf("%d\n", x);
在这里,通过任何机制修改x
(例如,通过获取指向它的指针并丢弃const
)是调用未定义行为。 所以编译器可以自由地假设你不这样做,它可以将常量 37 传播到 printf() 中。 这种优化对于您声明const
的任何 object 都是合法的。 (实际上,您从不引用的局部变量不会受益,因为编译器已经可以看到您是否在其 scope 中对其进行了修改。)
要回答您的“旁注”问题,(a) const 指针是指针; (b) const 指针可以等于 NULL。 您是正确的,内部表示(即地址)很可能是相同的。
[更新]
正如克里斯托夫在评论中指出的那样,我的回答是不完整的,因为它没有提到restrict
。
C99 标准的第 6.7.3.1 (4) 节说:
在每次执行 B 期间,令 L 为基于 P 的具有 &L 的任何左值。如果 L 用于访问它指定的 object X 的值,并且 X 也被修改(通过任何方式),则适用以下要求: T 不应是 const 限定的。 ...
(这里 B 是一个基本块,P 是指向 T 的限制指针,位于 scope 中。)
因此,如果 C function foo()
声明如下:
foo(const int * restrict p)
...那么编译器可能假设在 p 的生命周期内没有对*p
p
修改——即在foo()
的执行期间——因为否则行为将是未定义的。
因此,原则上,将restrict
与指向 const 的指针相结合可以启用上面忽略的两种优化。 我想知道是否有任何编译器实际上实现了这样的优化? (至少 GCC 4.5.2 没有。)
请注意, restrict
仅存在于 C 中,而不存在于 C++(甚至 C++0x 中),除非作为特定于编译器的扩展。
在我的脑海中,我可以想到两种情况,适当的const
限定允许额外的优化(在整个程序分析不可用的情况下):
const int foo = 42;
bar(&foo);
printf("%i", foo);
在这里,编译器知道打印42
而不必检查bar()
的主体(在当前的翻译单元中可能不可见),因为对foo
的所有修改都是非法的(这与Nemo 的示例相同)。
但是,这也可以通过将bar()
声明为而不将foo
标记为const
extern void bar(const int *restrict p);
在许多情况下,程序员实际上想要restrict
限定的指向const
的指针而不是普通的指向const
的指针作为 function 参数,因为只有前者对指向对象的可变性做出任何保证。
至于问题的第二部分:出于所有实际目的,C++ 引用可以被认为是具有自动间接的常量指针(不是指向常量值的指针) - 它不是任何“更安全”或“更快”比指针。 只是更方便。
C++ 中的const
有两个问题(就优化而言):
const_cast
mutable
const_cast
mean that even though you pass an object by const reference or const pointer, the function might cast the const-ness away and modify the object (allowed if the object is not const to begin with).
mutable
意味着即使 object 是const
,它的某些部分可能会被修改(缓存行为)。 此外,可以在const
方法中修改指向(而不是拥有)的对象,即使它们在逻辑上是 object state 的一部分。 最后全局变量也可以修改...
const
可以帮助开发人员及早发现逻辑错误。
const 正确性通常无助于提高性能; 大多数编译器甚至不费心去跟踪前端以外的常量。 根据具体情况,将变量标记为 const 会有所帮助。
引用和指针在 memory 中的存储方式完全相同。
这实际上取决于编译器/平台(它可能有助于优化某些尚未编写的编译器,或者您从未使用过的某些平台)。 C 和 C++ 标准除了给出某些功能的复杂性要求外,只字未提性能。
对 const 的指针和引用通常无助于优化,因为在某些情况下可以合法地抛弃 const 限定条件,并且 object 可以通过不同的非常量引用进行修改。 另一方面,将 object 声明为 const 可能会有所帮助,因为它保证了 object 不能被修改(即使传递给编译器不知道其定义的函数)。 这允许编译器将 const object 存储在只读 memory 中,或将其值缓存在集中位置,从而减少复制和检查修改的需要。
指针和引用通常以完全相同的方式实现,但这完全取决于平台。 如果您真的感兴趣,那么您应该查看为您的平台和程序中的编译器生成的机器代码(如果您确实使用的是生成机器代码的编译器)。
一件事是,如果您声明一个全局变量 const,则可以将其放在库或可执行文件的只读部分中,从而使用只读 mmap 在多个进程之间共享它。 至少如果您在全局变量中声明了大量数据,这可能是 Linux 上的一个大 memory 胜利。
另一种情况,如果您声明一个常量全局 integer 或浮点数或枚举,编译器可能只将常量内联而不是使用变量引用。 尽管我不是编译器专家,但我相信这会更快一些。
引用只是下面的指针,实现方式。
它可以对性能有所帮助,但前提是您直接通过其声明访问 object。 引用参数等无法优化,因为可能存在指向最初未声明为 const 的 object 的其他路径,并且编译器通常无法判断您引用的 object 是否实际声明为 const,除非这是您正在使用的声明。
如果您使用 const 声明,编译器将知道外部编译的 function 主体等无法修改它,因此您可以从中受益。 当然,像 const int 这样的东西是在编译时传播的,所以这是一个巨大的胜利(与一个 int 相比)。
引用和指针的存储方式完全相同,只是在语法上表现不同。 引用基本上是重命名,因此相对安全,而指针可以指向许多不同的东西,因此更强大且更容易出错。
我猜 const 指针在架构上与引用相同,因此机器代码和效率是相同的。 真正的区别在于语法——引用是一种更简洁、更易于阅读的语法,并且由于您不需要指针提供的额外机制,因此在风格上更倾向于引用。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.