memcpy 可以用于类型双关语吗? [英] Can memcpy be used for type punning?

查看:13
本文介绍了memcpy 可以用于类型双关语吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这是来自 C11 标准的引用:

This is a quote from the C11 Standard:

6.5 表达式
...

6 用于访问其存储值的对象的有效类型是对象的声明类型(如果有).如果通过具有非字符类型类型的左值将值存储到没有声明类型的对象中,则左值的类型将成为该访问的对象的有效类型以及不修改该类型的后续访问储值.如果使用 memcpymemmove 将值复制到没有声明类型的对象中,或者复制为字符类型的数组,则修改对象的有效类型为该访问和不修改该值的后续访问是从中复制该值的对象的有效类型(如果它有一个).对于没有声明类型的对象的所有其他访问,对象的有效类型只是用于访问的左值的类型.

6 The effective type of an object for an access to its stored value is the declared type of the object, if any. 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. 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.

7 对象的存储值只能由具有以下类型之一的左值表达式访问:

7 An object shall have its stored value accessed only by an lvalue expression that has one of the following types:

——与对象的有效类型兼容的类型,
— 与对象的有效类型兼容的类型的限定版本,
——对象的有效类型对应的有符号或无符号类型,
— 一种类型,是有符号或无符号类型,对应于对象的有效类型的限定版本,
— 在其成员中包含上述类型之一的聚合或联合类型(递归地,包括子聚合或包含联合的成员),或
— 一种字符类型.

— a type compatible with the effective type of the object,
— a qualified version of a type compatible with the effective type of the object,
— a type that is the signed or unsigned type corresponding to the effective type of the object,
— a type that is the signed or unsigned type corresponding to a qualified version of the effective type of the object,
— an aggregate or union type that includes one of the aforementioned types among its members (including, recursively, a member of a subaggregate or contained union), or
— a character type.

这是否意味着 memcpy 不能以这种方式用于类型双关:

Does this imply that memcpy cannot be used for type punning this way:

