sprintf期间的Segfault()

时间:2015-12-16 06:34:34

标签: c systems-programming

所以,我目前正在为我的Unix OS类进行系统编程。该程序应该执行的所有操作都是读取二进制文件并将行输出到CSV文件。我觉得我差不多完成了,但由于某种原因,我不断陷入错误。

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

for(i=0;i<numrecs;i++){
    if((bin2csv(fd1, fd2)) == -1){
        printf("Error converting data.\n");
    }
}

int bin2csv(fd1, fd2){
    bin_record rec;
    char buffer[100];
    int buflen;
    strncpy(buffer,"\0", 100); /* fill buffer with NULL */
    recs = &rec;

    /* read in a record */
    if((buflen = read(fd1, &recs, sizeof(recs))) < 0){
        printf("Fatal Error: Data could not be read.\n");        
        return -1;
    }

   sprintf(buffer, "%d, %s, %s, %f, %d\n", recs->id, recs->lname, recs->fname, recs->gpa, recs->iq);
   printf("%s\n", buffer);
   write(fd2, buffer, sizeof(buffer));
   return 0;
}

段错误发生在&#34; sprintf(缓冲区等);&#34;但是,我无法弄清楚为什么会这样。

这是gdb吐出的错误:

  

编程接收信号SIGSEGV,分段故障   bin2csv中的0x0000000100000c87(fd1 = 3,fd2 = 4)bin2csv.c:25
  25 sprintf(缓冲区,&#34;%d,%s,%s,%f,%d \ n&#34;,recs-&gt; id,recs-&gt; lname,
  recs-&gt; fname,recs-&gt; gpa,recs-&gt; iq);

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

4 个答案:

答案 0 :(得分:3)

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

read(fd1, &recs, sizeof(recs))

然后你开始在调用sprintf ... BOOM!

时使用它

实际上根本没有理由使用它(它是全局的吗?)......即使你用recs = &rec初始化它,并假设你没有丢弃它,它仍然不会包含有效在该功能之外的地址。那是因为rec是一个局部变量。

所以,请直接阅读rec,如下所示:

read(fd1, &rec, sizeof(rec))

然后在sprintf行,您使用rec.id代替recs->id etc )。

答案 1 :(得分:2)

我在这里看到一些问题:

  1. sprintf不会阻止写入字符串缓冲区的末尾。实际上,它不知道该缓冲区的长度(在您的情况下为100字节)。由于您已在堆栈中设置了缓冲区,如果sprintf过度运行缓冲区(可以使用长的名字或姓氏或垃圾字符串作为输入),您的堆栈将被破坏并且可能出现seg错误。您可能需要考虑包含逻辑以确保sprintf不会超过您拥有的缓冲区空间量。或者更好的是完全避免sprintf(更多关于以下内容)

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

  3. 您使用的函数是UNIX派生的函数(POSIX的一部分,但绝对是低级别),它们使用小整数作为文件描述符。我建议改用基于FILE *的。感兴趣的I / O函数将是fopenfclosefprintffwrite等。这将消除使用sprintf的需要。

  4. 有关详细信息,请参阅this previous question

答案 2 :(得分:1)

Public Sub LoadImage(ByVal caseNum As String, ByVal commnum As String)
    If Me.SpGetLOMCDocumentPathTableAdapter.GetData(caseNum, commnum).Count > 0 Then
        Try
            Me.SpGetLOMCDocumentPathTableAdapter.Fill(Me.DevGISDataSet.spGetLOMCDocumentPath, caseNum, commnum)

            Dim docPath As New BindingSource
            docPath = Me.SpGetLOMCDocumentPathBindingSource

            Me.bnTIF.BindingSource = docPath

            Dim b1 = New System.Windows.Forms.Binding("Text", docPath, "DocPath", True)

            AddHandler b1.BindingComplete, AddressOf Me.HandleBindingCompleted

            Me.lblDocPath.DataBindings.Add(b1)

            Dim currentPath As String = Nothing

            currentPath = Me.lblDocPath.Text
            MessageBox.Show(b1.Control.Text)

            If Not String.IsNullOrEmpty(currentPath) Then
                If currentPath.Contains(".TIF") Then
                    LoadTIF()
                End If

                If currentPath.Contains(".pdf") Then
                    LoadPDF()
                End If
            End If

            'FindImage()

            Me.Show()
        Catch ex As Exception
            MessageBox.Show("Error: " & ex.Message.ToString)
        End Try
    Else
        MessageBox.Show("Image not available. Please check FEMA and CAMSIS.")
        Exit Sub
    End If
End Sub

Private Sub HandleBindingCompleted(ByVal sender As Object, ByVal e As BindingCompleteEventArgs)
    MessageBox.Show(String.Format("{0}: {1}", e.Binding.BindingMemberInfo.BindingMember, e.Binding.Control.Text))
    If (Not e.Exception Is Nothing) Then
        Debug.WriteLine(e.ErrorText)
    End If
End Sub

使用if((buflen = read(fd1, &recs, sizeof(recs))) < 0){ 而不是<= 0,否则当返回值为0时,< 0可能会在尝试取消引用具有未初始化值的sprintf(buffer ...时出现错误

答案 3 :(得分:0)

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

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

3)用null填充缓冲区:

memset (buffer,'\0',100);
相关问题