我想知道是否有人可以解释ai
和*pai
的内存分配之间的差异
int ai[10];
int *pai = (int * ) calloc (10, sizeof(int));
我理解第二个是动态分配但我很难解释原因。
答案 0 :(得分:3)
让我们看看标准中指定的是什么(差异化)
来自7.22.3.1(在内存管理功能下)
...分配对象的生命周期从分配中扩展 直到解除分配。
所以是的,这是动态分配的内存。它们的寿命与局部变量的寿命不同。通过免费呼叫他们被解除分配。在那之前他们将活着。不依赖于创建它们的范围的生命周期。
第一个是自动存储持续时间。这是主要的区别。因此,在声明它的函数范围内,当它结束时,它的生命周期将结束。
也有人说有堆和堆栈 - 但是(un)幸运的是C标准没有提到它。它完全实现了C标准所期望的功能。实施可以是任何事情。所提出的差异对这类事物的影响最小。
作为概念性redpill(取自电影Matrix
)pai
具有自动存储持续时间,但其包含的内存地址不是。执行定义它的函数时,变量pai
将丢失。但它指出的记忆并没有。
为什么称为动态分配?
知道一件事 - 在编程中我们在语言环境中说dynamic
- 这意味着我们在运行时做了一些事情。同样,我们在运行时通过调用malloc
,calloc
等函数来分配一些内存。这就是动态分配的原因。
答案 1 :(得分:0)
数组ai
在堆栈上分配,当到达函数末尾时,它隐式超出范围。指针pai
指向指向内存位置,该位置可以是指向的类型的数组或单个元素,内存在堆上分配,且必须为free
d以后。第二个可以传递回函数末尾的函数调用者,甚至可以使用realloc
调整大小(realloc
不会像calloc
那样清除新内存,{{ 1}}就像malloc
而没有清零新内存)。第一种是快速数组计算,大部分时间都应该在缓存中。第二个是调用函数时未知的数组长度。当大小已知时,许多程序员倾向于在调用者中定义一个数组并将其传递给函数,该函数修改它。调用函数时,数组被隐式转换为指针。
某些库实现在全局部分中存储指向数组的指针,可以重新分配。或者它们在全球空间中具有固定长度的阵列。建议这些变量为calloc
。用户不必关心其他库变量的内存管理。
<强> library.h 强>
thread_local
<强> LIBRARY.C 强>
const char* getResourceString(int id);
答案 2 :(得分:0)
一般来说,自动分配的对象将位于堆栈上,而动态分配的对象将位于堆上。虽然这种区别是实现(非标准)依赖,但堆栈和堆是在C程序中管理内存的最常用方法。它们基本上是两个不同的内存区域,第一个专用于自动分配,第二个专用于动态分配。因此,当您调用函数(例如,main函数)时,在此函数范围内声明的所有对象将被堆叠(在堆栈中自动分配)。如果在此函数中发生了一些动态分配,则将在堆中分配内存,以便指向此区域的所有指针都将指向堆栈外部的对象。当您的函数返回时,堆栈中的所有对象也会自动取消堆叠,并且几乎不再存在。但是,在解除分配它们之前,堆中的所有对象都将存在(或者当程序结束时,它们将被OS强制释放)。 数组是可以自动或动态分配的结构。见这个例子:
int * automaticFactor() //wrong, see below
{
int x[10];
return &x[0];
}
int * dynamicFactor()
{
int * y = (int *) malloc(sizeof(int) * 10);
return &y[0];
}
int main()
{
//this will not work because &x[0] points to the stack
//and that area will be unstacked after the function return
int * n = automaticFactor();
//this will work because &y[0] points to the heap
//and that area will be in the heap until manual deallocation
int * m = dynamicFactor();
return 0;
}
请注意,指针本身位于堆栈中。堆中的内容是它们指向的区域。因此,当您在函数内部声明一个指针(例如示例的 y )时,它也会在函数末尾被取消堆栈。但是由于它的值(即分配区域的地址)被返回到函数外部的指针(即 m ),因此您不会忘记函数在堆中分配的区域。
答案 3 :(得分:0)
在第一行中,您创建一个数组类型的变量,但符号ai是指向此变量的常量指针。
在第二行中,您创建一个指针类型变量。然后用calloc()动态分配一个数组,并将它的地址放在指针中。
答案 4 :(得分:0)
这些是完全不同的操作:
int ai[10];
声明一个10个整数的数组对象。如果它在块内声明,它将具有自动存储持续时间,这意味着它将在块结束时消失(标识符和数据)。如果它在任何块之外声明(在文件级别)它将具有静态存储持续时间并且将存在于所有程序中。
int *pai = calloc (10, sizeof(int)); // DON'T CAST MALLOC IN C
声明一个指向已分配的内存区域的指针,该区域可以包含十个整数。您可以使用pai
作为指向数组第一个元素的指针,并在其上执行指针算术。但sizeof(pai)
是sizeof(int *)
。该阵列将具有动态存储持续时间,这意味着它的生命将结束:
如果它被重复用于存储其他对象
double * pd = pai;
for (int i=1; i<5; i++) { // assuming sizeof(double) == 2 * sizeof(int) here
pd[i] = i; // the allocated memory now contains 5 double
}
因此,在这两种情况下,你可以使用标识符指向10个整数的数组,但第一个是整数数组对象,而第二个只是指向动态内存块的指针(没有声明类型的内存可以获取将在那里复制/创建的对象的类型。