const是通过一个未定义的行为进行的吗? 与C++不同,C没有代码< CONTHOSTCAST < /代码>的概念。也就是说,没有有效的方法将常量限定指针转换为非限定指针: void const * p; void * q = p; // not good

const是通过一个未定义的行为进行的吗? 与C++不同,C没有代码< CONTHOSTCAST < /代码>的概念。也就是说,没有有效的方法将常量限定指针转换为非限定指针: void const * p; void * q = p; // not good,c,constants,language-lawyer,unions,C,Constants,Language Lawyer,Unions,首先:这个演员是否真的有未定义的行为 无论如何,GCC都会对此发出警告。为了生成需要const cast的“干净”代码(即,我可以保证不会改变内容,但我只有一个可变指针),我看到了以下“转换”技巧: 用法:u.cp=p;q=u.mp 通过这样的结合来抛弃常量的C语言规则是什么?我对C的了解非常零碎,但是我听说C比C++更宽松,因此我对这个构造有一种不好的感觉,我想从标准中得到一个论点(C99,我想,虽然如果C11改变了,那就好了)。.我的理解是,只有当您试图修改常量声明的对象时,UB才会出现 因

首先:这个演员是否真的有未定义的行为

无论如何,GCC都会对此发出警告。为了生成需要const cast的“干净”代码(即,我可以保证不会改变内容,但我只有一个可变指针),我看到了以下“转换”技巧:

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


通过这样的结合来抛弃常量的C语言规则是什么?我对C的了解非常零碎,但是我听说C比C++更宽松,因此我对这个构造有一种不好的感觉,我想从标准中得到一个论点(C99,我想,虽然如果C11改变了,那就好了)。.

我的理解是,只有当您试图修改常量声明的对象时,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类型限定符):

如果试图通过使用修改使用常量限定类型定义的对象 对于具有非常量限定类型的左值,行为未定义


一点也不要投。它是指向const的指针,这意味着不允许尝试修改数据,在许多实现中,如果指针指向不可修改的内存,则会导致程序崩溃。即使您知道memmory可以修改,也可能有其他指向它的指针不希望它更改,例如,如果它是逻辑不可变字符串存储的一部分

警告是有充分理由的


如果需要修改常量指针的内容,可移植的安全方法是首先复制它指向的内存,然后修改它。

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

如果使用联合对象成员的值时 对象的最近存储是另一个成员的,行为是 实现定义

更新:@AaronMcDaid评论说,这可能毕竟是定义良好的

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

类似地,指向compatible的限定或非限定版本的指针 类型应具有相同的表示和对齐方式 要求(第27段)

27)相同的表示和对齐要求旨在 暗示作为函数参数的互换性,从 职能和工会成员

和(6.7.2.1/14):

指向经过适当转换的联合对象的指针指向其每个对象 成员(或者,如果成员是位字段,则指向其所在的单位) ,反之亦然


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

初始化肯定不会导致UB。§6.3.2.3/2(n1570(C11))中明确允许在合格指针类型之间进行转换。是指针中的内容的使用导致了UB(参见的答案)

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

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

§6.5.16.1/1:(简单转让/契约)

  • 。。。两个操作数都是相同的 指向兼容类型的合格或不合格版本的指针,以及指向的类型 左边的to具有右边所指类型的所有限定符
  • 。。。一个操作数是指针 另一个是指向对象类型的合格或不合格版本的指针
    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)中的相应部分为

  • 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

你的意思是第一个代码块中的
void*q=(void*)p
吗?@KennyTM:是的-我需要显式强制转换吗,和/或它有什么不同吗?如果没有隐式强制转换,gcc警告放弃限定符。但gcc仍然报告错误(“初始值设定项元素不是常量”)即使使用cast。你可能会认为我从未变异过任何东西。也就是说,我有一个非变异操作,需要处理遗留的可变指针。我知道违反constness是UB——我的问题主要是关于
const int cx = 0;
const int *cp = &cx;
int *p = (int*)cp;
*p = 1; /* UB: cx is const */