动态初始化大型数组时出现段错误 11

Segfault 11 when initializing large arrays dynamically

您好,我正在使用 C 来完成一项学校作业。我必须道歉,我对像 C 这样的低级语言很生疏。当我尝试动态初始化一些数组时,当 n>100 时,我的代码给出了段错误 11。我试图启动 Valgrind 以了解发生了什么,但我不太了解调试日志,因为它报告的内存块比我打算分配的 n 值大得多。任何人都可以帮忙看看有什么问题吗?谢谢!

我的代码(当我将二维矩阵的每个条目都设置为1时发生错误):

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int n;

typedef struct{
  float x;
  float y;
} Point2D;

float rand_float(){
    return (float)rand()/RAND_MAX;
}
int main(void) {
    srand(time(NULL));
    
    scanf("%d", &n);
    

    int (*adjm)[n][n] = malloc( sizeof(float[n][n])+1 );
    Point2D (*vcoord)[n] = malloc( sizeof(Point2D[n])+1 ); 
    
    // initialize coords
    for (int i = 0; i < n; i++){
        (*vcoord[i]).x  = rand_float();
        (*vcoord[i]).y  = rand_float();
        printf("%d %f %f \n", i, (*vcoord[i]).x, (*vcoord[i]).y);
    }
    for (int x = 0; x < n; x++){
        for (int y = 0; y < n; y++){
             *adjm[x][y] = 1;
        }
    }
    
    // free up memory
    free(*adjm);
    free(*vcoord);
    return 0;

}

当我让 n=200 时,Valgrind 日志的前几行:

==18832== Memcheck, a memory error detector
==18832== Copyright (C) 2002-2017, and GNU GPLd, by Julian Seward et al.
==18832== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==18832== Command: ./out
==18832== Parent PID: 1670
==18832==
==18832== Invalid write of size 4
==18832==    at 0x1089F4: main (in /home/usrname/valgrindtest/out)
==18832==  Address 0x5257050 is 1,600 bytes inside a block of size 1,601 alloc'd
==18832==    at 0x4C31B0F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==18832==    by 0x1089B8: main (in /home/usrname/valgrindtest/out)
==18832==
==18832== Invalid write of size 4
==18832==    at 0x108A23: main (in /home/usrname/valgrindtest/out)
==18832==  Address 0x5257054 is 3 bytes after a block of size 1,601 alloc'd
==18832==    at 0x4C31B0F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)

(*vcoord[i]).x = rand_float();中:[]运算符比解引用*具有更高的precedence,所以这里需要的是(*vcoord)[i].x。否则,您会将 vcoord 视为大小为 n 的数组数组,并使用该数组的第 i 个元素,该元素超出范围。同样,对于 vcoordadjm 的所有其他用途,需要 (*adjm)[i][j].

这解释了 1600:Point2D 在您的平台上很可能是 8 个字节,所以 Point2D[200] 是 1600 个字节。因此 vcoord[1] 指的是这些 1600 字节数组的数组元素 1,它位于距基地址 vcoord 的偏移量 1600 处 - 就在末尾。

其他危害较小的错误:

  1. int (*adjm)[n][n] = malloc( sizeof(float[n][n])+1 ); : float 应该是 int 这里, +1 没有用。 (可能不是导致崩溃的原因,因为 floatint 在大多数常见平台上具有相同的大小。)许多人更喜欢通过编写 malloc(sizeof(*adjm)) 来避免这种不匹配的可能性.

  2. free(*adjm);malloc分配的指针在变量adjm中,不在其指向的数组中。在下一行创建 free(adjm);free(vcoord);。但是,它实际上不应该有任何影响,因为 *adjmadjm 指向的数组,并且它会衰减回指向同一地址的指针。

请注意,您在这里选择了一种复杂的代码编写方式。分配像 vcoord 这样的一维数组的正常方法是使用,不是指向数组的指针,而是指向相关对象类型的指针,然后使用 malloc 创建数组以指向它进入。所以更典型的写法是

Point2D *vcoord = malloc(n * sizeof(Point2D));  // or (n * sizeof *vcoord)
vcoord[i].x = rand_float();
free(vcoord);

同样,对于二维数组 adjm,不要使用指向二维数组的指针,而是指向一维数组的数组的指针。所以

int (*adjm)[n] = malloc(n * sizeof(int[n]));
// or: (n * sizeof *adjm)
// or even: (n * n * sizeof(int))
adjm[x][y] = 1;
free(adjm);

这避免了对 2D 数组指针进行棘手的取消引用的需要,并使语法更加简单。