为啥 uint8_t 在分配给取消引用的 uint32_t 指针时使用了 4 个字节?

Posted

技术标签:

【中文标题】为啥 uint8_t 在分配给取消引用的 uint32_t 指针时使用了 4 个字节?【英文标题】:Why is uint8_t using up 4 bytes when assigned to a dereferenced uint32_t pointer?为什么 uint8_t 在分配给取消引用的 uint32_t 指针时使用了 4 个字节? 【发布时间】:2014-07-30 05:19:42 【问题描述】:

在将字节值分配给取消引用的 4 字节指针时,我的应用程序中有一个错误:

uint8_t value = 5;
uint8_t myArray [4] = 1,2,3,4;
uint32_t *myPointer = &myArray[0];

*myPointer = value; // myArray is now 5,0,0,0
//*(uint8_t*)myPointer = value; // works correctly, myArray: 5,2,3,4

在我看来,这是一个非常讨厌的、难以检测的错误,即使打开了所有警告,编译期间也不会出现警告。为什么编译器不处理这个问题,因为value 的大小很明显?此外,尽管指针大小的概念与指向更高地址的能力相关,但与实际分配的内存量无关。

【问题讨论】:

所以int i = 0x00FFFFFF; i = 'c'; 不应该分配给i 的高位字节?顺便说一句,不能保证myArrayuint32_t 正确对齐。 您将 8 位无符号数分配给 32 位无符号数。该值被提升到更高的级别并被保存。这是一个问题,因为....?尝试 other 方向,您的编译器 应该 给您一个潜在的数据丢失警告。但实际上这里没有什么可看的。您对指针的概念是错误的。与指针关联的 type 指示存储要求;这不仅仅是一些地址。 我也很惊讶你的编译器没有因为uint32_t *myPointer = &myArray[0];而对你大喊大叫。 不确定您使用的是什么编译器,但error: cannot convert 'uint8_t* aka unsigned char*' to 'uint32_t* aka unsigned int*' in initialization 【参考方案1】:

编译器会在你犯错时警告你:

uint32_t *myPointer = &myArray[0];

(...)

und.c:8:11: warning: incompatible pointer types initializing 'uint32_t *'
      (aka 'unsigned int *') with an expression of type 'uint8_t *'
      (aka 'unsigned char *') [-Wincompatible-pointer-types]

您选择将值重新解释为整数,这会导致未定义的行为。在此之后,您尝试重新解释第二次,忽略了您分配给整数的事实。但是现在您陷入了 C 弱类型规则:您可以从 uint8_t 隐式转换为 uint32_t 而不会发出警告。如果你想要它,C 会做的。

【讨论】:

【参考方案2】:

为什么编译器不处理这个

因为你得到你所写的!

如果您为 32 位存储分配一个值,您将访问 32 位存储。如果你说,那就是你写的:

uint32_t *myPointer

如果您想要一个 8 位值,请使用指向 8 位值的指针!

编译器会处理所有这些!但是您不能将 8 位值写入指向 32 位值的指针。这导致从 8 位值转换为 32 位值!

 uint8_t val;
 uint32_t *myPointer;
 *myPointer = val;

结果:

 *myPointer = ( uint32_t ) val;

【讨论】:

【参考方案3】:

赋值运算符赋值给左边,值在右边。左侧的先前值被忽略。事实上,它甚至没有从内存中检索出来。

例子:

int a = 0x12345678;
a = 7;
cout << a << "\n";   // outputs 7

我根本看不出这是一个“讨厌的错误”。

如果您在谈论类类型,那么赋值运算符可能会被重载以使左侧部分保持不变(尽管这可能是一个糟糕的设计)。但是对于原始类型,分配甚至不会检索被分配的内存位置的内容;它只是写入新内容。

如果右侧操作数的类型不匹配,则将其转换为左侧操作数的类型。例如:

int a = 0x12345678;
double d = 6.5;

a = d;

cout << a << "\n";     // outputs 6

我不确定在这种情况下应该输出什么。

在某些编程语言中没有隐式转换,您必须编写 a = (int)d;

无论好坏,C 和 C++ 都不属于这些语言。并且您可以在算术类型之间进行隐式转换。一些编译器对潜在的缩小转换发出警告,但没有警告“扩大转换”。

【讨论】:

【参考方案4】:

这是值如何存储在内存中以及发生隐式转换的结果。有 99.9% 的可能性是,您使用的是小端处理器,如 Intel 或 ARM,这意味着多字节类型(如 32 位整数)中的字节以相反的顺序存储。 32 位 int 的存储方式是相关的,因为 8 位 value 在分配给 32 位整数 *myPointer 时会隐式转换为 32 位 int。当整数 5 存储在 little endian 处理器上时,它会反转字节顺序。因此,最低有效字节 5 存储在 myArray[0] 中,而更高有效字节 0 分配给 myArray[1]myArray[2]myArray[3]

【讨论】:

以上是关于为啥 uint8_t 在分配给取消引用的 uint32_t 指针时使用了 4 个字节?的主要内容,如果未能解决你的问题,请参考以下文章

将 uint8_t 数组转换为 C 中的 uint16_t 值

Emscripten将uint8_t数组传递给javascript?

将 uint8_t 数组传递给方法

将指向 uint16_t 的指针传递给需要 C 中 uint8_t[] 数组的子例程 - 如何?

将 int16_t 变量转换为 uint8_t 以传递给函数

使用新的内存分配失败