是通过union未定义的行为进行const-casting吗?

时间:2012-01-12 14:10:06

标签: c const language-lawyer unions

与C ++不同,C没有const_cast的概念。也就是说,没有有效的方法将const限定指针转换为非限定指针:

void const * p;
void * q = p;    // not good

首先关闭:这个演员实际上是未定义的行为吗?

无论如何,GCC警告这一点。要制作需要const-cast的“干净”代码(即我可以保证我不会改变内容,但我所拥有的只是一个可变指针),我看到了以下“转换”技巧:

typedef union constcaster_
{
    void * mp;
    void const * cp;
} constcaster;

用法:u.cp = p; q = u.mp;

通过这种联盟抛弃常量的C语言规则是什么?我对C的了解只是非常不完整,但我听说C对联合访问比C ++要宽松得多,所以虽然我对这个结构有一种不好的感觉,但我想从标准中得到一个论证(C99我想,虽然如果在C11中有所改变,那么知道就好了。

4 个答案:

答案 0 :(得分:10)

它的实现已定义,参见C99 6.5.2.3/5:

  

如果最多使用union对象的成员的值   最近商店的对象是一个不同的成员,行为是   实现定义的。

更新: @AaronMcDaid评论说,毕竟这可能是明确定义的。

该标准规定了以下6.2.5 / 27:

  

同样,指向兼容的合格或非限定版本的指针   类型应具有相同的表示和对齐方式   requirements.27)

     

27)相同的表示和对齐要求意味着   意味着可互换性作为函数的参数,返回值   职能和工会成员。

和(6.7.2.1/14):

  

指向适当转换的联合对象的指针指向每个联合对象   成员(或者如果一个成员是一个位域,那么就是它所在的单位   居住),反之亦然。

一个可能得出结论,在这种特殊情况下,只有一种方法可以访问联合中的元素。

答案 1 :(得分:3)

我理解只有当你试图修改一个const声明的对象时才会出现UB。

所以下面的代码不是UB:

int x = 0;
const int *cp = &x;
int *p = (int*)cp;
*p = 1; /* OK: x is not a const object */

但这是UB:

const int cx = 0;
const int *cp = &cx;
int *p = (int*)cp;
*p = 1; /* UB: cx is const */

使用联合而不是强制转换不应该有任何区别。

来自C99规范(6.7.3类型限定符):

  

如果尝试通过use修改使用const限定类型定义的对象   对于具有非const限定类型的左值,行为是未定义的。

答案 2 :(得分:3)

初始化肯定不会导致UB。在§6.3.2.3/ 2(n1570(C11))中明确允许限定指针类型之间的转换。之后使用该指针中的内容会导致UB(请参阅@rodrigo的回答)。

但是,您需要使用显式强制转换将void*转换为const void*,因为简单赋值的约束仍然要求LHS上的所有限定符都出现在RHS上。

  

§6.7.9/ 11: ...对象的初始值是表达式的初始值(转换后);与简单赋值相同的类型约束和转换适用,将标量的类型作为其声明类型的非限定版本。

     

§6.5.16.1/ 1 :(简单分配/约束)

     
      
  • ......两个操作数都是   指向兼容类型的限定或非限定版本的指针,以及指向的类型   左边的所有限定符都是右边所指的类型;
  •   
  • ...一个操作数是一个指针   到对象类型,另一个是指向合格或非限定版本的指针   void,左侧指向的类型具有指向的类型的所有限定符   在右边;
  •   

我不知道为什么gcc会发出警告。


对于联合技巧,是的,它不是UB,但结果可能还没有说明。

  

§6.5.2.3/ 3 fn 95 :如果用于读取union对象内容的成员与上次用于在对象中存储值的成员不同,则为值的对象表示的一部分被重新解释为新类型中的对象表示,如6.2.6中所述(有时称为“类型双关”的过程)。这可能是陷阱表示。

     

§6.2.6.1/ 7 :当一个值存储在union类型的对象的成员中时,该对象表示的字节与该成员不对应但对应于其他成员采用未指定的值。 (*注意:有关例外,另请参见§6.5.2.3/ 6,但此处不适用)


n1124(C99)中的相应部分是

  • C11§6.3.2.3/ 2 =C99§6.3.2.3/ 2
  • C11§6.7.9/ 11 =C99§6.7.8/ 11
  • C11§6.5.16.1/ 1 =C99§6.5.16.1/ 1
  • C11§6.5.2.3/ 3 fn 95 =缺失(“类型双关”未出现在C99中)
  • C11§6.2.6.1/ 7 =C99§6.2.6.1/ 7

答案 3 :(得分:0)

根本不要施放它。它是指向const的指针,这意味着不允许尝试修改数据,并且在许多实现中如果指针指向不可修改的内存,将导致程序崩溃。即使你知道memmory可以被修改,也可能有其他指针,它不会指望它改变,例如如果它是逻辑上不可变字符串存储的一部分。

警告是有充分理由的。

如果你需要修改const指针的内容,那么便携式安全方法首先要复制它指向的内存然后修改它。