C语言中,将uint8_t指针转换为uint32_t指针会发生什么?

3
我已经阅读了这个问题:How does casting uint8* to uint32* work?,但我不确定所给答案的正确性。
我是一个新手嵌入式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) 函数会修改从从slave端口传入的数据,并将其存储在指针所指向的位置。但是当启用 -O1, -O2, -O3-Os 优化选项时,上述代码不会按预期运行。例如,在串行通信端口上接收到值为 1, 5, 1 时,变量被设置的值分别为 0, 0, 1。而在未启用任何优化选项时,变量的值被正确地设置。

如果将数据类型更改回uint32_t,则代码将正常工作。我不明白为什么编译器没有发出任何警告(我已经开启了额外的警告)。这种问题是否与字节顺序和对齐方式有关?

uint8_t 指针向 uint32_t 指针升级的风险是什么?


假设 func 在任何时候都会取消引用指针,则其行为是未定义的。 - Christian Gibbons
查看安德鲁的答案以了解为什么你不能按照你定义函数的方式来做这个。但是,我建议干脆不要去做它。_除非_你有比这更多的数据,否则你不会节省多少。你总共有多少内存,你估计/期望能节省多少?你的内存现在有多满?(例如)如果你有:uint32_t array[1000];并且_可以_转换为uint8_t array[1000];。这是否值得代码复杂化? - Craig Estey
@CraigEstey 我之前不知道这是未定义行为,因为即使打开了-fstrict-aliasing-Wstrict-aliasing,我也没有收到编译器警告,但现在我会进行更改。我更改代码的原因是尽可能地节省字节(该项目已经使用了7669/8192总内存)。 - Nubcake
我刚刚对一个嵌入式系统进行了重构,所以我很熟悉这个问题空间。敢问这是为了哪个[古老/恶劣]的处理器(例如 PIC18F)?它似乎已经有几十年历史了。这必须在传统硬件上工作还是用一些更大内存的新CPU来制作未来产品?CPU制造商停止生产芯片前还有多久?树莓派售价$25-35,带有千兆字节的RAM。因此我们至少应该有64KB RAM,这是1985年左右就已经存在的;-) - Craig Estey
@CraigEstey 我们在这个特定的项目中使用ATxmega128A1U ;) 实际上,决定我们使用什么并不取决于我哈哈 ;) - Nubcake
我熟悉这个处理器系列的各种变体——Microchip又来了 :-( 如果这是为了一个新产品,选择这个芯片就是纯粹的医疗事故。有许多等效芯片具有更多功能。即使按照Microchip的低性能标准,这也是性能不足的。 - Craig Estey
1个回答

4

TLDR

将一个 uint8_t 的地址传递给期望 uint32_t 地址的函数可能导致内存损坏、未知结果、难以察觉的错误和代码崩溃。

DETAILS

首先,如果该函数被声明为

void func( uint32_t * arg );

而传递一个指向uint8_tuint16_t的地址将导致未定义行为,很可能会导致数据损坏——如果它能运行的话(继续阅读...)。该函数将修改指针传递给函数所引用对象之外的数据。
该函数期望访问uint32_t的四个字节,但您只提供了单个uint8_t字节的地址。其他三个字节去哪了?它们可能会破坏其他东西。
即使该函数仅读取内存而不修改它,您也不知道实际对象之外的内存中有什么,因此该函数可能表现得不可预测。并且读取甚至可能根本无法工作(再次继续阅读...)。
此外,将 uint8_t 的地址转换是一个严格的别名违规操作。请参见 什么是严格别名规则?。总结一下,在 C 语言中,你不能安全地将对象引用为它不是的东西,唯一的例外是你可以将任何对象视为由适当数量的 [signed|unsigned] char 字节组成。
但是,将 uint8_t 地址转换为 uint32 * 意味着你正在尝试访问一组四个 unsigned char 值(假设 uint8_t 实际上是 unsigned char,这几乎肯定是正确的现在),作为单个 uint32_t 对象,这是一个严格的别名违规、未定义行为,不安全。
你违反严格别名规则所看到的症状可能很微妙,真的很难找到和修复。请参见gcc、strict-aliasing 和 horror stories以了解一些恐怖故事。
此外,如果您将对象引用为其本身不是的东西,则可能会违反C(C11)标准6.3.2.3 指针,第7段

对象类型的指针可以转换为不同对象类型的指针。如果结果指针未正确对齐以引用类型,则行为是未定义的。

即使在基于x86的系统上也不安全,无论有人告诉您什么。 如果你听到有人说,“它能工作,所以一切都没问题”,那么他们非常、非常地错误。他们只是还没有观察到它的失败。但是,迟早会失败的。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接