【问题标题】:Segmentation fault: 11 while trying to print array with 50k items分段错误:11 在尝试打印包含 50k 个项目的数组时
【发布时间】:2021-10-02 19:14:02
【问题描述】:

我正在尝试将包含 50k 个项目的数组打印到一个文件中,但只有在我设置少量项目时才能完成,例如5k。

void fputsArray(int *arr, int size, char *filename)
{
    char *string = (char*)calloc( (8*size+1), sizeof(char) );
    for(int i = 0; i < size; i++)
        sprintf( &string[ strlen(string) ], "%d\n", arr[i] );

    FILE *output;
    char fullFilename[50] = "./";
    output = fopen(strcat(fullFilename, filename), "w");
    fputs(string, output);
    fclose(output);
    free(string);
}

size 是 50000,在 #DEFINE 中定义。 这是工作代码。但是,如果我删除 8 乘以 size,那我应该可以工作,但不起作用。我遇到了那种情况分段错误:11 为什么我应该分配比我需要多 8 倍的内存?

【问题讨论】:

  • 代码中有很多地方不检查空值或溢出。尝试使用调试器和/或使您的代码健壮。
  • 如果循环是 size 并且您正在 sprintfing 整数,它们每个将占用超过 1 个字节。所以*8.
  • “内存比需要多 8 倍”的评论很难理解;您正在打印至少 2*size+1 个字符(一个数字和 \n),并且可能高达 12*size+1,具体取决于整数的值。问题似乎很可能是注销了分配空间的末尾。
  • 您为什么不直接fprintf 到文件中?
  • 为什么需要在"./" 前面加上filename

标签: arrays c memory calloc fault


【解决方案1】:

假设您的函数的 输入 都是正确的:

void fputsArray(int *arr, int size, char *filename)

大小应为size_t

{
    char *string = (char*)calloc( (8*size+1), sizeof(char) );

不需要清除内存(calloc),malloc 和设置string[0] = '\0' 就足够了。根据定义,sizeof( char ) 始终为 1。还有you should not cast the result of an allocation

实际上,整个构造是不必要的,但那是以后的事了。

    for(int i = 0; i < size; i++)
        sprintf( &string[ strlen(string) ], "%d\n", arr[i] );

实际上并没有那么糟糕,除了 string + strlen( string ) 更简单并且应该始终有 { } 围绕声明。仍然是不必要的复杂。

    FILE *output;
    char fullFilename[50] = "./";
    output = fopen(strcat(fullFilename, filename), "w");

文件名总是相对于当前工作目录,所以"./" 是不必要的。但是,您应该strcat将其放入这样的静态缓冲区之前检查文件名长度。

    fputs(string, output);

啊,但是你还没有检查 fopen 是否真的成功了!

    fclose(output);
    free(string);
}

总而言之,我见过更糟糕的情况。不过,您的数字是否真的适合您的缓冲区是猜测,最重要的是整个内存恶作剧都是不必要的

考虑:

void printArray( int const * arr, size_t size, char const * filename )
{
    FILE * output = fopen( filename, "w" );
    if ( output != NULL )
    {
        for ( size_t i = 0; i < size; ++i )
        {
            fprintf( output, "%d\n", arr[i] );
        }
        fclose( output );
    }
    else
    {
        perror( "File open failed" );
    }
}

我认为这比试图找出你的记忆猜测出错的地方好多


编辑:再想一想,我会让该函数采用 FILE * 参数而不是文件名,这样您就可以灵活地打印到已经打开的流(例如 @987654341 @) 也可以,还可以让您在更高的位置对fopen 进行错误处理,该位置可能具有提供有用信息的附加功能。

