【发布时间】:2018-07-11 14:38:28
【问题描述】:
上下文:
我正在查看一些从 IO 描述符接收数据到字符缓冲区的代码,对其进行一些控制,然后使用接收到的缓冲区的一部分来填充结构,突然想知道是否可能涉及严格的别名规则违规.
这是一个简化版
#define BFSZ 1024
struct Elt {
int id;
...
};
unsigned char buffer[BFSZ];
int sz = read(fd, buffer, sizeof(buffer)); // correctness control omitted for brievety
// search the beginning of struct data in the buffer, and process crc control
unsigned char *addr = locate_and_valid(buffer, sz);
struct Elt elt;
memcpy(&elt, addr, sizeof(elt)); // populates the struct
// and use it
int id = elt.id;
...
到目前为止,一切都很好。如果缓冲区确实包含结构的有效表示 - 假设它是在同一平台上生成的,因此没有字节顺序或填充问题 - memcpy 调用已填充结构并且可以安全地使用它。
问题:
如果结构是动态分配的,则它没有声明的类型。让我们将最后几行替换为:
struct Elt *elt = malloc(sizeof(struct Element)); // no declared type here
memcpy(elt, addr, sizeof(*elt)); // populates the newly allocated memory and copies the effective type
// and use it
int id = elt->id; // strict aliasing rule violation?
...
C 语言草案 n1570 在 6.5 表达式 §6 中说明
访问其存储值的对象的有效类型是声明的类型 对象,如果有的话。87) 如果一个值通过 左值的类型不是字符类型,则左值的类型变为 该访问和不修改的后续访问的对象的有效类型 存储的值。 如果一个值被复制到一个没有声明类型的对象中 memcpy 或 memmove,或复制为字符类型的数组,则为有效类型 为该访问和后续访问不修改 value 是从中复制值的对象的有效类型(如果有的话)。
buffer 确实有一个有效的类型,甚至是一个声明的类型:它是一个unsigned char 的数组。这就是代码使用memcpy 而不是像这样的别名的原因:
struct Elt *elt = (struct Elt *) addr;
这确实是一个严格的别名规则违规(并且可能额外带来对齐问题)。但是如果memcpy 给elt 指向的区域提供了一个有效类型的无符号字符数组,那么一切都会丢失。
问题:
memcpy 从字符类型数组到没有声明类型的对象是否会给出有效的字符数组类型?
免责声明:
我知道它可以在所有常见编译器中正常工作而不会发出警告。我只想知道我对标准的理解是否正确
为了更好地展示我的问题,让我们考虑一个不同的结构 Elt2 与 sizeof(struct Elt2)sizeof(struct Elt),和
struct Elt2 actual_elt2 = {...};
对于静态或自动存储,我无法重用对象内存:
struct Elt elt;
struct Elt2 *elt2 = &elt;
memcpy(elt2, &actual_elt2, sizeof(*elt2));
elt2->member = ... // strict aliasing violation!
虽然对于动态来说很好(关于它的问题there):
struct Elt *elt = malloc(sizeof(*elt));
// use elt
...
struct Elt2 *elt2 = elt;
memcpy(elt2, &actual_elt2, sizeof(*elt2));
// ok, memory now have struct Elt2 effective type, and using elt would violate strict aliasing rule
elt2->member = ...; // fine
elt->id = ...; // strict aliasing rule violation!
从 char 数组复制有什么不同?
【问题讨论】:
-
struct Elt elt;和struct Elt *elt = malloc(sizeof *elt);的区别只是结构的内存位置。就是这样。无论结构的内存是如何分配的,或者它位于何处,这两个结构都同样有效。 -
@Someprogrammerdude:这就是我几十年来的想法,但是声明类型与有效类型的问题让我害怕......
-
您的编辑没有任何意义。
struct Elt2 *elt2 = elt;不是有效的 C。您的原始问题没有任何违反约束的野指针转换。我不知道你在问什么了。投票结束,因为不清楚。 -
@Lundin:你是对的,这是一个不同的问题。我问了there
-
@chux :感谢您的关注。帖子已编辑...
标签: c struct malloc language-lawyer memcpy