double d = 1234.5678;
uint64_t bits;
memcpy(&bits, &d, sizeof bits);
printf("the representation of %g is %08"PRIX64"
", d, bits);

为什么它不会给出与以下相同的输出:

Why would it not give the same output as:

union { double d; uint64_t i; } u;
u.d = 1234.5678;
printf("the representation of %g is %08"PRIX64"
", d, u.i);

如果我使用字符类型的 memcpy 版本会怎样:

What if I use my version of memcpy using character types:

void *my_memcpy(void *dst, const void *src, size_t n) {
    unsigned char *d = dst;
    const unsigned char *s = src;
    for (size_t i = 0; i < n; i++) { d[i] = s[i]; }
    return dst;
}


EOF 评论说 第 6 段中关于 memcpy() 的部分不适用于这种情况,因为 uint64_t bits 有一个声明的类型. 我同意,但不幸的是,这无助于回答 memcpy 是否可以用于类型双关语的问题,它只是使第 6 段与评估上述例子的有效性.


EOF commented that The part about memcpy() in paragraph 6 doesn't apply in this situation, since uint64_t bits has a declared type. I agree, but, unfortunately, this does not help answer the question whether memcpy can be used for type punning, it just makes paragraph 6 irrelevant to assess the validity of the above examples.

这里是使用 memcpy 进行类型双关的另一种尝试,我相信第 6 段会介绍:

Here here is another attempt at type punning with memcpy that I believe would be covered by paragraph 6:

double d = 1234.5678;
void *p = malloc(sizeof(double));
if (p != NULL) {
    uint64_t *pbits = memcpy(p, &d, sizeof(double));
    uint64_t bits = *pbits;
    printf("the representation of %g is %08"PRIX64"
", d, bits);
}

假设 sizeof(double) == sizeof(uint64_t),上述代码是否在第 6 段和第 7 段中定义了行为?

Assuming sizeof(double) == sizeof(uint64_t), Does the above code have defined behavior under paragraph 6 and 7?

一些答案​​指出读取陷阱表示可能会导致未定义的行为.这不相关,因为 C 标准明确排除了这种可能性:

Some answers point to the potential for undefined behavior coming from reading a trap representation. This is not relevant as the C Standard explicitly excludes this possibility:

7.20.1.1 精确宽度整数类型

1 typedef 名称 intN_t 指定宽度为 N 的有符号整数类型,无填充位, 和一个二进制补码表示.因此,int8_t 表示这种有符号整数类型,宽度正好为 8 位.

1 The typedef name intN_t designates a signed integer type with width N, no padding bits, and a two’s complement representation. Thus, int8_t denotes such a signed integer type with a width of exactly 8 bits.

2 typedef 名称 uintN_t 指定宽度为 N 且无填充位的无符号整数类型.因此,uint24_t 表示这样一种无符号整数类型,其宽度正好为 24 位.

2 The typedef name uintN_t designates an unsigned integer type with width N and no padding bits. Thus, uint24_t denotes such an unsigned integer type with a width of exactly 24 bits.

这些类型是可选的.但是,如果实现提供了宽度为 8、16、32 或 64 位的整数类型,没有填充位,并且(对于有符号类型)具有二进制补码表示,则它应定义相应的 typedef 名称.

These types are optional. However, if an implementation provides integer types with widths of 8, 16, 32, or 64 bits, no padding bits, and (for the signed types) that have a two’s complement representation, it shall define the corresponding typedef names.

类型 uint64_t 正好有 64 个值位并且没有填充位,因此不能有任何陷阱表示.

Type uint64_t has exactly 64 value bits and no padding bits, thus there cannot be any trap representations.

推荐答案

有两种情况需要考虑: memcpy()插入一个声明类型的对象, 和 memcpy()ing 到一个没有的对象.

There are two cases to consider: memcpy()ing into an object that has a declared type, and memcpy()ing into an object that does not.

第二种情况,

double d = 1234.5678;
void *p = malloc(sizeof(double));
assert(p);
uint64_t *pbits = memcpy(p, &d, sizeof(double));
uint64_t bits = *pbits;
printf("the representation of %g is %08"PRIX64"
", d, bits);

行为确实是未定义的,因为p指向的对象的有效类型会变成double,并且访问有效类型double<的对象/code> 虽然 uint64_t 类型的左值未定义.

The behavior is indeed undefined, since the effective type of the object pointed to by p will become double, and accessing an object of effective type double though an lvalue of type uint64_t is undefined.

另一方面,

double d = 1234.5678;
uint64_t bits;
memcpy(&bits, &d, sizeof bits);
printf("the representation of %g is %08"PRIX64"
", d, bits);

不是未定义.C11草案标准n1570:

is not undefined. C11 draft standard n1570:

7.24.1 字符串函数约定
3 对于本条中的所有功能,每个字符都应被解释为具有类型unsigned char (因此每个可能的对象表示都是有效且具有不同的值).

7.24.1 String function conventions
3 For all functions in this subclause, each character shall be interpreted as if it had the type unsigned char (and therefore every possible object representation is valid and has a different value).

还有

6.5 表达式
7 对象的存储值只能由具有以下类型之一的左值表达式访问:88)

6.5 Expressions
7 An object shall have its stored value accessed only by an lvalue expression that has one of the following types: 88)

——与对象的有效类型兼容的类型,
— 与对象的有效类型兼容的类型的限定版本,
— 对应于对象有效类型的有符号或无符号类型,
— 一种类型,它是有符号或无符号类型,对应于对象的有效类型,
— 包含上述类型之一的聚合或联合类型在其成员之间(递归地包括子聚合或包含联合的成员),或
— 一种字符类型.

— a type compatible with the effective type of the object,
— a qualified version of a type compatible with the effective type of the object,
— a type that is the signed or unsigned type corresponding to the effective type of the object,
— a type that is the signed or unsigned type corresponding to a qualified version of the effective type of the object,
— an aggregate or union type that includes one of the aforementioned types among its members (including, recursively, a member of a subaggregate or contained union), or
— a character type.

脚注 88)此列表的目的是指定对象可能会或可能不会被别名的情况.

Footnote 88) The intent of this list is to specify those circumstances in which an object may or may not be aliased.

所以 memcpy() 本身是很好定义的.

So the memcpy() itself is well-defined.

由于 uint64_t bits 有一个声明的类型,即使它的对象表示是从 double 复制的,它也会保留它的类型.

Since uint64_t bits has a declared type, it retains its type even though its object representation was copied from a double.

正如 chqrlie 所指出的,uint64_t 不能有陷阱表示,因此在 memcpy() 之后访问 bitsnot 未定义,提供 sizeof(uint64_t) == sizeof(double).但是,bits 将取决于实现(例如由于字节序).

As chqrlie points out, uint64_t cannot have trap representations, so accessing bits after the memcpy() is not undefined, provided sizeof(uint64_t) == sizeof(double). However, the value of bits will be implementation-dependent (for example due to endianness).

结论:memcpy() 可以用于类型双关,前提是memcpy()的目的地 确实有一个声明的类型,即不是由 [m/c/re]alloc() 或等价物分配的.

Conclusion: memcpy() can be used for type-punning, provided that the destination of the memcpy() does have a declared type, i.e. is not allocated by [m/c/re]alloc() or equivalent.

这篇关于memcpy 可以用于类型双关语吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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