将其存储到动态存储结构是否安全? [英] Is it safe to memcpy to a dynamic storage struct?

查看:87
本文介绍了将其存储到动态存储结构是否安全?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

上下文:

我正在查看一些将IO描述符中的数据接收到字符缓冲区中的代码,对其进行控制,然后使用接收到的缓冲区的一部分来填充结构,然后突然想知道是否可能涉及到严格的别名规则违规

I was reviewing some code that receives data from an IO descriptor into a character buffer, does some control on it and then use part of the received buffer to populate a struct, and suddenly wondered whether a strict aliasing rule violation could be involved.

这是简化版

#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调用已填充该结构并且可以安全地使用它.

So far, so good. Provide the buffer did contain a valid representation of the struct - say it has been produced on same platform, so without endianness or padding problem - the memcpy call has populated the struct and it can safely be used.

问题:

如果该结构是动态分配的,则它没有声明的类型.让我们将最后几行替换为:

If the struct is dynamically allocated, it has no declared type. Let us replace last lines with:

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?
...

在6.5表达式§6中说C语言的n1570草案

Draft n1570 for C language says in 6.5 Expressions §6

访问其存储值的对象的有效类型是声明的类型. 87)如果一个值通过一个值存储到一个没有声明类型的对象中 具有非字符类型的左值,则左值的类型变为 该访问以及未修改的后续访问的对象的有效类型 储值. 如果使用以下命令将值复制到没有声明类型的对象中 memcpy或memmove,或者被复制为字符类型的数组,然后是有效类型 该访问以及未修改该对象的后续访问的已修改对象的 值是从中复制值的对象的有效类型(如果有的话).

The effective type of an object for an access to its stored value is the declared type of the object, if any.87) If a value is stored into an object having no declared type through an lvalue having a type that is not a character type, then the type of the lvalue becomes the effective type of the object for that access and for subsequent accesses that do not modify the stored value. If a value is copied into an object having no declared type using memcpy or memmove, or is copied as an array of character type, then the effective type of the modified object for that access and for subsequent accesses that do not modify the value is the effective type of the object from which the value is copied, if it has one.

buffer确实具有有效类型,甚至具有声明的类型:它是unsigned char的数组.这就是为什么代码使用memcpy而不是像这样的纯别名的原因:

buffer does have an effective type and even a declared type: it is an array of unsigned char. That is the reason why the code uses a memcpy instead of a mere aliasing like:

struct Elt *elt = (struct Elt *) addr;

的确会严格违反别名规则(并且可能还会带来对齐问题).但是,如果memcpyelt所指向的区域提供了有效类型的无符号char数组,则所有内容都会丢失.

which would indeed be a strict aliasing rule violation (and could additionaly come with alignment problems). But if memcpy has given an effective type of an unsigned char array to the zone pointed by elt, everything is lost.

问题:

从字符类型数组到没有声明类型的对象的memcpy是否给出字符数组的有效类型?

Does memcpy from an array of character type to a object with no declared type give an effective type of array of character?

免责声明:

我知道它对所有常见的编译器都不会发出警告.我只想知道我对标准的理解是否正确

I know that it works without a warning with all common compilers. I just want to know whether my understanding of standard is correct

为了更好地展示我的问题,让我们考虑使用sizeof(struct Elt2)< = sizeof(struct Elt)

In order to better show my problem, let us considere a different structure Elt2 with sizeof(struct Elt2)<= sizeof(struct Elt), and

struct Elt2 actual_elt2 = {...};

对于静态或自动存储,我无法重用对象内存:

For static or automatic storage, I cannot reuse object memory:

struct Elt elt;
struct Elt2 *elt2 = &elt;
memcpy(elt2, &actual_elt2, sizeof(*elt2));
elt2->member = ...           // strict aliasing violation!

尽管它适合动态应用程序(关于它的问题):

While it is fine for dynamic one (question about it 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数组进行复制有什么不同?

What could make copying from a char array different?

推荐答案

代码很好,没有严格的别名冲突.指向的数据具有有效的类型,因此粗体引用的文本不适用.这里适用的是您遗漏的部分,最后一句6.5/6:

The code is fine, no strict aliasing violation. The pointed-at data has an effective type, so the bold cited text does not apply. What applies here is the part you left out, last sentence of 6.5/6:

对于没有声明类型的对象的所有其他访问,该对象的有效类型就是用于访问的左值的类型.

For all other accesses to an object having no declared type, the effective type of the object is simply the type of the lvalue used for the access.

因此,指向对象的有效类型为struct Elt.返回的malloc指针确实的确指向了没有delcared类型的对象,但是一旦指向它,有效类型便成为struct指针的类型.否则,C程序将根本无法使用malloc.

So the effective type of the pointed-at object becomes struct Elt. The returned pointer of malloc does indeed point to an object with no delcared type, but as soon as you point at it, the effective type becomes that of the struct pointer. Otherwise C programs would not be able to use malloc at all.

使代码安全的原因还在于,您将数据复制到该结构中.如果您只是分配了一个struct Elt*指向与addr相同的内存位置,那么您将遇到严格的别名冲突和UB.

What makes the code safe is also that you are copying data into that struct. Had you instead just assigned a struct Elt* to point at the same memory location as addr, then you would have a strict aliasing violation and UB.

这篇关于将其存储到动态存储结构是否安全?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