C语言操作txt文件跳行读取触发0xC0000005错误解决方法
问题现象
程序首次运行时可正常进入新账号注册流程、写入用户数据,二次运行已存有用户数据的test.txt时,直接触发错误码-1073741819 (0xC0000005)崩溃,无法输出预期的用户欢迎内容。
原问题代码:
int main() { FILE *fp; fp = fopen("test.txt", "a+"); char test = 'T', check3rd, optreg = 'T'; int x; char namalengkap[50], gender[50]; int umur, tb, bb; fprintf(fp, "Username : testa\nPassword : 12345\n"); rewind(fp); fscanf(fp, "%*[^\n]\n%*[^\n]\n"); fscanf(fp, "%c", check3rd); if (check3rd == '\0') { printf("Akun baru terdeteksi,Silahkan isi data anda terlebih dahulu\n"); while (optreg !='Y' && optreg !='y') { fflush(stdin); printf("Nama Lengkap : "); fgets(namalengkap, 50, stdin); strtok(namalengkap, "\n"); printf("Jenis Kelamin : "); fgets(gender, 50, stdin); strtok(gender, "\n"); printf("Umur : "); scanf("%d", &umur); fflush(stdin); printf("Tinggi Badan : "); scanf("%d", &tb); fflush(stdin); printf("Berat Badan : "); scanf("%d", &bb); fflush(stdin); printf("Apa anda sudah yakin dengan data ini?\n"); chkch: printf("(Y/T)"); scanf(" %c", &optreg); if (optreg != 'Y' && optreg !='y' && optreg != 'T' && optreg != 't') { puts("input salah\n"); goto chkch; } rewind(fp); fprintf(fp, "\nNama : %s\nJenis Kelamin: %s\nUmur : %d\nTinggi Badan : %d\nBerat Badan : %d", namalengkap, gender, umur, tb, bb); } } else { printf("Selamat datang %s!\n", namalengkap); } return 0; }
根因分析
代码存在多处可直接触发崩溃和逻辑错误的问题:
- 直接导致内存访问崩溃的核心错误:
fscanf(fp, "%c", check3rd);未传入check3rd的内存地址。%c格式符要求参数为char*类型,代码直接传入char类型变量值,fscanf会把读取到的字符值当作内存地址执行写入操作,触发非法内存访问,对应0xC0000005访问违例错误。 - 空行判断逻辑完全错误:跳过前两行后,若第三行为空,文件指针实际已经到达文件末尾,fscanf读取会直接失败;即使第三行是空行,首字符也是换行符
\n而非字符串结束符\0,原判断条件永远无法匹配预期场景。 - 未初始化内存访问:走到非新账号的else分支时,
namalengkap是栈上未初始化的字符数组,存储的是随机垃圾值,代码从未从文件中读取已存储的用户姓名,直接用%s打印该数组会访问随机内存,同样会触发崩溃。 - 重复写入逻辑bug:每次打开文件都无脑执行
fprintf(fp, "Username : testa\nPassword : 12345\n");,程序每运行一次就会在文件末尾追加一组账号密码,多次运行后文件内容会完全混乱。 - 依赖未定义行为:
fflush(stdin)不属于C标准规定的合法用法,标准仅支持对输出流执行fflush操作,输入流fflush的效果随编译器变化,会导致输入逻辑不可控。
修复方案
按以下规则修正代码即可解决问题:
- 所有格式化输入函数读取普通变量时,必须传入变量地址(数组名自动退化为指针,不需要额外取地址)。
- 打开文件后先判断文件是否为空,仅首次创建文件时写入初始账号密码行,避免重复写入。
- 修正空行判断逻辑:跳过前两行后检查fscanf返回值,若读取失败(到达EOF)或读到的首字符为
\n,判定为新账号需要注册。 - 移除所有
fflush(stdin),输入后残留的换行符用逐字符读取的方式清空缓冲区,不依赖未定义行为。 - 非新账号场景下,先从文件中解析出已存储的用户姓名字段,再执行欢迎语打印,禁止访问未初始化的内存。
修正后的可运行代码:
#include <stdio.h> #include <string.h> // 清空输入缓冲区残留字符,替代未定义的fflush(stdin) void clear_stdin() { int c; while ((c = getchar()) != '\n' && c != EOF); } int main() { FILE *fp; fp = fopen("test.txt", "a+"); if (fp == NULL) { perror("Gagal membuka file"); return 1; } char check3rd, optreg = 'T'; char namalengkap[50], gender[50]; int umur, tb, bb; // 判断文件是否为空,仅空文件首次写入初始账号密码 fseek(fp, 0, SEEK_END); long fsize = ftell(fp); if (fsize == 0) { fprintf(fp, "Username : testa\nPassword : 12345\n"); } rewind(fp); // 跳过文件前两行 fscanf(fp, "%*[^\n]\n%*[^\n]\n"); // 传入check3rd地址,修正fscanf参数错误 int ret = fscanf(fp, "%c", &check3rd); // 新账号判断:读失败到EOF/读到换行符,说明第三行为空 if (ret == EOF || check3rd == '\n') { printf("Akun baru terdeteksi,Silahkan isi data anda terlebih dahulu\n"); while (optreg !='Y' && optreg !='y') { printf("Nama Lengkap : "); fgets(namalengkap, 50, stdin); strtok(namalengkap, "\n"); printf("Jenis Kelamin : "); fgets(gender, 50, stdin); strtok(gender, "\n"); printf("Umur : "); scanf("%d", &umur); clear_stdin(); printf("Tinggi Badan : "); scanf("%d", &tb); clear_stdin(); printf("Berat Badan : "); scanf("%d", &bb); clear_stdin(); printf("Apa anda sudah yakin dengan data ini?\n"); chkch: printf("(Y/T)"); scanf(" %c", &optreg); clear_stdin(); if (optreg != 'Y' && optreg !='y' && optreg != 'T' && optreg != 't') { puts("input salah\n"); goto chkch; } // a+模式下写操作自动追加到文件末尾,无需rewind fprintf(fp, "\nNama : %s\nJenis Kelamin: %s\nUmur : %d\nTinggi Badan : %d\nBerat Badan : %d", namalengkap, gender, umur, tb, bb); fflush(fp); // 确保写入内容落盘 } } else { // 回退之前读取的1个字符,解析文件中存储的姓名 fseek(fp, -1, SEEK_CUR); fscanf(fp, "%*[^:]: %49[^\n]", namalengkap); printf("Selamat datang %s!\n", namalengkap); } fclose(fp); return 0; }
内容的提问来源于stack exchange,提问作者LearnerC
相关产品推荐
相关产品推荐

