当前位置: 首页 > 知识库问答 >
问题:

将一个结构别名为它的第一个成员是严格的别名违规吗?

端木野
2023-03-14

示例代码:

struct S { int x; };

int func()
{
     S s{2};
     return (int &)s;    // Equivalent to *reinterpret_cast<int *>(&s)
}

我认为这是常见的,被认为是可以接受的。该标准确实保证结构中没有初始填充。但是,这种情况未在严格别名规则中列出(C 17 [basic.lval]/11):

如果程序尝试通过下列类型之一以外的 gl 值访问对象的存储值,则行为未定义:

  • (11.1)物体的动态类型,
  • (11.2) 对象的动态类型的 cv 限定版本,
  • (11.3) 与对象的动态类型相似的类型(如 7.5 中所定义),
  • (11.4) 与对象的动态类型相对应的有符号或无符号类型的类型,
  • (11.5) 一种类型,它是与对象的动态类型的 cv 限定版本相对应的有符号或无符号类型,
  • (11.6) 在其元素或非静态数据成员(递归地包括子聚合或包含的联合的元素或非静态数据成员)中的上述类型之一的聚合或联合类型,
  • (11.7) 一种类型,它是对象的动态类型的(可能是 cv 限定的)基类类型,
  • (11.8) 字符、无符号字符或标准::字节类型。

很明显,对象< code>s的存储值正在被访问。

项目符号中列出的类型是进行访问的glvalue的类型,而不是被访问对象的类型。在此代码中,glvalue类型是< code>int,它不是聚合或联合类型,排除了11.6。

我的问题是:这个代码正确吗?如果正确,在上面哪一个要点下是允许的?

共有3个答案

夹谷星剑
2023-03-14

引用的规则来自C89中的类似规则,除非人们延伸“by”这个词的含义,或者认识到C89编写时“未定义行为”的含义,否则这将是荒谬的。给定类似结构 S {无符号 dat[10];}s;, 语句 s.dat[1] ; 将清楚地修改 s 的存储值,但该表达式中结构 S 类型的唯一左值仅用于生成无符号* 类型的值。唯一用于修改任何对象的左值是 int 类型

在我看来,有两种相关的方法来解决这个问题:(1)认识到标准的作者希望允许一种类型的左值明显地从另一种类型的左值派生出来的情况,但是不想被什么形式的可见派生必须考虑的细节所困扰,特别是因为编译器需要识别的情况的范围会根据他们执行的优化风格和他们正在使用的任务而有很大的不同;(2)认识到标准的作者没有理由认为标准是否真的要求对特定的结构进行有效的处理是重要的,如果它本来是有用的的话每个人都清楚,有理由不这样做。

我不认为委员会成员对编译器是否给出类似于:

struct foo {int ct; int *dat;} it;
void test(void)
{
  for (int i=0; i < it.ct; i++)
    it.dat[i] = 0;
}

应该被要求确保例如在它之后.ct = 1234; 它.dat =

富钧
2023-03-14

我想是在expr.reinterpret.cast#11中

如果“指向T1的指针”类型的表达式可以使用reinterpret_ cast显式转换为“指向T2的指针”类型,则指定对象< code>x的T1类型的glvalue表达式可以转换为“引用T2”类型。结果是< code>*reinterpret_ cast的结果

< sub>[1]当结果引用与源glvalue相同的对象时,这有时被称为类型pun

支持@M. M关于指针无法覆盖的回答:

来自cppreference:

假设满足对齐要求,<code>reinterpret_cast</code>不会在处理指针可转换对象的少数有限情况之外更改指针的值:

struct S { int a; } s;


int* p = reinterpret_cast<int*>(&s); // value of p is "pointer to s.a" because s.a
                                     // and s are pointer-interconvertible
*p = 2; // s.a is also 2

struct S { int a; };

S s{2};
int i = (int &)s;    // Equivalent to *reinterpret_cast<int *>(&s)
                     // i doesn't change S.a;
陈胤
2023-03-14

演员的行为归结为[expr.static.cast]/13;

“指向cv1 void的指针”类型的prvalue可以转换为“指向cv2 T的指针”类型的prvalue,其中< code>T是一个对象类型,cv2与cv1具有相同的cv资格,或者比cv1具有更高的cv资格。如果原始指针值表示内存中一个字节的地址< code>A,而< code>A不满足< code>T的对齐要求,则结果指针值是未指定的。否则,如果原始指针值指向对象< code>a,并且有一个类型为< code>T(忽略cv限定)的对象< code>b,它可以与< code>a进行指针相互转换,则结果是指向< code>b的指针。否则,指针值不会因转换而改变。

指针可转换的定义是:

在以下情况下,两个对象 a 和 b 是指针互转换的:

  • 它们是同一个对象,或者
  • 一个是联合对象,另一个是该对象的非静态数据成员,或
  • 一个是标准布局类对象,另一个是该对象的第一个非静态数据成员,或者,如果该对象没有非静态数据成员,则为该对象的第一个基类子对象,或者
  • 存在一个对象c,使得a和c是指针可互换的,而c和b是指针可互换的。

所以在原始代码中,< code>s和< code>s.x是指针可相互转换的,因此< code>(int

因此,在严格的别名规则中,正在访问其存储值的对象是 s.x 而不是 s,因此没有问题,代码是正确的。

 类似资料:
  • 我正在阅读关于reinterpret_cast的笔记,它是混淆现象(http://en.cppreference.com/w/cpp/language/reinterpret_cast)。 我写了代码: 我认为这些规则在这里不适用: T2是对象的(可能是cv限定的)动态类型 T2和T1都是指向相同类型T3的指针(可能是多级的,可能在每个级别都是cv限定的)(因为C11) T2是一个聚合类型或并集类

  • C标准规定: 指向结构对象的指针,经过适当的强制转换,指向其初始成员(如果该成员是位字段,则指向其所在的单元),反之亦然。 如果有问题的结构的第一个成员是匿名结构/联合,是否有任何可能(并且定义良好)的方法在C11中执行这种“合适的转换”?或者如果包含的结构是匿名的,则执行“反之亦然”的反向转换? 我猜想,转换为与匿名结构具有相同成员序列的非匿名结构会使它无法很好地定义,因为它们不兼容,因此不能保

  • 基本上,当启用严格别名时,此代码是否合法? 这里,我们通过另一种类型的指针(指向<code>void*</code>的指针)访问一种类型(<code<int*</code>)的对象,因此我认为这确实是一种严格的别名冲突。 但是,试图突出未定义行为的样本使我怀疑(即使它不能证明它是合法的)。 首先,如果我们将<code>int*</code>和<code>char*</code>别名,我们可以根据优

  • 注意:学习严格的别名规则。请耐心等待。 代码示例(t935.c): 调用: 问题:向函数传递指向同一联合成员的两个指针是否违反了严格的别名规则? 这个问题源于工会和类型双关语。 更新20210901 如果union类型在全局范围内定义,会发生什么? 对于“联合在全局范围内定义”(实际上是文件范围)的情况,gcc和clang都显示出与上面报告的gcc相同的结果。

  • 我的理解是,由于所谓的“严格混淆现象规则”,以下代码在C中具有未定义的行为。 特别是,C编译器可能会完全忽略赋值,因为它可以假定

  • 问题内容: 你能帮我一下吗?我有这个SQL查询: 它以某种方式说 问题答案: 您似乎从同一张表中选择了两次。这些事件中的每一个都需要有自己的别名: 请注意,我还为该表添加了缺少的别名。