为什么C中返回的结构中的数组没有被释放?

时间:2013-09-27 12:58:45

标签: c arrays struct

我有以下代码:

typedef struct{
    char *name;
    int age;
} person_t;

person_t
read_person(void);

int main(){
    person_t Peter = read_person();
    printf("%s %d\n", Peter.name, Peter.age);
}

person_t
read_person(void) {
    person_t a;
    a.name = "Peter";
    a.age = 18;
    return a;
}

因此,在函数read_person中,返回struct person_t。我想知道在函数完成时是否会释放数组a.name,因为“a”是一个局部变量。

事实证明,程序运行时会打印以下行

Peter 18

表示未释放a.name。有人可以为此提供解释吗?

6 个答案:

答案 0 :(得分:2)

当您从a返回read_person时,它会复制内容。一旦程序离开a,就会删除局部变量read_person的存储,因为它是一个自动变量。

name是一个指针,它将指向一个字符串文字"Peter",它具有静态存储持续时间,这意味着它将持续程序的生命周期,指针不需要{{1 }}编。请注意,尝试修改字符串文字是未定义的行为,因此您无法修改free的内容。

如果另一方面name指向name内存,则需要malloc。或者,您在free中声明了一个局部变量,例如:

read_person

并且您已为此分配 char arr[] = "Peter" ; ,那么您将有未定义的行为,因为一旦您离开aarr将不再存在。

答案 1 :(得分:1)

首先,name不是数组,它是指向char的指针,准确地说,指向字符串文字"Peter"。字符串文字有静态存储,随时可以访问它们。

其次,a是一个局部变量,但是你返回struct而不是一个指针,所以a的值被赋给变量{{1在Peter中可以访问它。

答案 2 :(得分:1)

  

我想知道在函数完成时是否会释放数组a.name,因为“a”是一个局部变量。

read_person未返回对局部变量的引用。而是返回一份副本。所以,程序已定义了行为。

"Peter"是字符串文字,具有静态存储持续时间。

答案 3 :(得分:1)

假设我们使用的是32位机器,因此指针和整数都是4个字节。那么你的结构大小是8个字节(可能更多,但我正在简化)。

在编译时,在静态内存中留出一个位置,其中有足够的空间来容纳6个字符'P', 'e', 't', 'e', 'r'和零空值。 'P'的位置将是一些地址,如A1。此数据位于静态存储器

在运行时间:
首先main()放在堆栈内存上 然后在堆栈上保留8个字节,这是名为peter的变量 然后将read_person()放到堆栈上 然后将另外8个字节放入堆栈;这是名为a的变量 然后将值A1放入a的前4个字节中 然后将值18放入a的后4个字节中 然后函数返回。返回时:a处堆栈中的整个8个字节被复制到peter的堆栈中。然后将read_person从堆栈中取出 所以现在peter的前4个字节包含值A1。第二个4字节包含18.
然后将printf语句放到read_person所在的堆栈中。 4字节值A1和4字节值18也被放到堆栈上以供printf使用 print语句就是这样,并且从堆栈中取回,就像给出的参数一样 Main现在已经完成,它从堆栈中取出,堆栈现在又空了。

注意从堆内存中没有分配任何内容。在任何时候,程序都没有要求操作系统从堆中为我们提供空间以使用malloc或类似的调用。因此,没有什么可以自由的。

答案 4 :(得分:1)

是和否。我认为你在这里缺少的是从函数返回调用的隐式复制操作。 person_t a确实存在于read_person()的堆栈框架上。但是,当您return a时,a中的值会按成员方式复制到位于person_t Peter堆栈框架中的main(),因为分配({{1} }})。复制完成后,Peter = read_person()堆栈帧中的person_t a对象确实被释放(但不是递归的 - 它不会相当于read_person(),我认为你是关心)。

根据您的优化设置和编译器的使用年限,可能会更复杂,将free(a.name)复制到临时未命名的a中,然后将其复制到person_t }。但是,许多编译器可以轻松优化额外的副本。

事实上,它甚至可能比这更简单 - 如果这是整个程序,编译器可以认识到这是Peter的唯一调用,并选择{ {1}}将该函数放入read_person(),之后进一步优化可能会注意到inlinemain()可以合并到一个对象中,在这种情况下a将有效地构建Peter到位,甚至不会有read_person() ...

答案 5 :(得分:-1)

您要问的是可变范围。你是正确的a是在read_person的范围内声明和初始化的,但是通过将它返回到main函数中的变量,你现在将它带入main()范围。主要退出时,它将超出范围。