TLDR
做一些类似传递地址的事情uint8_t
到某个期待 a 地址的东西uint32_t
可能会导致内存损坏、结果未知、微妙的错误和直接崩溃的代码。
DETAILS
首先,如果函数声明为
void func( uint32_t * arg );
并修改数据arg
指向,传递给它一个地址uint8_t
or a uint16_t
将导致未定义的行为 https://en.wikipedia.org/wiki/Undefined_behavior以及可能的数据损坏 - 如果它运行的话(继续阅读...)。该函数将修改实际上不属于传递给函数的指针所引用的对象的数据。
该函数期望能够访问四个字节uint32_t
但你只给了它一个地址uint8_t
字节。其他三个字节去哪里了?他们可能会踩到其他东西。
即使函数只读取内存而不修改它,您也不知道内存中的内容而不是实际对象中的内容,因此函数的行为可能会不可预测。而且阅读可能根本不起作用(继续阅读......)。
此外,投射 a 的地址uint8_t
是严格的别名违规。看严格的别名规则是什么? https://stackoverflow.com/questions/98650/what-is-the-strict-aliasing-rule。总而言之,在 C 中,你不能安全地引用一个对象,因为它不是,唯一的例外是你可以引用任何对象,就好像它是由适当数量的对象组成的一样。[signed|unsigned] char
bytes.
但铸造一个uint8_t
地址uint32 *
意味着您正在尝试访问一组四个unsigned char
值(假设uint8_t
实际上是unsigned char
,这在今天几乎肯定是正确的)作为一个单一的uint32_t
对象,这是严格的别名违规、未定义的行为,而且不安全。
您因违反严格的别名规则而看到的症状可能很微妙,而且很难发现和修复。看gcc、严格别名和恐怖故事 https://stackoverflow.com/questions/2958633/gcc-strict-aliasing-and-horror-stories对于一些恐怖故事。
此外,如果您将一个对象称为它不是的东西,则可能会发生冲突6.3.2.3 指针,C(C11)标准第7款 https://port70.net/~nsz/c/c11/n1570.html#6.3.2.3p7:
指向对象类型的指针可以转换为指向不同对象类型的指针。如果生成的指针未针对引用类型正确对齐,则行为未定义。
即使在 x86 上也不安全,无论别人如何告诉您有关基于 x86 的系统的信息。 https://stackoverflow.com/questions/46790550/c-undefined-behavior-strict-aliasing-rule-or-incorrect-alignment/46790815#46790815
如果你听到有人说,“好吧,它有效,所以那都是错的。”,好吧,他们是非常非常错误的。
他们只是没有观察到它失败了。
Yet.