C 仅写入动态分配内存块的一部分是否设置了整个块的有效类型?

C 仅写入动态分配内存块的一部分是否设置了整个块的有效类型?,c,strict-aliasing,C,Strict Aliasing,编辑:我所说的“有效类型”在中提到。(感谢David C.Rankin在您的评论中提供此链接。) 读了一些书之后,我还没有完全理解C中关于有效类型和严格别名的规则。我已经在下面的代码中评论了我认为有效类型正在发生的事情。在本例中,请假设int和float的大小相同 void *memory = malloc(sizeof(int) + sizeof(float)); int *x = memory; // x points to an "object" with no effective

编辑:我所说的“有效类型”在中提到。(感谢David C.Rankin在您的评论中提供此链接。)

读了一些书之后,我还没有完全理解C中关于有效类型和严格别名的规则。我已经在下面的代码中评论了我认为有效类型正在发生的事情。在本例中,请假设int和float的大小相同

void *memory = malloc(sizeof(int) + sizeof(float));

int *x = memory;    // x points to an "object" with no effective type.
*x = 1;             // x points to an object with effective type int.
float *y = memory;  // y points to an object with effective type int.
++y;                // y points to an "object" with effective type ???
最后,y指向尚未写入的内存。所以,如果y指向没有有效类型的“对象”,对我来说是有意义的

另一方面,在动态分配的“对象”中写入了一个int,因此这个“对象”可能被解释为一个int数组。从这个角度来看,如果y指向一个有效类型为int的对象,对我来说是有意义的

考虑另一个例子:

void *memory = malloc(sizeof(short) + sizeof(float));

short *x = memory;  // x points to an "object" with no effective type.
*x = 1;             // x points to an object with effective type short.
++x;                // x points to an "object" with effective type ???
在这里,由于内存对齐,将x指向的对象想象为浮点似乎是不合理的。由于像这样的对齐问题,我可以理解为什么写入内存块的一部分可能会设置整个块的有效类型

如果这总是正确的,如果我理解的话,那么从技术上讲,分配一个巨大的内存块,然后在它的两端访问不同的数据类型,这将是一种未定义的行为


这确实是促使我研究有效类型的核心问题。我一直在使用我自己的记忆领域,但我不知道分配大块的记忆并将它们解释为连续排列的不同结构在技术上是否是错误的。它一直在实践中发挥作用。否则,在一个动态分配的内存块中实现多种类型存储的有效方法是什么(除了将它们全部放在一个结构或联合中)?

我仍在为你的问题的基础而挣扎,但还是这样

首先,问题从
sizeof(int)+sizeof(float)
开始。这是你不能信守的诺言!如果你声明说:

struct t
{
    int x;
    float y;
};
您是否可以确保始终
sizeof(struct t)==sizeof(int)+sizeof(float)
? 显然不能,因为这完全取决于您所在的特定平台的对齐限制。您可能没有为两者分配足够的存储空间。因此,如果您想连续存储int和float,并通过各自类型的指针直接访问它们,那么您需要将它们包装在结构中并使用该类型的大小

在您的情况下,因为您知道尺寸是相同的,所以您总是可以争辩说它是有效的,但标准并不能保证这一点

第二,

float *y = memory;  // y points to an object with effective type int.
++y;                // y points to an "object" with effective type ???
显然是未定义的行为(当您尝试取消引用y时),因为您无法确保x和y不存在别名。除非知道有相同类型的同构数组,否则无法执行指针算术。例外情况可能是char*,您可以使用它直接查看属于任何类型的内存。同样,如果大小和对齐方式相同,这可能会起作用,但语言不允许这样做

否则,实现以上存储的有效方法是什么 动态分配内存块中的一种类型(除 将它们全部放在一个结构或联合中)


将内存块视为char数组,一次读取或写入一个类型,使用memcpy以正确的有效类型在temp变量之间进行复制。将该变量用于任何算术运算。否则,您将依赖仅在硬件上可用的特殊fairy dust

任何试图理解C标准的人都应该阅读已发布的基本原理文档(例如,这是谷歌对谷歌“C99基本原理”的点击)

支持各种有趣的构造(这些构造使C变得非常有用)的能力一直是实现质量的问题,超出了标准的管辖范围。相反,QoI问题是由市场决定的。由于客户需要某些构造的编译器编写者可能会寻求满足客户的需求,而不考虑标准是否需要,因此没有必要对某些程序需要但其他程序不需要的构造提供标准授权支持,也没有任何理由担心制定明确解决所有角落案件的规则

您所询问的案例是标准的作者似乎没有考虑的众多案例之一。因此,最合理的解释是,虽然标准没有禁止实现以无意义的方式处理此类构造,但它并不打算特别邀请此类行为,更关心构造是否有用而不是它是否被授权的高质量实现应该支持它

有效的类型规则是基于对缺陷报告028的糟糕的书面响应,该响应旨在解决编译器是否给出以下内容的问题:

float test(float *p1, unsigned *p2)
{
  *p1 = 1.0f;
  *p2 = 0;
  return *p1;
}
应考虑到它可能被以下函数调用:

float test2(void)
{
  union { float f, unsigned u} uf;
  return test2(&uf.f, &uf.u);
}
回答正确地指出,不应要求编译器考虑这种可能性,但引用了无意义的推理:因为将
unsinged
写入联合对象并读取
int
的行为是实现定义的行为,因此,通过指针访问这些对象的行为是未定义的行为。使用指针不应产生与直接使用对象相同的实现定义行为,这一说法没有根据。这里的含义是,对于联合,没有完全定义行为的操作将调用UB

事实上,对博士028的正确回答应该是没有访问美国的一般许可