【问题标题】:Segfault 11 when initializing large arrays dynamically动态初始化大型数组时的 Segfault 11
【发布时间】:2021-05-17 22:40:21
【问题描述】:

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

我的代码(当我将 2D 矩阵的每个条目设置为 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)

【问题讨论】:

  • int (*adjm)[n][n] = malloc( sizeof(float[n][n])+1 );: 看起来你的意思是int 而不是float
  • free(*adjm); 等错误; malloc 返回的指针在adjm 中,而不是在*adjm 中。应该是free(adjm); free(vcoord);
  • 非常感谢!我将 int 更改为 float 但段错误仍然出现在同一位置。还要感谢您指出 free(.) 的错误用法...我真的需要重新整理一下这些内容。
  • 最重要的是,malloc(sizeof(float[n][n])+1) 并没有像您期望的那样分配矩阵。 sizeof(float[n][n]) 等于 sizeof(float*) 可能是 8,因此总共是 9 个字节(总是)。
  • @CaptainTrojan:这不正确。 float[n][n] 本身不是指针,衰减不会在这里发生。但是+1 没有任何意义。

标签: c memory-management segmentation-fault


【解决方案1】:

(*vcoord[i]).x = rand_float(); 中:[] 运算符的precedence 比取消引用的* 更高,所以这里需要的是(*vcoord)[i].x。否则,您会将vcoord 视为大小为n 的数组的数组,并使用该数组的ith 元素,这是超出范围的。对于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);

这避免了对二维数组指针的复杂取消引用,并使语法更加简单。

【讨论】:

  • 谢谢!那是一个鬼鬼祟祟的错误......我并没有太在意我把括号放在哪里 - 下次肯定要更加小心。编辑:我按照您的建议修复了括号,现在代码工作正常。还有一个问题,当我尝试使用 n=200 进行调试时,我可以问一下 Valgrind 中的 1600 字节是从哪里来的吗?
  • @Macrophage:查看编辑,包括其他一些建议。
  • 感谢您的详细解答!是的,我的代码很笨拙,但现在我更好地理解了这些。
  • @M.M 那么他忘记了 = 吗?另外,我应该使用 vcoord = malloc(...) 还是指针 *vcoord = malloc(...),哪个是最佳实践?
  • @Macrophage:你不能这样做Point2D vcoord = malloc(...);。它没有意义,甚至不会编译; malloc 返回一个指针,它必须分配给一个指针。
猜你喜欢
  • 1970-01-01
  • 2016-12-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-09-21
相关资源
最近更新 更多