c fopen() 在打开许多文件时给我分段错误

c fopen() gives me segmentation fault when opening many files

我必须编写代码来进行外部归并排序。 这部分是第 1 阶段,它写入 {num_blocos} 个文件,其中包含 400KB 的排序文件:

char file_name[20];
while (1)
{
    size_read = fread(bloco, tam, mem_bloco, file);
    if (size_read != 0)
    {
        heapSort(bloco, size_read);
        sprintf(file_name, "file%drun0.bin", num_blocos);
        temp = fopen(file_name, "wb");
        fwrite(bloco, tam, size_read, temp);
        num_blocos++;
        fclose(temp);
    }
    else
    {
        break;
    }
}

这是第 2 阶段,它合并所有文件 {n x n} 每个 运行 或将其重写到下一个 运行。

int k = 0, n = 1, j = 0;;
char file_write[20], file_read[20];

while (n < num_blocos)
{
    while (k<num_blocos)
    {
        sprintf(file_write, "file%drun%d.bin", k, n);
        file = fopen(file_write, "wb");
        if (k + 1 == num_blocos)
        {
            sprintf(file_read, "file%drun%d.bin", k, j);
            temp = fopen(file_read, "rb");
            int size = fread(bloco, tam, mem_bloco, temp);
            fwrite(bloco, tam, size, file);
            break;
        }
        else
        {
            mergeSortFile(k, k + n, file, j);
            sprintf(file_name, "file%drun%d.bin", k, j);
            remove(file_name);
            sprintf(file_name, "file%drun%d.bin", k+n, j);
            remove(file_name);
            k += 2 * n;
        }
    }
    k = 0;
    j = n;
    n *= 2;
    fclose(temp);
    fclose(file);
    remove(file_read);
}

我的问题来了:

FILE *tempa, *tempb;
char file_name[20];
sprintf(file_name, "file%drun%d.bin", posb1, j);
tempa = fopen(file_name, "wb+");
if( tempa == NULL ) {
    fprintf(stderr, "Couldn't open %s: %s\n", file_name, strerror(errno));
    exit(1);
}
sprintf(file_name, "file%drun%d.bin", posb2, j);
tempb = fopen(file_name, "wb+");
if( tempb == NULL ) {
    fprintf(stderr, "Couldn't open %s: %s\n", file_name, strerror(errno));
    exit(1);
}

当我的原始文件有 1MB 时它运行良好,但是当它有 99MB 时我想要的大小甚至 6MB,它给我分段错误,调试后我发现它是在 tempb fopen 试图打开6 MB 或更多的文件“file10run1.bin”,有人可以给我灯吗?

您在同一 temp 文件句柄上多次使用 fclose

运行 使用 Address Sanitizer 的测试程序产生:

=================================================================
==3720049==ERROR: AddressSanitizer: attempting double-free on 0x615000002100 in thread T0:
    #0 0x7f51a7ee4277 in __interceptor_free (/usr/lib/x86_64-linux-gnu/libasan.so.5+0x107277)
    #1 0x7f51a7c8dd5f in _IO_deallocate_file /build/glibc-M65Gwz/glibc-2.30/libio/libioP.h:863
    #2 0x7f51a7c8dd5f in _IO_new_fclose /build/glibc-M65Gwz/glibc-2.30/libio/iofclose.c:74
    #3 0x7f51a7ee2490 in __interceptor_fclose (/usr/lib/x86_64-linux-gnu/libasan.so.5+0x105490)
    #4 0x5615b5ab1805 in fileSort /tmp/so/extsort.c:106
    #5 0x5615b5ab131c in main /tmp/so/extsort.c:27
    #6 0x7f51a7c40e0a in __libc_start_main ../csu/libc-start.c:308
    #7 0x5615b5ab1249 in _start (/tmp/so/a.out+0x1249)

0x615000002100 is located 0 bytes inside of 488-byte region [0x615000002100,0x6150000022e8)
freed by thread T0 here:
    #0 0x7f51a7ee4277 in __interceptor_free (/usr/lib/x86_64-linux-gnu/libasan.so.5+0x107277)
    #1 0x7f51a7c8dd5f in _IO_deallocate_file /build/glibc-M65Gwz/glibc-2.30/libio/libioP.h:863
    #2 0x7f51a7c8dd5f in _IO_new_fclose /build/glibc-M65Gwz/glibc-2.30/libio/iofclose.c:74

previously allocated by thread T0 here:
    #0 0x7f51a7ee4628 in malloc (/usr/lib/x86_64-linux-gnu/libasan.so.5+0x107628)
    #1 0x7f51a7c8e62a in __fopen_internal /build/glibc-M65Gwz/glibc-2.30/libio/iofopen.c:65
    #2 0x6e69622e306e74  (<unknown module>)

SUMMARY: AddressSanitizer: double-free (/usr/lib/x86_64-linux-gnu/libasan.so.5+0x107277) in __interceptor_free
==3720049==ABORTING

下面的补丁修复了这个问题,但是在其他地方还有两个内存泄漏(留给你修复的小练习,使用 gcc -fsanitize=address 找到它):

--- extsort.c   2020-07-19 17:42:02.354991453 -0700
+++ extsort-fixed.c     2020-07-19 17:41:57.067008342 -0700
@@ -64,6 +64,7 @@
             fwrite(bloco, size, size_read, temp);
             num_blocos++;
             fclose(temp);
+            temp = NULL;
         }
         else
         {
@@ -103,7 +104,10 @@
         k = 0;
         j = n;
         n *= 2;
-        fclose(temp);
+        if (temp != NULL)
+          fclose(temp);
+        temp = NULL;
+
         fclose(file);
         remove(file_read);
     }