能否在C90中模拟实现C99的左值数组及结构体常量成员初始化
解决方案
最优方案:利用Union兼容构造(无UB、最简洁)
C90标准明确支持Union的别名访问规则,我们可以通过一个仅内部可见的Union来安全构造不可变的struct arr对象,完全规避修改const对象的未定义行为,代码复杂度和C99原生写法几乎一致:
#include <stdio.h> enum { MUT_ARR_LEN = 4UL }; struct mut_arr { unsigned char bytes[sizeof(unsigned char const) * MUT_ARR_LEN]; }; struct arr { struct mut_arr const byte_arr; }; /* 仅当前编译单元可见的构造辅助Union,对外完全隐藏实现细节 */ union arr_construction { struct arr immutable; struct mut_arr mutable; }; static struct arr map(struct arr const* const a, unsigned char (*const op)(unsigned char const)) { union arr_construction res; size_t i = 0UL; for (; i < MUT_ARR_LEN; ++i) { res.mutable.bytes[i] = op(a->byte_arr.bytes[i]); } return res.immutable; } static unsigned char op_add_one(unsigned char const el) { return el + 1; } static unsigned char op_print(unsigned char const el) { printf("%u", el); return 0U; } int main() { struct arr const a1 = {{{1, 2, 3, 4}}}; struct arr const a2 = map(&a1, &op_add_one); map(&a2, &op_print); return 0; }
方案优势
- 完全符合ISO C90标准,无任何未定义行为,兼容性拉满
- 不需要额外内存复制、不需要强制类型转换,代码简洁可读
- 纯栈分配,线程安全,完全满足约束要求
- 辅助Union仅内部可见,对外暴露的
struct arr仍然保持成员不可变的特性,符合函数式编程的不可变数据要求
简化版Memcpy方案
如果你不想引入辅助Union,也可以直接简化原来的写法,去掉多余的指针转换和不必要的const声明,规避未定义行为:
#include <string.h> struct arr map(struct arr const* const a, unsigned char (*const op)(unsigned char const)) { /* 不要给res本身加const限定,避免修改const对象的UB */ struct arr res = {0}; struct mut_arr tmp = {0}; size_t i = 0UL; for (; i < sizeof(tmp.bytes); ++i) { tmp.bytes[i] = op(a->byte_arr.bytes[i]); } /* 直接用void*传递给memcpy即可,不需要复杂的数组指针转换 */ memcpy((void*)&res.byte_arr, &tmp, sizeof(tmp)); return res; }
注意
原写法中将局部变量res声明为const struct arr,之后修改其内容属于标准明确的未定义行为,遇到开启严格优化的编译器可能出现异常结果,上述两种方案均完全规避了该问题。
内容的提问来源于stack exchange,提问作者user16729031
相关产品推荐
相关产品推荐

