gpt4 book ai didi

memcpy 可以用于类型双关吗?

转载 作者:太空狗 更新时间:2023-10-29 16:32:22 37 4
gpt4 key购买 nike

这是引用自 C11 标准:

6.5 Expressions
...

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 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 不能以这种方式用于类型双关:

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

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

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

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

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 位 有声明的类型。 我同意,但是,不幸的是,这无助于回答 memcpy 是否可以用于类型双关的问题,它只是让第 6 段与评估以上例子的有效性。

这是使用 memcpy 进行类型双关的另一种尝试,我相信它会在第 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"\n", d, bits);
}

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


编辑:一些答案指出读取陷阱表示可能会导致未定义的行为。这无关紧要,因为 C 标准明确排除了这种可能性:

7.20.1.1 Exact-width integer types

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

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 个值位且没有填充位,因此不存在任何陷阱表示。

最佳答案

有两种情况需要考虑:memcpy()进入一个已声明类型的对象,memcpy()进入一个没有的对象。

在第二种情况下,

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"\n", d, bits);

该行为确实未定义,因为p指向的对象的有效类型将变为double,并且访问有效类型double<的对象 尽管 uint64_t 类型的左值未定义。

另一方面,

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

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

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 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)此列表的目的是指定对象可以或不可以使用别名的情况。

所以 memcpy() 本身是明确定义的。

因为 uint64_t bits 有一个声明的类型,它保留了它的类型,即使它的对象表示是从 double 复制的。

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

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

关于memcpy 可以用于类型双关吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38601780/

37 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com