在 C 中将大类型指针转换为较小类型后禁止做什么

What is forbidden after pointer-casting a big type to a smaller type in C

假设我有一个更大的类型。

uint32_t big = 0x01234567;

(char*)&big指针转换后被解释成char类型怎么办?

  1. (char*)&big的地址(char*)&big转移到(char*&big)+1(char*&big)+2等是不是未定义的行为?
  2. 这是 shiftedit (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?)

  1. 我见过这样的代码。当我需要完成类似的任务时,这就是我通常做的。这是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,但这只是一个端点。那里没有定义的字节,你不应该使用那个地址来读或写任何东西。

  1. 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 的偏移量定义的。

  1. Is that an undefined behavior to both shift and edit (char*)&big+1?

允许使用指向char的指针读取和写入big中的字节。这是字符类型的特殊规则。通常,不应使用不相关的类型访问对象的字节。例如,无法使用 int 类型访问 float 对象。但是,字符类型是特殊的;您可以使用字符类型访问任何对象的字节。

但是,最好为此使用 unsigned char,因为它避免了符号值的复杂化。

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