索引结构是否合法?

2021-12-23 00:00:00 c struct c++

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

Regardless of how 'bad' the code is, and assuming that alignment etc are not an issue on the compiler/platform, is this undefined or broken behavior?

如果我有这样的结构:-

If I have a struct like this :-

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

struct data thing;

abc 作为 (&thing.a)[0](&thing.a)[1](&thing.a)[2]?

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

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

In every case, on every compiler and platform I tried it on, with every setting I tried it 'worked'. I'm just worried that the compiler might not realize that b and thing[1] are the same thing and stores to 'b' might be put in a register and thing[1] reads the wrong value from memory (for example). In every case I tried it did the right thing though. (I realize of course that doesn't prove much)

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

This is not my code; it's code I have to work with, I'm interested in whether this is bad code or broken code as the different affects my priorities for changing it a great deal :)

标记 C 和 C++ .我主要对 C++ 感兴趣,但如果 C 不同,我也会感兴趣,只是为了兴趣.

Tagged C and C++ . I'm mostly interested in C++ but also C if it is different, just for interest.

推荐答案

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

It is illegal 1. That's an Undefined behavior in C++.

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

You are taking the members in an array fashion, but here is what the C++ standard says (emphasis mine):

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

[dcl.array/1]: ...An object of array type contains a contiguously allocated non-empty set of N subobjects of type T...

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

But, for members, there's no such contiguous requirement:

[class.mem/17]::strong> ...;实现对齐要求 可能会导致两个相邻的成员不要紧随其后分配...

[class.mem/17]: ...;Implementation alignment requirements might cause two adjacent members not to be allocated immediately after each other...

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

While the above two quotes should be enough to hint why indexing into a struct as you did isn't a defined behavior by the C++ standard, let's pick one example: look at the expression (&thing.a)[2] - Regarding the subscript operator:

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

[expr.post//expr.sub/1]: A postfix expression followed by an expression in square brackets is a postfix expression. One of the expressions shall be a glvalue of type "array of T" or a prvalue of type "pointer to T" and the other shall be a prvalue of unscoped enumeration or integral type. The result is of type "T". The type "T" shall be a completely-defined object type.66 The expression E1[E2] is identical (by definition) to ((E1)+(E2))

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

Digging into the bold text of the above quote: regarding adding an integral type to a pointer type (note the emphasis here)..

[expr.add/4]::strong> 当一个整数类型的表达式被添加到一个或从一个指针,结果具有指针操作数的类型.如果表达式P指向数组对象x的元素x[i]对于 n 个元素,表达式 P + JJ + P(其中 J 有值 j) 指向(可能是假设的)元素 x[i + j]如果 0 ≤ i + j ≤ n;否则,行为未定义....

[expr.add/4]: When an expression that has integral type is added to or subtracted from a pointer, the result has the type of the pointer operand. 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; otherwise, the behavior is undefined. ...

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

Note the array requirement for the if clause; else the otherwise in the above quote. The expression (&thing.a)[2] obviously doesn't qualify for the if clause; Hence, Undefined Behavior.

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

On a side note: Though I have extensively experimented the code and its variations on various compilers and they don't introduce any padding here, (it works); from a maintenance view, the code is extremely fragile. you should still assert that the implementation allocated the members contiguously before doing this. And stay in-bounds :-). But its still Undefined behavior....

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

Some viable workarounds (with defined behavior) have been provided by other answers.

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

As rightly pointed out in the comments, [basic.lval/8], which was in my previous edit doesn't apply. Thanks @2501 and @M.M.

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

1: See @Barry's answer to this question for the only one legal case where you can access thing.a member of the struct via this parttern.

相关文章