【讨论】:

    【解决方案2】:

    大小为 50000,在 #DEFINE 中定义。这是工作代码。但是,如果我删除 8 乘以大小,那我应该可以工作,但不起作用。我遇到了这种情况 分段错误:11 为什么我应该分配比我需要的多 8 倍的内存?

    您正在撰写有关此尺寸估算的文章:

        char *string = (char*)calloc( (8*size+1), sizeof(char) );
    

    但是使用的数组是int[],你将在磁盘中的每一行写入一个值

        sprintf( &string[ strlen(string) ], "%d\n", arr[i] );
    

    这似乎不必要的复杂。至于大小,假设所有值都是INT_MIN,a.k.a. (in limits.h)

    #define INT_MIN     (-2147483647 - 1)
    

    为 4 字节整数。所以你有 11 个字符。只是。 10 位数字加一个信号符号。这将为您提供任何 int 值。为'\n'加1

    但是……

    • 为什么要使用 calloc()?

    • 为什么不直接使用适合所有可能值的 size * 12-byte 数组?

    • 为什么要声明一个新的char* 来保存char 格式的值,而不是一次只使用fprintf()

    • 为什么void 而不是仅仅返回-1 表示错误或返回成功时写入磁盘的iten 数量?

    返回程序

    如果您真的想在一次调用fputs() 时将数组写入磁盘,将整个巨大的字符串保存在内存中,请考虑sprintf() 返回写入的字节数,所以这是您需要的值用作指向输出字符串的指针...

    如果您想使用内存分配,您可以分块进行。考虑如果所有值都低于 999,则 50.000 行每行不超过 4 个字节。但如果所有值都等于INT_MIN,则每行最多有 12 个字节。

    因此,您可以使用sprintf() 的返回来更新指向字符串的指针,并在需要时使用realloc(),比如说,以几K 字节为单位分配。 (如果你真的想回信,我可以发布一个例子)

    C 示例

    下面的代码以您尝试的方式写入文件,并返回写入的总字节数。无论如何,这取决于数组的值。最大就是我说的,每行12个字节...

    int fputsArray( unsigned size, int*  array , const char* filename)
    {
        static char string[12 * MY_SIZE_ ] = {0};
        unsigned ix = 0; // pointer to the next char to use in string
        FILE*    output = fopen( filename, "w");
        if ( output == NULL ) return -1;
        // file is open
        for(int i = 0; i < size; i+= 1)
        {
            unsigned used = sprintf( (string + ix), "%d\n", array[i] );
            ix += used;
        } 
        fputs(string, output);
        fclose(output);
        return ix;
    }
    

    使用fprintf()

    此代码使用fprintf() 编写相同的文件,而且更简单...

    int fputsArray_b( unsigned size, int*  array , const char* filename)
    {
        unsigned ix = 0; // bytes written
        FILE*    output = fopen( filename, "w");
        if ( output == NULL ) return -1;
        // file is open
        for(int i = 0; i < size; i+= 1)
            ix += fprintf( output, "%d\n", array[i]);
        fclose(output);
        return ix;
    }
    

    2个函数的完整测试

    #define     MY_SIZE_ 50000
    #include <limits.h>
    #include <stdio.h>
    #include <stdlib.h>
    
    int fputsArray(const unsigned,int*,const char*);
    int fputsArray_b(const unsigned,int*,const char*);
    
    int main(void)
    {
        int value[MY_SIZE_];
        srand(210726); // seed for today :)
        value[0] = INT_MIN; // just to test: this is the longest value
        for ( int  i=1; i<MY_SIZE_; i+=1 ) value[i] = rand();
        int used = fputsArray( MY_SIZE_, value, "test.txt");
        printf("%d bytes written to disk\n", used );
    
        used = fputsArray_b( MY_SIZE_, value, "test_b.txt");
        printf("%d bytes written to disk using the alternate function\n", used );
        return 0;
    }
    
    
    int fputsArray( unsigned size, int*  array , const char* filename)
    {
        static char string[12 * MY_SIZE_ ] = {0};
        unsigned ix = 0; // pointer to the next char to use in string
        FILE*    output = fopen( filename, "w");
        if ( output == NULL ) return -1;
        // file is open
        for(int i = 0; i < size; i+= 1)
        {
            unsigned used = sprintf( (string + ix), "%d\n", array[i] );
            ix += used;
        } 
        fputs(string, output);
        fclose(output);
        return ix;
    }
    
    int fputsArray_b( unsigned size, int*  array , const char* filename)
    {
        unsigned ix = 0; // bytes written
        FILE*    output = fopen( filename, "w");
        if ( output == NULL ) return -1;
        // file is open
        for(int i = 0; i < size; i+= 1)
            ix += fprintf( output, "%d\n", array[i]);
        fclose(output);
        return ix;
    }
    

    程序写入2个相同的文件...

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-09-01
      • 1970-01-01
      • 2015-02-08
      • 1970-01-01
      • 1970-01-01
      • 2012-07-01
      • 2020-08-15
      • 1970-01-01
      相关资源
      最近更新 更多