C语言多源文件中union跨文件访问无法读取一致值问题
跨源文件访问联合体读取值不一致问题
问题现象
项目包含main.c、loader.c两个源文件,以及定义联合体类型的comp.h头文件,预期实现两个源文件访问同一个联合体实例完成读写。实际运行时,loader()函数内写入数据后打印可确认写入成功,但回到main函数执行读操作时,对应位置值全为0。
相关代码如下:
main.c
#include <stdio.h> #include "comp.h" union memory mem1; int main() { int choice; unsigned int addlo, addhi; unsigned short data; int k; while (1) { printf("Please Select a Command\n"); printf("0. Exit\n"); printf("1. Load S-Record\n"); printf("2. Read Memory\n"); if(1 != scanf("%d", &choice) == 1) { fprintf(stderr, "Failed to read command.\n"); break; } else if(choice == 0) { break; } else if(choice == 1) { loader(); } else if(choice == 2) { for(k = 1; k<= 10; k++) { printf("k: %x mem: %x\n", k, mem1.byte[k]); } } } return 0; }
comp.h
void loader(void); union memory{ unsigned char byte[0x10000]; unsigned short word[0x8000]; };
loader.c
#include <stdio.h> #include "comp.h" union memory mem; void loader (void){ int i; mem.byte[0] = 0xA1; mem.byte[1] = 0xA2; for(i = 0; i<= 10; i++) { printf("i: %x mem: %x\n", i, mem.byte[i]); } }
运行输出:
Please Select a Command 0. Exit 1. Load S-Record 2. Read Memory 1 i: 0 mem: a1 i: 1 mem: a2 i: 2 mem: 0 i: 3 mem: 0 i: 4 mem: 0 i: 5 mem: 0 i: 6 mem: 0 i: 7 mem: 0 i: 8 mem: 0 i: 9 mem: 0 i: a mem: 0 Please Select a Command 0. Exit 1. Load S-Record 2. Read Memory 2 k: 1 mem: 0 k: 2 mem: 0 k: 3 mem: 0 k: 4 mem: 0 k: 5 mem: 0 k: 6 mem: 0 k: 7 mem: 0 k: 8 mem: 0 k: 9 mem: 0 k: a mem: 0 Please Select a Command 0. Exit 1. Load S-Record 2. Read Memory
根因分析
- 核心问题是定义了两个完全独立的全局联合体实例:
main.c中定义的mem1和loader.c中定义的mem是分配在不同内存地址的两个独立变量,loader()写入的是mem,main函数读取的是从未写入过的mem1,自然读不到正确值。全局变量如果没有手动初始化会被默认填0,这就是main里读出来全是0的原因。 - 代码里还有两个附加bug:
- main中读内存的循环从
k=1开始,就算访问的是正确实例,也读不到下标0位置写入的0xA1 scanf的判断逻辑写法错误,1 != scanf("%d", &choice) == 1的运算优先级不符合预期,无法正确判断输入是否失败
- main中读内存的循环从
- 不存在“写入数据没在两次菜单操作间留存”的问题:全局变量的生命周期覆盖整个程序运行周期,只要不主动覆写,写入的值会一直保留。
修复方案
按照C语言多文件编程的全局变量规范修改即可:
- 修改
comp.h,添加全局联合体的外部声明,让所有包含该头文件的源文件都知道这个实例的存在:void loader(void); union memory{ unsigned char byte[0x10000]; unsigned short word[0x8000]; }; extern union memory mem; // 外部声明,不分配内存,仅告知编译器该变量在其他源文件中定义 - 全局变量只能在一个源文件中定义,删除
main.c里的union memory mem1;,保留loader.c里的union memory mem;作为唯一的实例定义。 - 修改main函数里的访问逻辑,把读内存部分的
mem1.byte[k]改成mem.byte[k],如果需要读取下标0的0xA1,把循环起始值改成k=0。 - 修正输入判断逻辑,把错误的判断改成:
if(scanf("%d", &choice) != 1) { fprintf(stderr, "Failed to read command.\n"); break; }
改完重新编译运行,loader写入的值就可以在main里正常读取到了。
内容的提问来源于stack exchange,提问作者JohnM Wall
相关产品推荐
相关产品推荐

