使用灵活数组成员延迟指向结构的指针不是 "copy" FAM?

Defererencing a pointer to a struct with a Flexible Array Member doesn't "copy" the FAM?

使用 灵活数组成员 (FAM) 将 pointer 推迟到 struct (FAM) 不会“复制”FAM?

根据这个程序,这似乎是行为,它创建一个带有 FAM 的结构实例,然后检查指向该结构的指针和该结构的取消引用的“二进制布局”。

#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>

int main(){

typedef struct{
  uint8_t  idim;
  uint32_t data[];
}array_t;  // https://gustedt.wordpress.com/2011/03/14/flexible-array-member/

  int64_t  x_bdim = sizeof(array_t) + sizeof(uint32_t)*0x2;
  array_t* _x     = aligned_alloc(0x1000,x_bdim);
  array_t   x     = *_x;
  uint8_t* raw;

  x.idim          = 0xff;
  x.data[0x0]     = 0x11111111;
  x.data[0x1]     = 0x22222222;

  _x->idim          = 0xff;
  _x->data[0x0]     = 0x11111111;
  _x->data[0x1]     = 0x22222222;

  printf("%'ld %'ld %'ld\n", sizeof(array_t), sizeof(array_t) + sizeof(uint32_t)*0x2, x_bdim);

  putchar(0x0a);
  raw = (uint8_t*)_x;
  for(int i=0; i<x_bdim; ++i)
    printf("%02x\n", raw[i]);

  putchar(0x0a);
  for(int i=0; i<0x2; ++i)
    printf("%08x\n", _x->data[i]);

  putchar(0x0a);
  raw = (uint8_t*)&x;
  for(int i=0; i<x_bdim; ++i)
    printf("%02x\n", raw[i]);

  putchar(0x0a);
  for(int i=0; i<0x2; ++i)
    printf("%08x\n", x.data[i]);
}

我是不是遗漏了什么,或者这是正确的吗?故事的主旨是我们不应该使用灵活的数组成员来尊重(指向)结构,因为我们只得到结构的“头”而不是完整的数组数据?

有点技术性 - 取消引用指针在这里用词不当。实际问题是关于使用 FAM 复制结构。

您的观察是正确的,如果您尝试使用 FMA 复制结构,FMA 部分将不会被复制。原因很简单 - 由于编译器不知道分配存储的大小,因此无法复制 FMA“尾巴”。

没错。编译器不知道数组的大小,并假设此类操作为 0,包括复制、return、分配和比较。实际上,灵活数组在结构之后。

GCC 的原型语法更明显。他们把它写成 uint32_t data[0];

C 2018 6.7.2.1 18 说:

… In most situations, the flexible array member is ignored…

并且 C 标准中没有任何内容说明在使用结构时使用灵活的数组成员(是值的一部分),如 array_t x = *_x; 中所示。因此,在此初始化中未复制数组成员的事实是由于标准中没有任何内容说明它已被复制。

该标准说明了关于灵活数组成员的两个值得注意的事情。下一句允许填充以满足灵活数组成员的对齐要求:

In particular, the size of the structure is as if the flexible array member were omitted except that it may have more trailing padding than the omission would imply.

后面的句子说访问该成员(与 x->data 一样)的行为就好像那里确实有一个数组,只要为其提供的内存允许就可以了。

因此最后一句允许您手动访问灵活数组成员中的数据,但第一句不需要编译器自动对它们执行任何操作。

这当然是因为编译器一般无法知道灵活数组成员的大小。其他答案(到目前为止)错误地说明了这一点;他们说编译器 不知道 大小。在显示的代码中,编译器当然可以看到大小,因此它缺乏知识并不是不复制灵活数组成员的原因。更准确地说,原因是编译器无法知道所有使用该结构的情况下的大小,因为内存预留可能不在当前正在编译的代码的视线范围内,因此一般规则是不处理灵活的数组成员作为基本结构的一部分,即使内存预留可见。