什么时候丢弃 const 是合法的还是非法的? (gcc、clang 和 MSVC)
When is casting away const legal or illegal? (gcc, clang, and MSVC)
什么时候丢弃 const
是非法的?
在许多情况下,const
只是为了用户的利益而由编译器强制执行的注释。这个问题是在什么时候抛出 const
是严格非法的,C99 标准的哪一部分禁止它(如果有的话)?
我怀疑它在某些情况下一定是非法的,因为我观察到 const
全局范围内的函数指针有时会被 gcc 内联。如果这些函数指针可以在程序的后面合法修改,那么这个优化将是无效的。此外,我很确定我已经阅读了关于什么时候在 Whosebug 上放弃 const
是非法的解释,但我就是找不到它。 (我认为答案是 const
只能在变量最初未声明为 const
时被丢弃。)
我已标记此 C99,因为我希望这是 C99 标准中指定的内容,但如果答案可以解释 C99 的 gcc、clang 和 MSVC 实现的常见行为,那么这也足以我.
(显然这是一个非常基本的问题,所以我确实尝试并未能找到一个重复的 专门针对 C 标准 。如果其他人可以指出一个完全相同的重复我'我会很乐意结束投票。无论哪种情况,我认为这个问题标题至少有助于 SSO 解决此类问题。)
抛弃常量从不违法。你可以随心所欲地自由合法地做这件事。但是在去掉常量之后试图修改指向的对象可能会导致未定义的行为(或者可能不会,这取决于具体情况)。
C99 明确指出
6.7.3 Type qualifiers
5 If an attempt is made to modify an object defined with a const-qualified type through use of an lvalue with non-const-qualified type, the behavior is undefined.
(C11中为6.7.3/6)
请注意,C(和 C++)中的 const
用于两个独立且截然不同的角色:对象本身的 const 限定和 的 const 限定对象的访问路径。
const int a = 42; // `a` is a const-qualifiet object
int b = 5; // `b` is not const-qualified
const int *p = &b; // `*p` is a const-qualified access path to `b`
在后一种情况下,可以合法地丢弃常量性,并且可以使用由此产生的非常量访问路径来修改对象,前提是对象本身不是常量
*(int *) p = 6; // nothing formally wrong with it
这就是我们有能力在语言中抛弃常量的原因。
但是前者(对象本身的常量)不能被覆盖
*(int *) &a = 43; // undefined behavior
原因很简单——const 对象可以物理地位于只读内存中。即使不是,编译器仍然可以在假设对象的值永远不会改变的情况下自由编译代码。
这意味着在像
这样的声明中
const int *const cp = &b;
这两个const
的意思完全不同。第一个(最左边的)是一个“软”const
,如果条件合适,它可以被覆盖。但是第二个 const
是一个“硬”const
,不能被覆盖。
因此,当您说“const
只是一个为了用户的利益而由编译器强制执行的注解”时,它仅适用于访问路径的常量限定。当谈到对象本身的 const 限定时,通常不仅仅是纯粹概念性的编译器强制“注释”。
什么时候丢弃 const
是非法的?
在许多情况下,const
只是为了用户的利益而由编译器强制执行的注释。这个问题是在什么时候抛出 const
是严格非法的,C99 标准的哪一部分禁止它(如果有的话)?
我怀疑它在某些情况下一定是非法的,因为我观察到 const
全局范围内的函数指针有时会被 gcc 内联。如果这些函数指针可以在程序的后面合法修改,那么这个优化将是无效的。此外,我很确定我已经阅读了关于什么时候在 Whosebug 上放弃 const
是非法的解释,但我就是找不到它。 (我认为答案是 const
只能在变量最初未声明为 const
时被丢弃。)
我已标记此 C99,因为我希望这是 C99 标准中指定的内容,但如果答案可以解释 C99 的 gcc、clang 和 MSVC 实现的常见行为,那么这也足以我.
(显然这是一个非常基本的问题,所以我确实尝试并未能找到一个重复的 专门针对 C 标准 。如果其他人可以指出一个完全相同的重复我'我会很乐意结束投票。无论哪种情况,我认为这个问题标题至少有助于 SSO 解决此类问题。)
抛弃常量从不违法。你可以随心所欲地自由合法地做这件事。但是在去掉常量之后试图修改指向的对象可能会导致未定义的行为(或者可能不会,这取决于具体情况)。
C99 明确指出
6.7.3 Type qualifiers
5 If an attempt is made to modify an object defined with a const-qualified type through use of an lvalue with non-const-qualified type, the behavior is undefined.
(C11中为6.7.3/6)
请注意,C(和 C++)中的 const
用于两个独立且截然不同的角色:对象本身的 const 限定和 的 const 限定对象的访问路径。
const int a = 42; // `a` is a const-qualifiet object
int b = 5; // `b` is not const-qualified
const int *p = &b; // `*p` is a const-qualified access path to `b`
在后一种情况下,可以合法地丢弃常量性,并且可以使用由此产生的非常量访问路径来修改对象,前提是对象本身不是常量
*(int *) p = 6; // nothing formally wrong with it
这就是我们有能力在语言中抛弃常量的原因。
但是前者(对象本身的常量)不能被覆盖
*(int *) &a = 43; // undefined behavior
原因很简单——const 对象可以物理地位于只读内存中。即使不是,编译器仍然可以在假设对象的值永远不会改变的情况下自由编译代码。
这意味着在像
这样的声明中const int *const cp = &b;
这两个const
的意思完全不同。第一个(最左边的)是一个“软”const
,如果条件合适,它可以被覆盖。但是第二个 const
是一个“硬”const
,不能被覆盖。
因此,当您说“const
只是一个为了用户的利益而由编译器强制执行的注解”时,它仅适用于访问路径的常量限定。当谈到对象本身的 const 限定时,通常不仅仅是纯粹概念性的编译器强制“注释”。