与C++不同,C没有const_cast的概念。也就是说,没有有效的方法将常量限定指针转换为非限定指针:
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中发生了变化,那就好了)中提出一个论点。
发布于 2012-01-12 22:36:35
它的实现已定义,请参阅C99 6.5.2.3/5:
如果联合对象的成员的值在对该对象的最新存储是对不同成员的存储时使用,则行为由实现定义。
更新:@AaronMcDaid评论说,毕竟这可能是定义良好的。
该标准规定了以下6.2.5/27:
同样,指向兼容类型的限定或非限定版本的指针应具有相同的表示和对齐要求。27)
27)相同的表示和对齐要求意味着可互换性,如函数的参数、函数的返回值和联合成员。
和(6.7.2.1/14):
指向联合对象的指针,经过适当转换,指向它的每个成员(或者,如果成员是位域,则指向它所在的单元),反之亦然。
人们可能会得出结论,在这种特殊情况下,只有一种方法可以访问联合中的元素。
发布于 2012-01-12 22:19:46
我的理解是,只有当您尝试修改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类型限定符)中:
如果试图通过将左值与非常量限定类型一起使用来修改使用常量限定类型定义的对象,则行为是未定义的。
发布于 2012-01-12 22:43:17
初始化肯定不会导致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:如果用于读取联合对象内容的成员与上次用于在对象中存储值的成员不同,则该值的对象表示的适当部分将被重新解释为6.2.6中描述的新类型中的对象表示(该过程有时称为“类型双关语”)。这可能是一个陷阱表示。
§6.2.6.1/7:当值存储在联合类型的对象的成员中时,对象表示中不对应于该成员但确实对应于其他成员的字节采用未指定的值。(*注:另请参阅§6.5.2.3/6了解异常,但此处不适用)
n1124 (C99)中的相应部分包括
§6.5.16.1/1
https://stackoverflow.com/questions/8836418
复制相似问题