我知道还有其他类似的问题。我已阅读这些内容,但认为这个问题尚未得到解答。
我可以使用指针算术在连续的(在声明序列中连续的)相同类型结构的字段序列之间移动而不需要咨询
_Alignof
吗?
我可以在数组元素之间移动而无需咨询
_Alignof
,因为包含尾随填充。
但是,结构体中相同类型的连续字段是否可能与数组中的对齐方式不同?
换句话说,这段代码有可能是未定义的行为吗?
struct MyStruct {
long int field_1;
int field_2;
int field_3;
};
int main(void) {
struct MyStruct my_struct;
int *field_2_ptr = &my_struct.field_2;
int field_3_value = *(field_2_ptr + 1);
}
我知道这是不好的做法。我意识到
->
。我知道填充通常会产生干扰。我想知道填充是否会干扰这种特定情况。
这个问题是关于C的。我不关心C++。
到目前为止,我已经使用 GCC 编译了它,并尝试使用 Valgrind 看看是否有问题,并使用 clang 和 UBSan 编译它。在这个系统(x86-64 Linux)上似乎没问题。
严格来说,这是未定义的行为。
允许在数组对象上进行指针算术(为此目的,单个对象被视为 1 元素数组),前提是原始指针和结果指针指向同一数组对象(或超出末尾的一个元素)。此外,指向尾后一位的指针不能被取消引用,否则会触发未定义的行为。
C 标准第 6.5.6p8 节中关于加法运算符对此进行了详细说明:
对整数类型的表达式进行加减运算时 对于指针,结果具有指针操作数的类型。如果 指针操作数指向数组对象的元素,并且数组 足够大,结果指向与 原始元素使得下标的差异 结果和原始数组元素等于整数表达式。 换句话说,如果表达式 P 指向 an 的第 i 个元素 数组对象,表达式 (P)+N(相当于 N+(P))和 (P)-N (其中 N 的值为 n)分别指向第 i+n 和 数组对象的第 i−n 个元素(前提是它们存在)。此外,如果 表达式 P 指向数组对象的最后一个元素, 表达式 (P)+1 指向数组对象的最后一个元素, 如果表达式 Q 指向数组的最后一个元素 对象,表达式 (Q)-1 指向数组的最后一个元素 目的。如果指针操作数和结果都指向元素 同一数组对象,或数组的最后一个元素 对象,评估不得产生溢出;否则, 行为未定义。 如果结果指向最后一个元素 数组对象的,它不能用作一元的操作数
被评估的运算符。*
粗体部分正是这里发生的情况:
int field_3_value = *(field_2_ptr + 1);
所以这会触发未定义的行为。
&my_struct.field_2 + 1 == &my_struct.field_3
的评估结果为 true 并不重要。
出于同样的原因,这也是未定义的行为:
int x[2][2] = {{1,2},{3,4}};
int y = x[0][2];