为啥 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
的高位字节?顺便说一句,不能保证myArray
与uint32_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?
将指向 uint16_t 的指针传递给需要 C 中 uint8_t[] 数组的子例程 - 如何?