通过联合进行常量转换是否是未定义的行为?
与 C++ 不同,C 没有 const_cast 的概念。也就是说,没有有效的方法将 const 限定指针转换为非限定指针:
void const * p;
void * q = p; // not good
首先:此强制转换实际上是未定义的行为吗?
无论如何,海湾合作委员会对此发出警告。为了制作需要 const 强制转换的“干净”代码(即我可以保证不会改变内容,但我拥有的只是一个可变指针),我看到了以下“转换”技巧:
typedef union constcaster_
{
void * mp;
void const * cp;
} constcaster;
用法:<代码>u.cp = p; q = u.mp;。
通过这样的联合抛弃常量性的 C 语言规则是什么?我对 C 的了解非常零散,但我听说 C 对于联合访问比 C++ 宽松得多,所以虽然我对这种构造有不好的预感,但我想要一个来自标准的参数(我想是 C99,不过如果这在 C11 中发生了变化,那就很高兴知道)。
Unlike C++, C has no notion of a const_cast
. That is, there is no valid way to convert a const-qualified pointer to an unqualified pointer:
void const * p;
void * q = p; // not good
First off: Is this cast actually undefined behaviour?
In any event, GCC warns about this. To make "clean" code that requires a const-cast (i.e. where I can guarantee that I won't mutate the contents, but all I have is a mutable pointer), I have seen the following "conversion" trick:
typedef union constcaster_
{
void * mp;
void const * cp;
} constcaster;
Usage: u.cp = p; q = u.mp;
.
What are the C language rules on casting away constness through such a union? My knowledge of C is only very patchy, but I've heard that C is far more lenient about union access than C++, so while I have a bad feeling about this construction, I would like an argument from the standard (C99 I suppose, though if this has changed in C11 it'll be good to know).
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
它的实现定义参见 C99 6.5.2.3/5:
更新:@AaronMcDaid 评论说这毕竟可能是明确定义的。
该标准规定了以下6.2.5/27:
以及(6.7.2.1/14):
可能得出这样的结论:在这种特殊情况中,只有一种方法可以访问联合中的元素。
It's implementation defined, see C99 6.5.2.3/5:
Update: @AaronMcDaid commented that this might be well-defined after all.
The standard specified the following 6.2.5/27:
And (6.7.2.1/14):
One might conclude that, in this particular case, there is only room for exactly one way to access the elements in the union.
我的理解是,只有当您尝试修改 const 声明的对象时,UB 才会出现。
所以下面的代码不是 UB:
但这是 UB:
使用联合而不是强制转换在这里应该没有任何区别。
根据 C99 规范(6.7.3 类型限定符):
My understanding it that the UB can arise only if you try to modify a const-declared object.
So the following code is not UB:
But this is UB:
The use of a union instead of a cast should not make any difference here.
From the C99 specs (6.7.3 Type qualifiers):
初始化肯定不会导致UB。 §6.3.2.3/2 (n1570 (C11)) 中明确允许限定指针类型之间的转换。之后对该指针中内容的使用会导致 UB(请参阅 @rodrigo 的答案)。
但是,您需要显式转换才能将
void*
转换为const void*
,因为简单赋值的约束仍然要求左侧的所有限定符都出现在右侧。我不知道为什么 gcc 只是给出警告。
对于联合技巧,是的,它不是 UB,但结果仍然可能未指定。
n1124(C99)中的相应部分为
The initialization certainly won't cause UB. The conversion between qualified pointer types is explicitly allowed in §6.3.2.3/2 (n1570 (C11)). It's the use of content in that pointer afterwards that cause UB (see @rodrigo's answer).
However, you need an explicit cast to convert a
void*
to aconst void*
, because the constraint of simple assignment still require all qualifier on the LHS appear on the RHS.I don't know why gcc just gives a warning though.
And for the union trick, yes it's not UB, but still the result is probably unspecified.
The corresponding sections in n1124 (C99) are
根本不要投射它。它是一个指向 const 的指针,这意味着不允许尝试修改数据,并且在许多实现中,如果指针指向不可修改的内存,将导致程序崩溃。即使您知道内存可以修改,也可能有其他指向它的指针不希望它发生更改,例如,如果它是逻辑上不可变字符串的存储的一部分。
该警告的存在是有充分理由的。
如果需要修改 const 指针的内容,可移植的安全方法是首先复制它指向的内存,然后对其进行修改。
Don't cast it at all. It's a pointer to const which means that attempting to modify the data is not allowed and in many implementations will cause the program to crash if the pointer points to unmodifiable memory. Even if you know the memmory can be modified, there may be other pointers to it that do not expect it to change e.g. if it is part of the storage of a logically immutable string.
The warning is there for good reason.
If you need to modify the content of a const pointer, the portable safe way to do it is first to copy the memory it points to and then modify that.