C - 分段关闭文件时出错

时间:2015-09-03 17:03:17

标签: c fclose

我遇到了一个奇怪的问题,我在尝试关闭文件时收到分段错误。正确地将数据写入文件,fflush和fclose之间是否会出现某种竞争条件?

//main.c

#define filename "/home/test.txt";
char fbuf[255];
sprintf(fbuf, "%s, %f, %f,%f \n", "big", 8.0,0.0,0.8);

sendFile(&fbuf, sizeof(fbuf), (void *)filename);

static void
sendFile( void *data, int size, char *pName)
{
    FILE *pFile = fopen(pName,"a");
    char *buf = NULL;
    buf = (char *)malloc(255);
    memcpy(buf, data, sizeof(char *)*size);

    if(pFile == NULL) {
        logger(LOG_INFO, "Error opening file\n");
}
    else {
        fwrite(buf, 1, strlen(buf), pFile);
        fflush(pFile);
        fclose(pFile);
    }
    free (buf);
}

非常感谢任何帮助或建议。

2 个答案:

答案 0 :(得分:3)

我认为问题出在memcpy(buf, data, sizeof(char *)*size)

不应该只是memcpy(buf, data, size)吗?

查看您的示例,fbuf(即data)是255个字符,buf也是255个字符。但memcpy正在复制超过一千个字符,有效地将垃圾写入堆中,结果不可预测。

答案 1 :(得分:1)

这是一个有效的代码版本,其中包含有关为什么与发布的代码不同的嵌入式注释。

注意:在Linux(和其他操作系统)上,/home目录在没有管理权限的情况下是不可写的。所以对fopen()的调用总是会失败。

//main.c

#include <stdio.h>  // fwrite, fopen, fclose, perror
#include <stdlib.h> // exit, EXIT_FAILURE
#include <string.h> // strlen, sprintf, memcpy

// note: no comments at end of #define statement as the comment would be copied into the code
// note no ';' at end of #define statement
#define filename "/home/test.txt"
 // to avoid using 'magic' numbers in code
 // and this name used throughout the code
#define BUF_SIZE (255)

// prototypes
void sendFile( char *, char *); // so compiler does not make incorrect assumptions
                                // and no second parameter needed

char fbuf[BUF_SIZE] = {'\0'}; // avoid 'magic' numbers
                              // avoid placing trash into output file
                              // however, will place many NUL bytes

// main() function to make executable platform for testing
int main( void )
{
    sprintf(fbuf, "%s, %f, %f,%f \n", "big", 8.0, 0.0, 0.8);
     // changed 3rd parameter type to match actual function parameter type
    sendFile(fbuf, filename); // no '&' because in C
                              // array name degrades to address of array
    return 0;
}

void sendFile( char *data, char *pName) // use actual parameter types
{
    FILE *pFile = fopen(pName,"a");
    if(pFile == NULL)  // always check for error immediately after call to system function, not later
    { // then fopen failed
         perror( "fopen failed" );
        //logger(LOG_INFO, "Error opening file\n");
        exit( EXIT_FAILURE); // exit, so no other code executed
    }

    // implied else, fopen successful

    char *buf = NULL;
    if(NULL == (buf = malloc(BUF_SIZE) ) ) // don't cast returned value
    { // then malloc failed   -- always check for error immediately after call to system function
        perror( "malloc for output buffer failed");
        //logger(LOG_INFO, "malloc for BUF_SIZE failed");
        fclose( pFile); // cleanup
        exit(EXIT_FAILURE);  // exit, so no other code executed
    }

    // implied else, malloc successful

    memcpy(buf, data, BUF_SIZE); // using original 3rd parameter would move 4 times as many bytes,
                             // I.E. past the end of the source buffer and past the end of the destination buffer
                             // resulting in undefined behaviour, leading to a seg fault event
                             // this memcpy() and the destination buffer
                             // are unneeded as first passed in parameter
                             // contains ptr to the source buffer
                             // which can be used in the call to fwrite()

    fwrite(buf, BUF_SIZE, 1, pFile); // buf will have NUL byte immediately after 'big' so strlen() would return 3
                                     // and the syntax for fwrite is source buffer, size of one item, number of items, FILE*
                                     // and this parameter order is best
                                     // because, if error checking,
                                     // can just compare returned value to 1
    fflush(pFile);  // not actually needed as the fclose() performs a flush
    fclose(pFile);

    free (buf);     // original code did this even if malloc had failed
                    // and even if the fopen had failed
                    // which would have corrupted the 'heap' leading to a seg fault event
}