const 正确性是否为编译器提供了更多优化空间?
我知道它提高了可读性并使程序不易出错,但是它对性能提高了多少?
顺便说一句,引用和 const 指针之间的主要区别是什么?我假设它们以不同的方式存储在内存中,但又是如何呢?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
我知道它提高了可读性并使程序不易出错,但是它对性能提高了多少?
顺便说一句,引用和 const 指针之间的主要区别是什么?我假设它们以不同的方式存储在内存中,但又是如何呢?
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
接受
或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
发布评论
评论(7)
[编辑:好的,所以这个问题比我一开始想象的更微妙。]
声明 const 指针或 const 引用永远不会帮助任何编译器优化任何内容。 (尽管请参阅此答案底部的更新。)
const 声明仅指示如何在其声明的范围内使用标识符;它并没有说底层对象不能改变。
示例:
编译器不能假设
*p
未被调用bar()
修改,因为p
可能是(例如)指针为全局 int,并且bar()
可能会修改它。如果编译器足够了解
foo()
的调用者和bar()
的内容,它可以证明bar()
不会修改*p
,那么它也可以在没有 const 声明的情况下执行该证明。但总的来说确实如此。因为 const 仅在声明的范围内有效,所以编译器已经可以看到您如何处理该范围内的指针或引用;它已经知道您没有修改底层对象。
简而言之,const 在这种情况下所做的一切都是为了防止您犯错误。它不会告诉编译器任何它不知道的事情,因此它与优化无关。
调用
foo()
的函数怎么样?例如:由于
foo()
采用const int *
,编译器能否证明它会打印 37?不会。即使 foo() 接受一个指向 const 的指针,它也可能会抛弃 const 性并修改 int。 (这不是未定义的行为。)这里再次强调,编译器一般不能做出任何假设;如果它对
foo()
有足够的了解来进行这样的优化,它就会知道即使没有const
。const
唯一允许优化的情况是这样的情况:这里,通过任何机制修改
x
(例如,通过获取指向它的指针并丢弃x
>const) 是调用未定义的行为。因此编译器可以假设您不这样做,并且它可以将常量 37 传播到 printf() 中。这种优化对于您声明const
的任何对象都是合法的。 (实际上,您从不引用的局部变量不会受益,因为编译器已经可以看到您是否在其范围内修改了它。)要回答您的“旁注”问题,(a) const 指针是指针; (b) const 指针可以等于 NULL。您是正确的,内部表示(即地址)很可能是相同的。
[更新]
正如 Christoph 在评论中指出的那样,我的答案不完整,因为它没有提到
restrict.
C99 标准第 6.7.3.1 (4) 节规定:
(这里 B 是一个基本块,P(指向 T 的限制指针)在其范围内。)
因此,如果 C 函数
foo()
声明如下:..那么编译器可能假设在
p
的生命周期内没有发生对*p
的修改——即在的执行期间foo()
——因为否则行为将是未定义的。因此原则上,将
restrict
与指向 const 的指针结合起来可以实现上面忽略的两种优化。我想知道是否有编译器实际上实现了这样的优化? (至少 GCC 4.5.2 没有。)请注意,
restrict
仅存在于 C 中,而不是 C++(甚至 C++0x)中,除非作为特定于编译器的扩展。[Edit: OK so this question is more subtle than I thought at first.]
Declaring a pointer-to-const or reference-of-const never helps any compiler to optimize anything. (Although see the Update at the bottom of this answer.)
The
const
declaration only indicates how an identifier will be used within the scope of its declaration; it does not say that the underlying object can not change.Example:
The compiler cannot assume that
*p
is not modified by the call tobar()
, becausep
could be (e.g.) a pointer to a global int andbar()
might modify it.If the compiler knows enough about the caller of
foo()
and the contents ofbar()
that it can provebar()
does not modify*p
, then it can also perform that proof without the const declaration.But this is true in general. Because
const
only has an effect within the scope of the declaration, the compiler can already see how you are treating the pointer or reference within that scope; it already knows that you are not modifying the underlying object.So in short, all
const
does in this context is prevent you from making mistakes. It does not tell the compiler anything it does not already know, and therefore it is irrelevant for optimization.What about functions that call
foo()
? Like:Can the compiler prove that this prints 37, since
foo()
takes aconst int *
?No. Even though
foo()
takes a pointer-to-const, it might cast the const-ness away and modify the int. (This is not undefined behavior.) Here again, the compiler cannot make any assumptions in general; and if it knows enough aboutfoo()
to make such an optimization, it will know that even without theconst
.The only time
const
might allow optimizations is cases like this:Here, to modify
x
through any mechanism whatsoever (e.g., by taking a pointer to it and casting away theconst
) is to invoke Undefined Behavior. So the compiler is free to assume you do not do that, and it can propagate the constant 37 into the printf(). This sort of optimization is legal for any object you declareconst
. (In practice, a local variable to which you never take a reference will not benefit, because the compiler can already see whether you modify it within its scope.)To answer your "side note" question, (a) a const pointer is a pointer; and (b) a const pointer can equal NULL. You are correct that the internal representation (i.e. an address) is most likely the same.
[update]
As Christoph points out in the comments, my answer is incomplete because it does not mention
restrict
.Section 6.7.3.1 (4) of the C99 standard says:
(Here B is a basic block over which P, a restrict-pointer-to-T, is in scope.)
So if a C function
foo()
is declared like this:...then the compiler may assume that no modifications to
*p
occur during the lifetime ofp
-- i.e., during the execution offoo()
-- because otherwise the Behavior would be Undefined.So in principle, combining
restrict
with a pointer-to-const could enable both of the optimizations that are dismissed above. Do any compilers actually implement such an optimization, I wonder? (GCC 4.5.2, at least, does not.)Note that
restrict
only exists in C, not C++ (not even C++0x), except as a compiler-specific extension.我立即想到了两种情况,其中适当的 const 限定允许额外的优化(在整个程序分析不可用的情况下):
这里,编译器知道打印
42
而不必检查bar()
的主体(在当前翻译单元中可能不可见),因为对foo
的所有修改都是非法的(< em>这与Nemo 的示例 )。然而,通过将
bar()
声明为foo
而不将foo
标记为const
也是可能的。在许多情况下,程序员实际上想要
restrict
限定的指向const
的指针,而不是普通的指向const
的指针作为函数参数,因为只有前者对所指向的变量的可变性做出任何保证- 到对象。至于你的问题的第二部分:出于所有实际目的,C++引用可以被认为是具有自动间接寻址的常量指针(而不是指向常量值的指针!) - 它不是任何“更安全”或“更快”比指针方便多了。
Off the top of my head, I can think of two cases where proper
const
-qualification allows additional optimizations (in cases where whole-program analysis is unavailable):Here, the compiler knows to print
42
without having to examine the body ofbar()
(which might not be visible in the curent translation unit) because all modifications tofoo
are illegal (this is the same as Nemo's example).However, this is also possible without marking
foo
asconst
by declaringbar()
asIn many cases, the programmer actually wants
restrict
-qualified pointers-to-const
and not plain pointers-to-const
as function parameters, as only the former make any guarantees about the mutability of the pointed-to objects.As to the second part of your question: For all practical purposes, a C++ reference can be thought of as a constant pointer (not a pointer to a constant value!) with automatic indirection - it is not any 'safer' or 'faster' than a pointer, just more convenient.
C++ 中的
const
有两个问题(就优化而言):const_cast
mutable
const_cast
意味着即使您通过 const 引用或 const 指针传递对象,该函数也可能会放弃 const 性并修改该对象(如果该对象一开始就不是 const 则允许)。可变
意味着即使一个对象是const
,它的某些部分也可以被修改(缓存行为)。此外,指向的对象(而不是被拥有)可以在 const 方法中修改,即使它们在逻辑上是对象状态的一部分。最后,全局变量也可以被修改...const 的作用是帮助开发人员及早发现逻辑错误。
There are two issues with
const
in C++ (as far as optimization is concerned):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
mean that even though an object isconst
, some of its parts may be modified (caching behavior). Also, objects pointed to (instead of being owned) can be modified inconst
methods, even when they logically are part of the object state. And finally global variables can be modified too...const
is here to help the developer catch logical mistakes early.const 正确性通常对性能没有帮助;大多数编译器甚至懒得去跟踪前端之外的常量。根据具体情况,将变量标记为 const 会有所帮助。
引用和指针在内存中的存储方式完全相同。
const-correctness generally doesn't help performance; most compilers don't even bother to track constness beyond the frontend. Marking variables as const can help, depending on the situation.
References and pointers are stored exactly the same way in memory.
一件事是,如果声明一个全局变量 const,则可以将其放入库或可执行文件的只读部分,从而使用只读 mmap 在多个进程之间共享它。至少如果您在全局变量中声明了大量数据,这在 Linux 上可能会带来很大的内存优势。
另一种情况是,如果声明一个常量全局整数、浮点数或枚举,编译器可能能够将常量内联,而不是使用变量引用。尽管我不是编译器专家,但我相信这要快一些。
从实现角度来看,引用只是底层的指针。
One thing is, if you declare a global variable const, it may be possible to put it in the read-only portion of a library or executable and thus share it among multiple processes with a read-only mmap. This can be a big memory win on Linux at least if you have a lot of data declared in global variables.
Another situation, if you declare a constant global integer, or float or enum, the compiler may be able to just put the constant inline rather than using a variable reference. That's a bit faster I believe though I'm not a compiler expert.
References are just pointers underneath, implementation-wise.
这实际上取决于编译器/平台(它可能有助于优化某些尚未编写的编译器,或某些您从未使用过的平台)。 C 和 C++ 标准除了给出某些函数的复杂性要求外,没有提及性能。
对 const 的指针和引用通常无助于优化,因为在某些情况下可以合法地放弃 const 限定,并且对象可能可以通过不同的非 const 引用进行修改。另一方面,将对象声明为 const 可能会有所帮助,因为它保证该对象不能被修改(即使传递给编译器不知道其定义的函数也是如此)。这允许编译器将 const 对象存储在只读内存中,或将其值缓存在集中位置,从而减少复制和检查修改的需要。
指针和引用通常以完全相同的方式实现,但这又完全依赖于平台。如果您真的感兴趣,那么您应该查看为您的平台和您的程序中的编译器生成的机器代码(如果您确实正在使用生成机器代码的编译器)。
This really depends on the compiler/platform (it may help optimisation on some compiler that has not yet been written, or on some platform that you never use). The C and C++ standards say nothing about performance other than giving complexity requirements for some functions.
Pointers and references to const usually do not help optimisation, as the const qualification can legally be cast away in some situations, and it is possible that the object can be modified by a different non-const reference. On the other hand, declaring an object to be const can be helpful, as it guarantees that the object cannot be modified (even when passed to functions that the compiler does not know the definition of). This allows the compiler to store the const object in read-only memory, or cache its value in a centralised place, reducing the need for copies and checks for modifications.
Pointers and references are usually implemented in the exact same way, but again this is totally platform dependant. If you are really interested then you should look at the generated machine code for your platform and compiler in your program (if indeed you are using a compiler that generates machine code).
它可以稍微提高性能,但前提是您直接通过对象的声明访问该对象。引用参数等无法优化,因为可能存在指向最初未声明为 const 的对象的其他路径,并且编译器通常无法判断您引用的对象是否实际上已声明为 const,除非您正在使用该声明。
如果您使用 const 声明,编译器将知道外部编译的函数体等无法修改它,因此您可以从中受益。当然,像 const int 这样的东西是在编译时传播的,所以这是一个巨大的胜利(与仅仅一个 int 相比)。
引用和指针的存储方式完全相同,只是语法上的行为不同。引用基本上是重命名,因此相对安全,而指针可以指向许多不同的事物,因此更强大且更容易出错。
我猜想 const 指针在架构上与引用相同,因此机器代码和效率将是相同的。真正的区别在于语法——引用是一种更清晰、更容易阅读的语法,并且由于您不需要指针提供的额外机制,因此在风格上首选引用。
It can help performance a little bit, but only if you are accessing the object directly through its declaration. Reference parameters and such cannot be optimized, since there might be other paths to an object not originally declared const, and the compiler generally can't tell if the object you are referencing was actually declared const or not unless that's the declaration you are using.
If you are using a const declaration, the compiler will know that externally-compiled function bodies, etc. cannot modify it, so you get a benefit there. And of course things like const int's are propagated at compile time, so that's a huge win (compared to just an int).
References and pointers are stored exactly the same, they just behave differently syntactically. References are basically renamings, and so are relatively safe, whereas pointers can point to lots of different things, and are thus more powerful and error-prone.
I guess the const pointer would be architecturally identical to the reference, so the machine code and efficiency would be the same. the real difference is syntax -- references are a cleaner, easier to read syntax, and since you don't need the extra machinery provided by a pointer, a reference would be stylistically preferred.