我有一个问题。这是一个家庭作业,并没有理解它。这个简单的程序要求用户将他/她的姓名和电话号码写入文件,如果它找到RDS电话号码ex({{ 1}})然后写出二进制文件的所有者和电话号码。 问题是程序没有将任何内容写入二进制文件(output.dat)。
到目前为止,我做到了这一点:
0*3*12415324
答案 0 :(得分:0)
当我运行你的代码时,我得到一个段错误。正在运行valgrind会显示问题。
==75112== Invalid read of size 8
==75112== at 0x1001D0C5F: flockfile (in /usr/lib/system/libsystem_c.dylib)
==75112== by 0x1001D303C: fscanf (in /usr/lib/system/libsystem_c.dylib)
==75112== by 0x100000D42: main (test.c:21)
==75112== Address 0x68 is not stack'd, malloc'd or (recently) free'd
==75112==
==75112==
==75112== Process terminating with default action of signal 11 (SIGSEGV)
==75112== Access not within mapped region at address 0x68
==75112== at 0x1001D0C5F: flockfile (in /usr/lib/system/libsystem_c.dylib)
==75112== by 0x1001D303C: fscanf (in /usr/lib/system/libsystem_c.dylib)
==75112== by 0x100000D42: main (test.c:21)
由于fscanf
失败, fopen
被传递了伪造的文件句柄。在这种情况下,我没有input.txt
。始终检查fopen
的结果。我使用包装函数来确保始终发生这种情况。
#include <errno.h>
#include <string.h>
FILE *open_file(const char *filename, const char *mode) {
FILE *fp = fopen(filename, mode);
if( fp == NULL ) {
fprintf(stderr, "Could not open %s: %s.\n", filename, strerror(errno));
exit(errno);
}
return fp;
}
当我用fopen
替换open_file
时,我收到了一条很好的错误消息。
$ ./test
Could not open input.txt: No such file or directory.
好的,touch input.txt
再次运行。另一个段错误。 Valgrind再次......
==75478== Conditional jump or move depends on uninitialised value(s)
==75478== at 0x100000D0F: main (test.c:32)
==75478==
==75478== Conditional jump or move depends on uninitialised value(s)
==75478== at 0x100000D91: main (test.c:36)
==75478==
==75478== Conditional jump or move depends on uninitialised value(s)
==75478== at 0x100000DB9: main (test.c:37)
前两个约为n
,未初始化,因为input.txt为空。应该填充它的fscanf
失败,因此n
包含它开始的任何垃圾。循环基于n
中的垃圾运行随机次数。它尝试并且失败,从input.txt读取,因此p
未被初始化并且还包含垃圾。
第三个错误是关于尝试在p
中读取垃圾箱。 n
包含垃圾,因此for循环会尝试从未初始化的p
数组中读取。
这可以通过初始化n
来解决,并像以前一样检查fscanf
是否有效。同样,包装函数可确保检查错误。
int scan_check(FILE *restrict stream, const char *restrict format, ...) {
va_list args;
va_start(args, format);
int ret = vfscanf(stream, format, args);
if( ret <= 0 ) {
fprintf(stderr, "Could not match input to '%s'.\n", format);
exit(errno);
}
return ret;
}
现在它运行到最后。一切都好,对吗?不,valgrind
再说一遍。
==76285== Syscall param write(buf) points to uninitialised byte(s)
==76285== at 0x1002DE97A: write$NOCANCEL (in /usr/lib/system/libsystem_kernel.dylib)
==76285== by 0x1001D8844: _swrite (in /usr/lib/system/libsystem_c.dylib)
==76285== by 0x1001D12FE: __sflush (in /usr/lib/system/libsystem_c.dylib)
==76285== by 0x1001D1001: fclose (in /usr/lib/system/libsystem_c.dylib)
==76285== by 0x100000E6E: main (test.c:60)
==76285== Address 0x100806bc4 is 4 bytes inside a block of size 4,096 alloc'd
==76285== at 0x100008EBB: malloc (vg_replace_malloc.c:303)
==76285== by 0x1001D468E: __smakebuf (in /usr/lib/system/libsystem_c.dylib)
==76285== by 0x1001E91DF: __swsetup (in /usr/lib/system/libsystem_c.dylib)
==76285== by 0x1001D3928: __sfvwrite (in /usr/lib/system/libsystem_c.dylib)
==76285== by 0x1001D3F02: fwrite (in /usr/lib/system/libsystem_c.dylib)
==76285== by 0x100000DDB: main (test.c:54)
这告诉我们fwrite
来电有问题。
fwrite(p[i].name,sizeof(PERSON),n,fout);
fwrite(p[i].tel,sizeof(PERSON),n,fout);
这就是写n
个PERSON
结构大小的项目。如果您正在编写所有p
,那就没问题了。但是你只写了一个名字和一个电话号码。相反,你想要写出等于字符串长度的字符,再加上一个用于空字节的字符。
fwrite(p[i].name, sizeof(char), strlen(p[i].name)+1, fout);
fwrite(p[i].tel, sizeof(char), strlen(p[i].tel)+1 , fout);
最后,把它们放在一起,valgrind没有抱怨。
int main()
{
int n = 0;
PERSON p[50];
FILE *fin = open_file("input.txt","rt");
FILE *fout = open_file("output.dat","wb");
scan_check(fin,"%i", &n);
for (int i=0; i<n; ++i)
{
scan_check(fin,"%s%s", p[i].name, p[i].tel);
}
for (int i=0; i<n; ++i){
if (p[i].tel[1]=='3'){
fwrite(p[i].name, sizeof(char), strlen(p[i].name)+1, fout);
fwrite(p[i].tel, sizeof(char), strlen(p[i].tel)+1 , fout);
}
}
fclose(fin);
fclose(fout);
return 0;
}
通过消除固定大小的p
数组,可以使此代码更安全。没有必要。如果input.txt
中的人数超过50人,则列表将会溢出,而不是存储人员列表以便稍后输出,而是在单个for循环中输出它们。
PERSON p;
....
for (int i=0; i<n; ++i)
{
scan_check(fin,"%s%s", p.name, p.tel);
if (p.tel[1]=='3'){
fwrite(p.name, sizeof(char), strlen(p.name)+1, fout);
fwrite(p.tel, sizeof(char), strlen(p.tel)+1 , fout);
}
}
同样不需要{p> n
。 input.txt
无需告诉您有多少条记录,而是在eof(fin)
之前阅读该文件。这可以避免n
不准确。