13

最近有人在我正在使用的一段代码中向我指出

char* name = malloc(256*sizeof(char));
// more code
free(name);

我的印象是这种设置数组的方式与使用

char name[256];

并且这两种方式都需要使用free()。我错了,如果是这样,有人可以用低级的术语解释一下有什么区别吗?

4

6 回答 6

29

在第一个代码中,内存是在堆上动态分配的。需要使用 free() 释放该内存。它的生命周期是任意的:它可以跨越函数边界等。

在第二个代码中,256 个字节分配在堆栈上,并在函数返回时自动回收(或者如果它在所有函数之外,则在程序终止时)。因此,您不必(也不能)对其调用 free() 。它不能泄漏,但它也不会超过函数的末尾。

根据对内存的要求在两者之间进行选择。

附录(人):

如果我可以补充一点,Ned,大多数实现通常会提供比堆栈更多的堆(至少在默认情况下)。这对于 256 字节通常无关紧要,除非您已经用完堆栈或执行大量递归操作。

此外,根据标准, sizeof(char) 始终为 1,因此您不需要多余的乘法。即使编译器可能会优化它,它也会使代码变得丑陋 IMNSHO。

结束附录(Pax)。

于 2009-01-07T02:48:14.687 回答
7

并且这两种方式都需要使用free()。

不,只有第一个需要免费使用。第二个分配在堆栈上。这使得分配速度非常快。看这里:

void doit() {
    /* ... */
    /* SP += 10 * sizeof(int) */
    int a[10];
    /* ... (using a) */

} /* SP -= 10 */

当您创建它时,编译器在编译时知道它的大小,并将在堆栈中为其分配正确的大小。堆栈是位于某处的一大块连续内存。将某些内容放入堆栈只会增加(或减少取决于您的平台)堆栈指针。超出范围将做相反的事情,并且您的数组被释放。这将自动发生。因此,以这种方式创建的变量具有自动存储持续时间。

使用 malloc 是不同的。它将订购一些任意的大内存块(来自一个名为freestore的地方)。运行时将不得不查找相当大的内存块。大小可以在运行时确定,因此编译器一般无法在编译时对其进行优化。因为指针可以超出范围,或者被复制,分配的内存和分配内存地址的指针之间没有内在的耦合,所以即使你很久以前离开函数,内存仍然是分配的. 如果时间到了,您必须调用 free 并手动将您从 malloc 获得的地址传递给它。

一些“最新”形式的 C,称为 C99,允许您为数组指定运行时大小。即你可以这样做:

void doit(int n) {
    int a[n]; // allocate n * sizeof(int) size on the stack */
}

但是,如果您没有理由使用它,最好避免使用该功能。一个原因是它不是故障安全的:如果没有可用的内存,任何事情都可能发生。另一个是 C99 在编译器中的可移植性不是很好。

于 2009-01-07T02:52:39.187 回答
6

这里有第三种可能性,即数组可以在函数外部声明,但是是静态的,例如,

// file foo.c
char name[256];

int foo() {
    // do something here.
}

我对另一个关于 SO 的问题的回答感到相当惊讶,有人认为这在 C 中是不合适的;这里甚至没有提到,对此我有点困惑和惊讶(比如“他们这些天在学校教孩子什么?”)。

如果使用此定义,则内存是静态分配的,既不在堆上,也不在堆栈上,而是在映像的数据空间中。因此,既不必像 malloc/free 那样进行管理,也不必像使用自动定义那样担心地址被重用。

在这里回顾整个“声明”与“定义”的事情很有用。这是一个例子

/* example.c */

char buf1[256] ;           /* declared extern, defined in data space */
static char buf2[256] ;    /* declared static, defined in data space */
char * buf3 ;              /* declared extern, defined one ptr in data space */
int example(int c) {       /* c declared here, defined on stack */
    char buf4[256] ;       /* declared here, defined on stack   */
    char * buf5 = malloc(256)]   /* pointer declared here, defined on stack */
                           /* and buf4 is address of 256 bytes alloc'd on heap */
    buf3 = malloc(256);    /* now buf3 contains address of 256 more bytes on heap */

    return 0;              /* stack unwound; buf4 and buf5 lost.      */
                           /* NOTICE buf4 memory on heap still allocated */
                           /* so this leaks 256 bytes of memory */
}

现在在一个完全不同的文件中

/* example2.c */

extern char buf1[];             /* gets the SAME chunk of memory as from example.c */
static char buf2[256];          /* DIFFERENT 256 char buffer than example.c */
extern char * buf3 ;            /* Same pointer as from example.c */
void undoes() {
     free(buf3);                /* this will work as long as example() called first */
     return ;
}
于 2009-01-07T04:07:28.473 回答
2

这是不正确的 - 数组声明不需要 free。此外,如果这是在函数内,它会在堆栈上分配(如果内存服务)并随着函数返回自动释放 - 不要将引用传递回调用者!

于 2009-01-07T02:46:44.177 回答
2

分解你的陈述

char* name = malloc(256*sizeof(char)); // one statement
char *name; // Step 1 declare pointer to character
name = malloc(256*sizeof(char)); // assign address to pointer of memory from heap
name[2]; // access 3rd item in array
*(name+2); // access 3rd item in array
name++; // move name to item 1

翻译:name 现在是一个指向字符的指针,它被分配了堆上一些内存的地址

char name[256]; // declare an array on the stack
name++; // error name is a constant pointer
*(name+2); // access 3rd item in array
name[2]; // access 3rd item in array
char *p = name;
p[2]; // access 3rd item in array
*(p+2); // access 3rd item in array
p++; // move p to item 1
p[0]; // item 1 in array

翻译:name是一个指向某个字符的常量指针,该字符指向栈上的一些内存

在 C 中,数组和指针或多或少是一回事。数组是指向内存的常量指针。主要区别在于,当您调用 malloc 时,您从堆中获取内存,并且从堆中获取的任何内存都必须从堆中释放。当您声明具有大小的数组时,它会从堆栈中分配内存。您无法释放此内存,因为 free 是为了从堆中释放内存。当前程序单元返回时,堆栈上的内存将自动释放。在第二个例子中,free(p) 也是一个错误。p 是堆栈上的名称数组的指针。因此,通过释放 p 您正在尝试释放堆栈上的内存。

这与:

int n = 10;
int *p = &n;

在这种情况下释放 p 将是一个错误,因为 p 指向 n ,它是堆栈上的一个变量。因此 p 在堆栈中拥有一个内存位置并且不能被释放。

int *p = (int *) malloc(sizeof(int));
*p = 10;
free(p);

在这种情况下,free 是正确的,因为 p 指向堆上由 malloc 分配的内存位置。

于 2009-01-07T04:34:28.390 回答
0

根据您在哪里运行,堆栈空间可能非常宝贵。例如,如果您正在为 Verizon/Alltel 手机编写 BREW 代码,那么您通常会被限制在极小的堆栈中,但会不断增加堆访问。

此外,由于 char[] 最常用于字符串,因此允许字符串构造方法为所讨论的字符串分配所需的内存并不是一个坏主意,而不是希望永远永远是 256(或任何数字法令)就足够了。

于 2009-02-11T20:11:36.220 回答