具体来说,是下面的代码,标记下方的行,好吗?
struct S{
int a;
};
#include <stdlib.h>
int main(){
struct S *p;
p = malloc(sizeof(struct S) + 1000);
// This line:
*(&(p->a) + 1) = 0;
}
Run Code Online (Sandbox Code Playgroud)
人们在这里争论,但没有人给出令人信服的解释或参考.
他们的论点略有不同,但基本相同
typedef struct _pack{
int64_t c;
} pack;
int main(){
pack *p;
char str[9] = "aaaaaaaa"; // Input
size_t len = offsetof(pack, c) + (strlen(str) + 1);
p = malloc(len);
// This line, with similar intention:
strcpy((char*)&(p->c), str);
// ^^^^^^^
Run Code Online (Sandbox Code Playgroud) 在这两个示例中,通过偏移指向其他成员的指针来访问结构的成员是否会导致未定义/未指定/实现定义的行为?
struct {
int a;
int b;
} foo1 = {0, 0};
(&foo1.a)[1] = 1;
printf("%d", foo1.b);
struct {
int arr[1];
int b;
} foo2 = {{0}, 0};
foo2.arr[1] = 1;
printf("%d", foo2.b);
Run Code Online (Sandbox Code Playgroud)
第6.7.2.1段第14段似乎表明这应该是实施定义的:
结构或联合对象的每个非位字段成员以适合其类型的实现定义方式对齐.
然后继续说:
结构对象中可能存在未命名的填充,但不是在其开头.
但是,像下面这样的代码似乎很常见:
union {
int arr[2];
struct {
int a;
int b;
};
} foo3 = {{0, 0}};
foo3.arr[1] = 1;
printf("%d", foo3.b);
(&foo3.a)[1] = 2; // appears to be illegal despite foo3.arr == &foo3.a
printf("%d", foo3.b);
Run Code Online (Sandbox Code Playgroud)
标准似乎保证foo3.arr是相同的&foo3.a,并且没有任何意义,指的是一种方式是合法的而另一种方式不是,但同样没有意义的是,添加外部联合与数组应该突然(&foo3.a)[1]法律. …
这是对另一个问题的后续行动
我试图在编译时建立一个特定的实现是否在结构中添加了未命名的填充.像gcc这样的特定实现允许使用pragma来控制结构中的填充和对齐,但代价是与其他实现的兼容性.由于这两个static_assert和offset_of被用于C11的n1570草案所要求的,我想用他们看到实现是否成员之间使用填充.
这是代码的相关部分(引用问题中的完整代码):
#include <stdio.h>
#include <stddef.h>
#include <assert.h>
struct quad {
int x;
int y;
int z;
int t;
};
int main() {
// ensure members are consecutive (note 1)
static_assert(offsetof(struct quad, t) == 3 * sizeof(int),
"unexpected padding in quad struct");
struct quad q;
...
Run Code Online (Sandbox Code Playgroud)
正如6.7.2.1结构和联合说明符§15所说:
在结构对象中,非位字段成员和位字段所在的单元具有按声明顺序增加的地址.指向适当转换的结构对象的指针指向其初始成员(或者如果该成员是位字段,则指向它所在的单元),反之亦然.结构对象中可能存在未命名的填充,但不是在其开头.
我假设如果结构中元素的偏移量是在它之前声明的元素的大小的总和,那么这些元素之间不存在填充,并且它们应该连续分配,从而如果它们是相同类型则构成数组.
问题是:上述假设是错误的,它是(对参考问题的评论让我们思考)为什么?