在 C 中将大类型指针转换为较小类型后禁止做什么
What is forbidden after pointer-casting a big type to a smaller type in C
假设我有一个更大的类型。
uint32_t big = 0x01234567;
那(char*)&big
指针转换后被解释成char类型怎么办?
- 将
(char*)&big
的地址(char*)&big
转移到(char*&big)+1
、(char*&big)+2
等是不是未定义的行为?
- 这是 shift 和 edit
(char*)&big+1
的未定义行为吗?就像下面的例子。我认为这个例子应该是一个未定义的行为,因为在转换为 (char*)
之后,我们将我们的视线限制在一个 char
类型的指针上,我们不应该访问,甚至改变这个范围之外的值。
uint32_t big = 0x01234567;
*((char*)&big + 1) = 0xff;
printf("%02x\n\n\n", *((char*)&big+1));
printf("%02x\n\n\n", big);
(这通过了我的 Visual C++ 编译器。顺便说一句,我想问一个分支问题,为什么在这个例子中第一个 printf
给出 ffffffff
?不应该是 ff
?)
- 我见过这样的代码。当我需要完成类似的任务时,这就是我通常做的。这是UB还是不是?为什么或者为什么不?实现此目标的标准方法是什么?
uint8_t catcher[8] = { 0 };
uint64_t big = 0x1234567812345678;
memcpy(catcher, (uint8_t*)&big, sizeof(uint64_t));
对于 variadic functions (like printf
) all arguments undergoes default argument promotion which promotes 较小的整数类型为 int
。
如果较小的类型有符号,此转换将包括 符号扩展,因此该值保持其值。
因此,如果 char
是一个带符号类型(由实现定义)且值为 -1
,那么它将被提升为 int
值 -1
。这就是你所看到的。
如果要打印较小的字体,首先需要转换为正确的类型 (unsigned char
),然后使用正确的格式(如 %hhx
用于打印 unsigned char
值)。
Then what can I do for (char*)&big
, the pointer interpreted as a char type after casting?
如果 char
是八位,这是大多数现代 C 实现中的,那么 uint32_t
big
中有四个字节,您可以对地址从 (char *) &big + 0
到 (char *) &big + 4
。您还可以读取和写入从 (char *) &big + 0
到 (char *) &big + 3
的字节,这些字节将访问 big
表示中的各个字节。尽管算术被定义为最多 (char *) &big + 4
,但这只是一个端点。那里没有定义的字节,你不应该使用那个地址来读或写任何东西。
- Is that an undefined behavior to shift the address of
(char*)&big
to (char*&big)+1
, (char*&big)+2
, etc.?
这些是加法,不是移位,语法是 (char *) &big + 1
,而不是 (char*&big)+1
。算术是为从 +0 到 +4 的偏移量定义的。
- Is that an undefined behavior to both shift and edit
(char*)&big+1
?
允许使用指向char
的指针读取和写入big
中的字节。这是字符类型的特殊规则。通常,不应使用不相关的类型访问对象的字节。例如,无法使用 int
类型访问 float
对象。但是,字符类型是特殊的;您可以使用字符类型访问任何对象的字节。
但是,最好为此使用 unsigned char
,因为它避免了符号值的复杂化。
- I have seen a code like this.
允许使用memcpy
读取或写入对象的字节。 memcpy
被定义为像复制字符一样工作。
请注意,虽然访问对象的字节是由 C 标准定义的,但字节如何表示值部分是实现定义的。不同的 C 实现可能对对象中的字节使用不同的顺序,并且可能存在其他差异。
By the way, I want to ask a forked question on that why in this example the first printf
gives ffffffff
? Shouldn't it be ff
?
在您的 C 实现中,char
是有符号的,可以表示从 −128 到 +127 的值。在 *((char*)&big + 1) = 0xff;
中,0xff
是 255,太大而不适合 char
。它以实现定义的方式转换为 char
值。您的 C 实现将其转换为 −1。 (-1 的八位二进制补码表示,位 11111111,使用与 255 的二进制表示相同的位,同样是位 11111111。)
然后 printf("%02x\n\n\n", *((char*)&big+1));
将此值 -1 传递给 printf
。由于它是一个 char
,它被提升为 int
以传递给 printf
。这会产生相同的值 −1,但它有 32 位,11111111111111111111111111111111。然后您传递的是 int
,但 printf
期望 unsigned int
对应 %02x
。此行为未由 C 标准定义,但您的 C 实现读取 32 位,就好像它们是 unsigned int
。作为 unsigned int
,32 位 111111111111111111111111111111111 表示值 4,294,967,295 或 0xffffffff
,因此这就是 printf
打印的内容。
您可以使用 printf("%02hhx\n\n\n", * ((unsigned char *) &big + 1));
打印正确的值。作为 unsigned char
,位 11111111 表示 255 或 0xff
,将其转换为 int
会产生 255 或 0x000000ff
。
假设我有一个更大的类型。
uint32_t big = 0x01234567;
那(char*)&big
指针转换后被解释成char类型怎么办?
- 将
(char*)&big
的地址(char*)&big
转移到(char*&big)+1
、(char*&big)+2
等是不是未定义的行为? - 这是 shift 和 edit
(char*)&big+1
的未定义行为吗?就像下面的例子。我认为这个例子应该是一个未定义的行为,因为在转换为(char*)
之后,我们将我们的视线限制在一个char
类型的指针上,我们不应该访问,甚至改变这个范围之外的值。
uint32_t big = 0x01234567;
*((char*)&big + 1) = 0xff;
printf("%02x\n\n\n", *((char*)&big+1));
printf("%02x\n\n\n", big);
(这通过了我的 Visual C++ 编译器。顺便说一句,我想问一个分支问题,为什么在这个例子中第一个 printf
给出 ffffffff
?不应该是 ff
?)
- 我见过这样的代码。当我需要完成类似的任务时,这就是我通常做的。这是UB还是不是?为什么或者为什么不?实现此目标的标准方法是什么?
uint8_t catcher[8] = { 0 };
uint64_t big = 0x1234567812345678;
memcpy(catcher, (uint8_t*)&big, sizeof(uint64_t));
对于 variadic functions (like printf
) all arguments undergoes default argument promotion which promotes 较小的整数类型为 int
。
如果较小的类型有符号,此转换将包括 符号扩展,因此该值保持其值。
因此,如果 char
是一个带符号类型(由实现定义)且值为 -1
,那么它将被提升为 int
值 -1
。这就是你所看到的。
如果要打印较小的字体,首先需要转换为正确的类型 (unsigned char
),然后使用正确的格式(如 %hhx
用于打印 unsigned char
值)。
Then what can I do for
(char*)&big
, the pointer interpreted as a char type after casting?
如果 char
是八位,这是大多数现代 C 实现中的,那么 uint32_t
big
中有四个字节,您可以对地址从 (char *) &big + 0
到 (char *) &big + 4
。您还可以读取和写入从 (char *) &big + 0
到 (char *) &big + 3
的字节,这些字节将访问 big
表示中的各个字节。尽管算术被定义为最多 (char *) &big + 4
,但这只是一个端点。那里没有定义的字节,你不应该使用那个地址来读或写任何东西。
- Is that an undefined behavior to shift the address of
(char*)&big
to(char*&big)+1
,(char*&big)+2
, etc.?
这些是加法,不是移位,语法是 (char *) &big + 1
,而不是 (char*&big)+1
。算术是为从 +0 到 +4 的偏移量定义的。
- Is that an undefined behavior to both shift and edit
(char*)&big+1
?
允许使用指向char
的指针读取和写入big
中的字节。这是字符类型的特殊规则。通常,不应使用不相关的类型访问对象的字节。例如,无法使用 int
类型访问 float
对象。但是,字符类型是特殊的;您可以使用字符类型访问任何对象的字节。
但是,最好为此使用 unsigned char
,因为它避免了符号值的复杂化。
- I have seen a code like this.
允许使用memcpy
读取或写入对象的字节。 memcpy
被定义为像复制字符一样工作。
请注意,虽然访问对象的字节是由 C 标准定义的,但字节如何表示值部分是实现定义的。不同的 C 实现可能对对象中的字节使用不同的顺序,并且可能存在其他差异。
By the way, I want to ask a forked question on that why in this example the first
printf
givesffffffff
? Shouldn't it beff
?
在您的 C 实现中,char
是有符号的,可以表示从 −128 到 +127 的值。在 *((char*)&big + 1) = 0xff;
中,0xff
是 255,太大而不适合 char
。它以实现定义的方式转换为 char
值。您的 C 实现将其转换为 −1。 (-1 的八位二进制补码表示,位 11111111,使用与 255 的二进制表示相同的位,同样是位 11111111。)
然后 printf("%02x\n\n\n", *((char*)&big+1));
将此值 -1 传递给 printf
。由于它是一个 char
,它被提升为 int
以传递给 printf
。这会产生相同的值 −1,但它有 32 位,11111111111111111111111111111111。然后您传递的是 int
,但 printf
期望 unsigned int
对应 %02x
。此行为未由 C 标准定义,但您的 C 实现读取 32 位,就好像它们是 unsigned int
。作为 unsigned int
,32 位 111111111111111111111111111111111 表示值 4,294,967,295 或 0xffffffff
,因此这就是 printf
打印的内容。
您可以使用 printf("%02hhx\n\n\n", * ((unsigned char *) &big + 1));
打印正确的值。作为 unsigned char
,位 11111111 表示 255 或 0xff
,将其转换为 int
会产生 255 或 0x000000ff
。