理解C语言中的内存分配有点困难

理解C语言中的内存分配有点困难,c,memory-management,malloc,dynamic-memory-allocation,C,Memory Management,Malloc,Dynamic Memory Allocation,所以我正在学习如何用C语言编程,并开始学习动态内存分配。我所知道的是,并不是所有的时间你的程序都知道它在运行时需要多少内存 我有以下代码: #include <stdio.h> int main() { int r, c, i, j; printf("Rows?\n"); scanf("%d", &r); printf("Columns?\n"); scanf("%d", &c); int array[r][c]; for

所以我正在学习如何用C语言编程,并开始学习动态内存分配。我所知道的是,并不是所有的时间你的程序都知道它在运行时需要多少内存

我有以下代码:

#include <stdio.h>

int main() {
   int r, c, i, j;
   printf("Rows?\n");
   scanf("%d", &r);
   printf("Columns?\n");
   scanf("%d", &c);

   int array[r][c];
   for (i = 0; i < r; i++)
      for (j = 0; j < c; j++)
         array[i][j] = rand() % 100 + 1;

   return 0;
}
#包括
int main(){
int r,c,i,j;
printf(“行?\n”);
scanf(“%d”、&r);
printf(“列?\n”);
scanf(“%d”、&c);
整数数组[r][c];
对于(i=0;i
因此,如果我想创建一个2D数组,我可以声明一个数组并将数字放在括号中。但在这段代码中,我询问用户想要多少行和列,然后用这些变量声明一个数组,然后用随机整数填充行和列


所以我的问题是:为什么我不在这里使用类似于
malloc
?我的代码不知道在运行时要放入多少行和列,那么为什么我可以使用当前代码访问该数组呢?

您的数组是在堆栈上分配的,因此当函数(在您的例子中,
main()
)退出时,数组消失在空中。如果您使用
malloc()
对其进行分配,内存将在堆上分配,并将永远保持分配状态(直到您
free()
释放它)。数组的大小在运行时已知(但在编译时不知道)

所以我的问题是:为什么我不在这里使用类似malloc的东西?
我的代码不知道我要输入多少行和列 运行时,那么为什么我可以使用当前代码访问该数组

您正在使用一个名为“可变长度数组”的C特性。它在C99中作为一项强制性功能引入,但在C11和C18中对它的支持是可选的。这种动态分配的替代方案有几个局限性,其中包括:

  • 由于该特性是可选的,因此无条件依赖它的代码对于不支持该特性的实现是不可移植的

  • 支持VLA的实现通常将本地VLA存储在堆栈上,如果在运行时数组维度较大,则很容易产生堆栈溢出。(动态分配的空间通常对此类问题不太敏感。大型、固定大小的自动阵列也可能是一个问题,但这些问题的潜在问题在源代码中很明显,并且在测试期间不太可能逃避检测。)

  • 在声明数组之前,程序仍然需要知道数组的维度,并且声明点的维度在数组的生存期内是固定的。与动态分配的空间不同,VLA无法调整大小

  • 有些上下文可以容纳普通的固定长度数组,但不能容纳VLA,例如文件范围变量


在“C”中处理二维数组的经典方法是将其声明为大小足够的一维数组,然后进行例程/宏/计算,根据该数组中指定的行、列、元素大小和列数计算该一维数组的元素数

因此,假设您要计算“specifiedRow”和“specifiedCol”的表中的地址偏移量,并且数组元素的大小为“tableElemSize”,并且该表具有“tableCols”列。该偏移量可以这样计算:

addrOffset = specifiedRow * tableCols * tableElemSize + (specifiedCol * tableElemSize);
然后可以将其添加到表的起始地址,以获得指向所需元素的指针

这是假设您有一个字节数组,而不是整数或其他结构。如果某个值大于一个字节,则不需要“tableElemSize”。这取决于你想如何在记忆中表现出来

我不认为你正在做的事情是可以在很多编译器中移植的,并且会建议你反对。如果你需要一个二维数组,其中的维度可以动态地改变,你可能想考虑一些类似于我在前一个线程中所发布的矩阵“对象”。

另一个解决方案是动态分配的数组。这比编译时分配的2D数组占用的内存多一点,而且数组中的元素不是连续的(这可能会影响一些工作),但它仍然会为您提供“x[i][j]”类型的表示法,这通常是在编译时定义2D数组时得到的。例如,以下代码创建了一个二维整数数组(省略了错误检查以提高可读性):

int**x;
int i,j;
整数计数;
int行,cols;
行=/*从用户或文件读取值*/
cols=/*从文件的用户处读取值*/
x=calloc(sizeof(int*),行数);
对于(i=0;i
这里需要记住的一点是,因为我们使用的是一个数组数组,所以我们不能总是保证每个数组都在下一个内存块中,特别是在同时进行垃圾收集的情况下(如果代码是多线程的,可能会发生这种情况)。即使没有它,内存也不会从一个数组连续到下一个数组(尽管每个数组中的元素都是连续的)。内存分配会带来开销,如果查看组成行的2D数组和1D数组的地址,就会发现这一点。通过打印2D数组和每个1D数组的地址,可以看到这一点,如下所示:

printf("Main Array:  0x%08X\n", x);
for (i = 0; i < rows; i++)
    printf("  0x08X [%04d], x[i], (int) x[i] - (int) x);
Object* makeObject() {
    Object* result = malloc(sizeof(*result));
    result->someMember = ...;
    return result;
}
printf(“主数组:0x%08X\n”,x);
对于(i=0;i
当我用2D arr测试时
Object* makeObject() {
    Object* result = malloc(sizeof(*result));
    result->someMember = ...;
    return result;
}
void destroyObject(Object* object) {
    ...  //some cleanup
    free(object);
}
Object* makeObject() {
    Object result;
    result->someMember = ...;
    return &result;    //Wrong! Don't do this!
}