关于您的具体问题:
问题是这是否合法并在 C89 中定义?
答案是否:它违反了严格的别名规则。见What is the strict aliasing rule?。编译器可以为所欲为,包括完全忽略语句:
*((uint32*)(&s.myVar_H)) = myValue;
更安全的方法是使用memcpy;您的代码 sn-p 变为:
MyStruct s;
memcpy(&s.myVar_H, &myValue, sizeof myValue);
当然,在本例中,&s.myVar_H 与 &s 相同。
这假定myValue 必须具有正确的大小和格式(例如,它不能是char 或float)。事实并非如此,如果你有 C99 的复合文字,你可以这样写:
memcpy(&s.myVar_H, (uint32[]){myValue}, sizeof(uint32));
这很丑,但你可以在宏中隐藏它的丑:
#define LEGACY_CPY32(dst, src) memcpy(&(dst), (uint32[]){src}, sizeof(uint32))
但这些建议将避免仅严格混叠问题。 Litte-endian 问题和结构填充问题仍然存在,如果您切换机器和/或编译器,甚至只是更改编译器开关,您的代码可能会中断。
更新 #1:当然,没有人会编写这样的新代码。但我假设 OP 的目标是用 minimal 数量的更改来更新 工作的旧 代码,而不是进行完全重写。
另见:
更新 #2:也许我可以提出一个更有说服力的案例?对于任何构想,都可以从以下方面争论:
对于第三个标准,优雅,与例如工会双关语。
对于第二个标准,效率,John Regehr 在Embedded in Academia, "Type Punning, Strict Aliasing, and Optimization"(2013 年 6 月)中就类似问题提出:
诸如 GCC 4.8.1 和 Clang ~3.3 之类的编译器(都适用于 x86-64 Linux)无法为 c2 [union 的类型双关语功能的示例] 生成好的代码 ... [并给出] 糟糕的目标代码
而在他的示例c5 中使用memcpy:
两个编译器对memcpy 的理解都足够深入,我们可以得到所需的[最佳] 目标代码。
在我看来,c5 是这小批函数中最容易理解的代码,因为它不会进行混乱的转换,而且它完全、完全、显然没有混乱规则可能引起的并发症用于联合和严格的别名。几年前,当我发现编译器可以看穿memcpy 并生成正确的代码时,它成为我首选的类型双关语。
最后,让我们看看第一个标准,合法性。 Regehr 关于工会双关语的陈述(强调添加):
不幸的是,这段代码 [c2,使用联合双关语] 也是现代 C/C++ 方言未定义(或者可能只是未指定,我不完全确定)。
我不愿意不同意 Regehr,但关于 Stack Overflow 的共识似乎是工会双关语自 C99 以来一直有效;见:
但这是 OP 专门询问的 C89 中未定义的行为。
最后请注意 OP 要求:
由于这是基于专有协议的古老遗留代码,我宁愿不触及结构定义
memcpy 解决方案保持头文件不变,如果您与一个敏感的程序员团队一起工作,这是有利的。您可以通过将宏 LEGACY_CPY32 重新定义为之前的“不正确”但可以正常工作的功能,轻松撤销所有更改:
#define LEGACY_CPY32(dst, src) (*((uint32*)(&(dst))) = (src))