对结构进行索引是否合法?

新手上路,请多包涵

不管代码有多“糟糕”,并且假设对齐等在编译器/平台上不是问题,这是未定义或损坏的行为吗?

如果我有这样的结构:-

 struct data
{
    int a, b, c;
};

struct data thing;

Is it legal to access a , b and c as (&thing.a)[0] , (&thing.a)[1] , and (&thing.a)[2] ?

在每种情况下,在我尝试过的每个编译器和平台上,我尝试过的每个设置都“有效”。我只是担心编译器可能没有意识到 bthing[1] 是同一个东西,并且存储到 ‘b’ 的内容可能会放在一个寄存器中,而 thing[1] 从内存中读取错误的值(例如)。在每种情况下,我都尝试过它做了正确的事情。 (我当然意识到这并不能证明太多)

这不是我的代码;这是我必须使用的代码,我对这是 代码还是 损坏 代码感兴趣,因为不同之处会影响我对其进行大量更改的优先级:)

标记 C 和 C++ 。我对 C++ 最感兴趣,但如果 C 不同,我也对 C 感兴趣,只是为了感兴趣。

原文由 jcoder 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 466
2 个回答

这是非法的1 。这是 C++ 中的未定义行为。

您正在以数组方式获取成员,但这是 C++ 标准所说的(强调我的):

[dcl.array/1] : …数组类型的对象包含一个 连续 分配的非空集合,由 N 个 T 类型的子对象组成…

但是,对于成员来说,没有这样的 连续 要求:

[class.mem/17] : …;实现对齐要求 可能会导致两个相邻的成员不会被立即分配

虽然上面的两个引号应该足以暗示为什么索引到 struct 不是 C++ 标准定义的行为,让我们举一个例子:看看表达式 (&thing.a)[2] - 关于下标运算符:

[expr.post//expr.sub/1] 后缀表达式后跟方括号中的表达式是后缀表达式。其中一个表达式应为“T 数组”类型的左值或“指向 T 的指针”类型的纯右值,另一个应为无作用域枚举或整数类型的纯右值。结果是类型“T”。类型“T”应为完全定义的对象类型。66 表达式 E1[E2] ((E1)+(E2)) (根据定义)

深入研究上面引用的粗体文本:关于将整数类型添加到指针类型(注意这里的重点)..

[expr.add/4] : 当一个整数类型的表达式被添加到指针或从指针中减去时,结果具有指针操作数的类型。 If the expression P points to element x[i] of an array object x with n elements, the expressions P + J and J + P (where J has the value j ) point to the (possibly-hypothetical) element x[i + j] if 0 ≤ i + j ≤ n ; 否则,行为未定义。 …

注意 if 子句的 数组 要求; 否则 在上面的引用中。表达式 (&thing.a)[2] 显然不符合 if 子句的条件;因此,未定义的行为。


附带说明:虽然我已经在各种编译器上对代码及其变体进行了广泛的试验,但它们在这里没有引入任何填充,(它 _有效_);从维护的角度来看,代码是极其脆弱的。在执行此操作之前,您仍然应该断言实现连续分配成员。并保持在界内:-)。但它仍然是未定义的行为….

其他答案提供了一些可行的解决方法(具有定义的行为)。



正如评论中正确指出的那样,我之前编辑的 [basic.lval/8] 不适用。谢谢@2501 和@MM

1 :请参阅@Barry 对此问题的回答,了解您可以通过此部分访问 thing.a 结构成员的唯一一个法律案例。

原文由 WhiZTiM 发布,翻译遵循 CC BY-SA 3.0 许可协议

不。在 C 中,即使没有填充,这也是未定义的行为。

导致未定义行为的事情是越界访问1 。当您有一个标量(结构中的成员 a、b、c)并尝试将其用作数组2来访问下一个假设元素时,您会导致未定义的行为,即使恰好在那个地址。

但是,您可以使用 struct 对象的地址并计算特定成员的偏移量:

 struct data thing = { 0 };
char* p = ( char* )&thing + offsetof( thing , b );
int* b = ( int* )p;
*b = 123;
assert( thing.b == 123 );

这必须为每个成员单独完成,但可以放入类似于数组访问的函数中。


1 (引自:ISO/IEC 9899:201x 6.5.6 加法运算符 8)

如果结果指向数组对象的最后一个元素,则不应将其用作计算的一元 \* 运算符的操作数。

2 (引自:ISO/IEC 9899:201x 6.5.6 加法运算符 7)

出于这些运算符的目的,指向不是数组元素的对象的指针与指向长度为 1 且对象类型作为其元素类型的数组的第一个元素的指针的行为相同。

原文由 2501 发布,翻译遵循 CC BY-SA 3.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题