我知道还有其他类似的问题.我已经通读了这些,但我认为这个问题还没有得到回答.

我是否可以使用指针算法在相同类型的 struct 的连续(在声明序列中是连续的)字段序列之间移动,而无需咨询_Alignof

我可以在数组元素之间移动而不需要咨询_Alignof,因为包括了尾随填充.

然而, struct 中相同类型的连续字段是否可能与数组中的对齐方式不同?

换句话说,这段代码有可能是未定义的行为吗?

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);
}

我认为这是一种糟糕的做法.我am意识到了->.I know填充通常会产生干扰.我想知道填充在这种特定情况下是否会干扰.

这个问题是关于C的,我不关心C++.

到目前为止,我已经用GCC编译了这个,并try 了Valgrind,看看是否有什么不对劲,并用clang和UBSan编译了它.在这个系统(x86-64 Linux)上,它似乎很好.

推荐答案

严格地说,这是一种未定义的行为.

在数组对象上允许指针算法(为此,单个对象被视为1元素数组),前提是原始指针和结果指针指向相同的数组对象(或结束后的一个元素).此外,指向一次过终点的指针不能被取消引用,否则会触发未定义的行为.

这在关于加法运算符的C standard条的第6.5.6p8节中有详细说明:

将整型表达式相加或相减时 在指针中,结果具有指针操作数的类型.如果 指针操作数指向数组对象的元素,数组 足够大,则结果指向从 元素的下标之间的差异 结果数组元素和原始数组元素等于整数表达式. 换句话说,如果表达式P指向 数组对象,表达式(P)+N(相当于N+(P))和(P)-N (其中N的值为n)分别指向第i+n次和 I−数组对象的第n个元素,如果它们存在的话.此外,如果 表达式P指向数组对象的最后一个元素 表达式(P)+1指向数组对象的最后一个元素之后一个, 如果表达式Q指向数组的最后一个元素 对象,则表达式(Q)-1指向数组的最后一个元素 对象.如果指针操作数和结果都指向元素 相同的数组对象,或数组的最后一个元素之后的元素 对象,则计算不应产生溢出;否则, 行为未定义.If the result points one past the last element of the array object, it shall not be used as the operand of a unary 100 operator that is evaluated.

粗体部分正是这里发生的事情:

int field_3_value = *(field_2_ptr + 1);

由于这将取消引用指向(本质上)一个1元素数组的元素之后的指针,因此会触发未定义行为. &my_struct.field_2 + 1 == &my_struct.field_3的值为真并不重要.

出于同样的原因,这也是未定义的行为:

int x[2][2] = {{1,2},{3,4}};
int y = x[0][2];

虽然做到以上probably个都行得通,但不能保证.现代编译器正在积极地进行优化,他们能够而且确实假设不存在未定义的行为,并利用这一假设.

我见过这样的情况,在给定指向相邻内存的指针ab的情况下,(uintptr_t)(a+1) == (uintptr_t)b为真,a+1 == b为假.

C++相关问答推荐

在C中使用强制转换将uint16_t转换为uint8_t [2]是否有效?

来自stdarg.h的c中的va_args无法正常工作<>

这是一个合法的C Strdup函数吗?

当我更改编译优化时,相同的C代码以不同的方式运行

调用mProtection将堆栈上的内存设置为只读,直接导致程序SIGSEGV

限制不同类型的限定符

如何识别Linux中USB集线器(根)和连接到集线器(根设备)的设备(子设备)?

判断X宏的空性

tick.q中的Kdb+键控表语法

在C中访问数组中的特定值

处理EPOLL_WAIT中的接收数据和连接关闭信号

在C++中允许使用字符作为宏参数

如何使这个While循环在新行上结束

';malloc():损坏的顶部大小';分配超过20万整数后

C中2个数字的加法 - 简单的人类方法

GCC认为这是一个VLA是对的吗?

创建 makefile 来编译位于不同目录中的多个源文件

仅使用其内存地址取消引用 C 中的 struct

当 a 是代码块时使用逗号运算符 (a, b)

c中数组上显示的随机元素