C语言将文本文件中MAC地址与设备名存入结构体数组的问题排查
C语言读取MAC地址与设备名时结构体字段赋值错误的排查与解决
问题描述
尝试用C语言读取Inet.txt中的MAC地址(分段格式,如AA:BB:CC:DD:EE:FF)和设备名,存入自定义结构体数组:
typedef struct list { char a[3]; // 存储MAC第一段(最多2字符,留1位存结束符) char b[3]; char c[3]; char d[3]; char e[3]; char f[3]; // 注:原描述的a-g为笔误,MAC仅6段,故调整为a-f char alias[33]; // 设备名最多32字符,留1位存结束符 } list;
程序通过fgetc逐字符读取文件,逻辑为:
- 将MAC分段分配到结构体对应字段
- 读取到连续2个以上非
:/空格的字符时,判定为设备名并存储
但运行后结构体中MAC分段(a-f)赋值混乱,出现错误拼接,无法正确输出MAC地址与设备名。
常见问题排查点及解决方法
1. 结构体字段未初始化
结构体数组未初始化时,字段内存会残留垃圾值,导致输出混乱。
- 解决:定义数组后用
memset初始化,或声明时直接赋值:list devices[100]; memset(devices, 0, sizeof(devices));
2. MAC分段的字符边界处理错误
逐字符读取时,未正确识别:作为分段分隔符,或未及时给当前分段添加字符串结束符'\0',导致分段拼接混乱。
- 解决:每读取1个MAC分段的字符,在满2个或遇到
:时,立即给对应字段加上'\0',同时切换到下一个分段的存储位置。
3. 设备名触发逻辑模糊
原逻辑“读取到超过2个非:或空格字符时存储”边界模糊,容易误将MAC分段字符判定为设备名,或设备名读取时未正确终止。
- 解决:先完整读取6段MAC地址,再读取后续设备名;或用
fgets先读取整行,再拆分每行的MAC与设备名,比逐字符读取更易控制。
4. 逐字符读取的状态机逻辑混乱
未维护清晰的读取状态(如正在读取MAC第几段、是否读取设备名),导致字符存储错位。
- 解决:用变量标记当前读取的MAC分段序号(0-5对应a-f),遇到
:时切换分段,完成6段后切换到设备名读取状态。
修正后的示例代码
#include <stdio.h> #include <string.h> typedef struct list { char a[3]; char b[3]; char c[3]; char d[3]; char e[3]; char f[3]; char alias[33]; } list; int main() { FILE *fp = fopen("Inet.txt", "r"); if (!fp) { perror("Failed to open file"); return 1; } list devices[100]; memset(devices, 0, sizeof(devices)); int dev_idx = 0; int mac_segment = 0; // 0:a,1:b,...5:f int char_count = 0; // 当前分段的字符数 int in_device_name = 0; // 是否处于设备名读取状态 int ch; while ((ch = fgetc(fp)) != EOF) { if (ch == '\n') { // 换行表示当前设备记录结束,重置状态 mac_segment = 0; char_count = 0; in_device_name = 0; dev_idx++; if (dev_idx >= 100) break; // 防止数组越界 continue; } if (!in_device_name) { // 读取MAC地址 if (ch == ':') { // 分段结束,添加结束符,切换到下一段 switch(mac_segment) { case 0: devices[dev_idx].a[char_count] = '\0'; break; case 1: devices[dev_idx].b[char_count] = '\0'; break; case 2: devices[dev_idx].c[char_count] = '\0'; break; case 3: devices[dev_idx].d[char_count] = '\0'; break; case 4: devices[dev_idx].e[char_count] = '\0'; break; case 5: devices[dev_idx].f[char_count] = '\0'; break; } mac_segment++; char_count = 0; // 6段MAC读完后,切换到设备名读取 if (mac_segment >= 6) { in_device_name = 1; } continue; } // 跳过空格,直到遇到MAC字符 if (ch == ' ') continue; // 存储当前MAC字符到对应分段 switch(mac_segment) { case 0: if (char_count < 2) devices[dev_idx].a[char_count++] = ch; break; case 1: if (char_count < 2) devices[dev_idx].b[char_count++] = ch; break; case 2: if (char_count < 2) devices[dev_idx].c[char_count++] = ch; break; case 3: if (char_count < 2) devices[dev_idx].d[char_count++] = ch; break; case 4: if (char_count < 2) devices[dev_idx].e[char_count++] = ch; break; case 5: if (char_count < 2) devices[dev_idx].f[char_count++] = ch; break; } } else { // 读取设备名,跳过开头空格,最多存32字符 static int alias_count = 0; if (ch == ' ' && alias_count == 0) continue; if (alias_count < 32) { devices[dev_idx].alias[alias_count++] = ch; } } } // 输出测试 for (int i = 0; i < dev_idx; i++) { printf("MAC: %s:%s:%s:%s:%s:%s | 设备名: %s\n", devices[i].a, devices[i].b, devices[i].c, devices[i].d, devices[i].e, devices[i].f, devices[i].alias); } fclose(fp); return 0; }
额外优化建议
优先用fgets读取整行,再用sscanf拆分MAC和设备名,代码更简洁易维护:
char line[100]; while (fgets(line, sizeof(line), fp)) { list dev = {0}; sscanf(line, "%2s:%2s:%2s:%2s:%2s:%2s %[^\n]", dev.a, dev.b, dev.c, dev.d, dev.e, dev.f, dev.alias); devices[dev_idx++] = dev; }
这种方式避免了逐字符读取的复杂状态机逻辑,大幅降低出错概率。
内容的提问来源于stack exchange,提问作者coconut crab
相关产品推荐
相关产品推荐

