C sprintf()期间的Segfault

C sprintf()期间的Segfault,c,systems-programming,C,Systems Programming,因此,我目前正在为我的Unix操作系统类进行系统编程。这个程序所要做的就是读取一个二进制文件并将这些行输出到一个CSV文件中。我觉得我差不多完成了,但由于某种原因,我总是犯错误 澄清: fd1=输入文件, fd2=输出文件, numrecs=输入文件中的记录数。 在main()的某个地方: for(i=0;iid,recs->lname,recs->fname,recs->gpa,recs->iq); printf(“%s\n”,缓冲区); 写入(fd2,缓冲区,sizeof(缓冲区)); 返回

因此,我目前正在为我的Unix操作系统类进行系统编程。这个程序所要做的就是读取一个二进制文件并将这些行输出到一个CSV文件中。我觉得我差不多完成了,但由于某种原因,我总是犯错误

澄清: fd1=输入文件, fd2=输出文件, numrecs=输入文件中的记录数。 在main()的某个地方:

for(i=0;iid,recs->lname,recs->fname,recs->gpa,recs->iq);
printf(“%s\n”,缓冲区);
写入(fd2,缓冲区,sizeof(缓冲区));
返回0;
}
segfault发生在“sprintf(buffer等);”行上,但是,我不明白为什么会发生这种情况

这是gdb吐出的错误:

程序接收信号SIGSEGV,分段故障。
0x0000000100000c87位于bin2csv处的bin2csv中(fd1=3,fd2=4)。c:25
25 sprintf(缓冲区,“%d,%s,%s,%f,%d\n”,记录->标识,记录->名称,
recs->fname,recs->gpa,recs->iq)


希望这是足够的信息。谢谢

它看起来像是一个指针。您正在将字节直接读取到该指针中,就像从文件中读取原始内存地址一样:

read(fd1, &recs, sizeof(recs))
然后在调用
sprintf
时开始使用它。。。轰

实际上根本没有理由使用它(它是全球性的吗?)。。。即使您通过
rec=&rec
初始化了它,并且假设您没有将其丢弃,它仍然不会包含该函数之外的有效地址。这是因为
rec
是一个局部变量

因此,只需像这样直接阅读
rec

read(fd1, &rec, sizeof(rec))
 sprintf(buffer, 100, "%d, %s, %s, %f, %d\n", recs->id, recs->lname, recs->fname, recs->gpa, recs->iq);

然后在
sprintf
行上,使用
rec.id
而不是
recs->id
(等)。

看起来
recs
是一个指针。您正在将字节直接读取到该指针中,就像从文件中读取原始内存地址一样:

read(fd1, &recs, sizeof(recs))
然后在调用
sprintf
时开始使用它。。。轰

实际上根本没有理由使用它(它是全球性的吗?)。。。即使您通过
rec=&rec
初始化了它,并且假设您没有将其丢弃,它仍然不会包含该函数之外的有效地址。这是因为
rec
是一个局部变量

因此,只需像这样直接阅读
rec

read(fd1, &rec, sizeof(rec))
 sprintf(buffer, 100, "%d, %s, %s, %f, %d\n", recs->id, recs->lname, recs->fname, recs->gpa, recs->iq);

然后在
sprintf
行中,使用
rec.id
而不是
recs->id
(等)。

我在这里看到一些问题:

  • sprintf
    不阻止写入字符串缓冲区的末尾。事实上,它不知道缓冲区的长度(在您的例子中是100字节)。由于您已经在堆栈中设置了缓冲区,如果
    sprintf
    过度运行缓冲区(它可以使用长名字或姓氏或垃圾字符串作为输入),堆栈将损坏,并且可能出现seg故障。您可能需要考虑包括逻辑,以确保SaveTF不会超过您所拥有的缓冲空间。或者最好是完全避免使用
    sprintf
    (更多信息见下文)

  • 您没有在提供的代码中处理文件结尾。对于文件结尾,read返回0。如果将错误指针传递给sprintf,它将失败

  • 您正在使用的函数是UNIX派生的函数(POSIX的一部分,但级别很低),它们使用小整数作为文件描述符。我建议改为使用基于
    文件*
    的文件。感兴趣的I/O功能将是
    fopen
    fclose
    fprintf
    fwrite
    ,等等。这样就不需要使用
    sprintf


  • 有关更多信息,请参阅。

    我在这里看到一些问题:

    if((buflen = read(fd1, &recs, sizeof(recs))) < 0){
    
  • sprintf
    不阻止写入字符串缓冲区的末尾。事实上,它不知道缓冲区的长度(在您的例子中是100字节)。由于您已经在堆栈中设置了缓冲区,如果
    sprintf
    过度运行缓冲区(它可以使用长名字或姓氏或垃圾字符串作为输入),堆栈将损坏,并且可能出现seg故障。您可能需要考虑包括逻辑,以确保SaveTF不会超过您所拥有的缓冲空间。或者最好是完全避免使用
    sprintf
    (更多信息见下文)

  • 您没有在提供的代码中处理文件结尾。对于文件结尾,read返回0。如果将错误指针传递给sprintf,它将失败

  • 您正在使用的函数是UNIX派生的函数(POSIX的一部分,但级别很低),它们使用小整数作为文件描述符。我建议改为使用基于
    文件*
    的文件。感兴趣的I/O功能将是
    fopen
    fclose
    fprintf
    fwrite
    ,等等。这样就不需要使用
    sprintf

  • 有关更多信息,请参阅。

    if((buflen=read(fd1,&recs,sizeof(recs))<0){
    
    if((buflen = read(fd1, &recs, sizeof(recs))) < 0){
    
    使用具有未初始化值的
    id

    如果((buflen=read(fd1,&recs,sizeof(recs)))<0){
    
    使用未初始化值的
    id

    您有一些问题: 1) bin_记录的结构。它有char[],可能会溢出。 2) 在sprintf中,不能设置缓冲区最大大小。最好像这样使用snprintf:

    read(fd1, &rec, sizeof(rec))
    
     sprintf(buffer, 100, "%d, %s, %s, %f, %d\n", recs->id, recs->lname, recs->fname, recs->gpa, recs->iq);
    
    3) 要使用null us填充缓冲区,请执行以下操作:

    memset (buffer,'\0',100);
    
    你有一些问题: 1) bin_记录的结构。它有char[],可能会溢出。 2) 在sprintf中,不能设置缓冲区最大大小。最好像这样使用snprintf:

    read(fd1, &rec, sizeof(rec))
    
     sprintf(buffer, 100, "%d, %s, %s, %f, %d\n", recs->id, recs->lname, recs->fname, recs->gpa, recs->iq);
    
    3) 要使用null us填充缓冲区,请执行以下操作:

    memset (buffer,'\0',100);
    

    分段错误是由错误的内存访问引起的,由于该行上唯一的内存访问是
    recs->…
    ,您可以假定recs没有正确初始化。请检查它是否在之前为
    NULL
    ,如果不是,请继续挖掘。
    bin\u记录的后期定义
    strncpy(缓冲区,“\0”,100);