![](/img/trans.png)
[英]C Casting from pointer to uint32_t to a pointer to a union containing uint32_t
[英]c - casting uint8_t* to uint32_t* behaviour
我读过这个问题: 如何将 uint8* 转换为 uint32* 工作? 但我不确定给出的答案。
我是新手嵌入式 C 程序员,正在从事一个使用 GCC 的项目,我一直在重构代码块以减少内存使用。 一个例子是我将一些变量的数据类型从uint32_t
更改为更小的类型:
uint8_t colour;
uint16_t count;
uint16_t pixel;
func((uint32_t*)&colour);
func((uint32_t*)&count);
func((uint32_t*)&pixel);
其中func(uint32_t* ptr)
修改传递给它在从端口上接收的数据的值。 我遇到了一个问题,当启用-O1
、 -O2
、 -O3
或-Os
优化时,上述代码无法正确运行。 例如,当通信端口和函数分别接收到值1
5
1
时,为变量设置的值为0
0
1
。 未启用优化时,值设置正确。
如果我将数据类型改回uint32_t
则代码行为正确。 我不明白为什么我没有收到编译器的任何警告(我打开了额外的警告)。 发生这种情况的原因是否与字节序/对齐有关?
从uint8_t
指针向上转换为uint32_t
指针的陷阱是什么?
TLDR
执行诸如将uint8_t
的地址传递给期望uint32_t
的地址之类的操作可能会导致内存损坏、未知结果、微妙的错误以及简单易爆的代码。
细节
首先,如果函数被声明为
void func( uint32_t * arg );
并修改arg
指向的数据,将uint8_t
或uint16_t
的地址传递给它会导致未定义的行为和可能的数据损坏 - 如果它完全运行(继续阅读......)。 该函数将修改实际上不是传递给函数的指针所指对象的一部分的数据。
该函数希望能够访问uint32_t
的四个字节,但您只给了它一个uint8_t
字节的地址。 其他三个字节去哪儿了? 他们可能会踩到别的东西。
并且即使函数只读取内存而不修改它,你也不知道内存中的内容而不是实际对象中的内容,因此该函数的行为可能无法预测。 阅读甚至可能根本不起作用(继续阅读......)。
此外,转换uint8_t
的地址是严格的别名违规。 请参阅什么是严格的别名规则? . 总而言之,在 C 中,您不能安全地将对象称为它不是的对象,唯一的例外是您可以引用任何对象,就好像它是由适当数量的[signed|unsigned] char
字节组成的.
但是将uint8_t
地址转换为uint32 *
意味着您试图访问一组四个unsigned char
值(假设uint8_t
实际上是unsigned char
,现在几乎可以肯定是这样)作为单个uint32_t
对象,这是严格的别名违规,未定义的行为,并且不安全。
您因违反严格的别名规则而看到的症状可能很微妙,而且很难找到和修复。 有关一些恐怖故事,请参阅gcc、严格别名和恐怖故事。
此外,如果你将一个对象称为它不是的东西,你可能会违反6.3.2.3 Pointers ,C (C11) 标准的第 7 段:
指向对象类型的指针可以转换为指向不同对象类型的指针。 如果结果指针未针对引用类型正确对齐,则行为未定义。
无论有人告诉您有关基于 x86 的系统的内容,即使在 x86 上也不安全。
如果你听到有人说,“好吧,它有效,所以这一切都错了。”,好吧,他们是非常非常错误的。
他们只是没有观察到它失败了。
然而。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.