1

我有以下代码:

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 中,返回了一个结构 person_t。我想知道当函数完成时数组 a.name 是否会被释放,因为“a”是一个局部变量。

原来程序运行时打印了下面一行

Peter 18

这意味着 a.name 没有被释放。有人可以对此提供解释吗?

4

6 回答 6

2

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

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

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

 char arr[] = "Peter" ;

并且您分配a给它,那么您将有未定义的行为,因为arr一旦您离开就不再存在read_person

于 2013-09-27T13:03:24.960 回答
1

首先,name它不是一个数组,它是一个指针char,准确地说,是一个字符串字面量的指针"Peter"。而且字符串字面量是静态存储的,随时可以访问。

其次,它确实a是一个局部变量,但是你返回的是struct,而不是指针,所以 的值a被分配给变量Peter,可以在 中访问它main

于 2013-09-27T13:03:20.133 回答
1

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

read_person没有返回对局部变量的引用。相反,它正在返回一个副本。因此,该程序具有定义的行为。

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

于 2013-09-27T13:06:51.203 回答
1

假设我们在 32 位机器上,所以指针和整数都是 4 个字节。然后你的结构的大小是 8 个字节(它可能更多,但我正在简化)。

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

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

注意堆内存是如何分配的。程序在任何时候都没有要求操作系统从堆中给我们空间来使用malloc或类似的调用。因此,没有什么可以免费的。

于 2013-09-27T13:17:01.840 回答
1

是和不是。我认为您在这里缺少的是从函数返回调用的隐式复制操作。确实person_t a存在于read_person(). 但是,当 you 时,由于赋值 ( ) ,将 in 中return a的值按a成员复制到person_t Peter位于main()s 堆栈帧中的 中。Peter = read_person()复制完成后, s 堆栈帧中的person_t a对象read_person()确实被释放(但不是递归的 - 它不做相当于free(a.name),我认为这是你所关心的)。

根据您的优化设置和编译器的年龄,它可能会更复杂,a被复制到一个临时的 unnamedperson_t中,然后将其复制到Peter. 但是,许多编译器可以轻松优化多余的副本。

事实上,它甚至可以比这更简单——如果这是你的程序的全部,编译器可以识别出这是对 的唯一调用read_person(),并选择inline函数 into main(),之后进一步的优化可能会注意到这一点a并且Peter可以被折叠成一个对象,在这种情况下read_person()将有效地Peter在原地构建,甚至永远不会有一个a......

于 2013-09-27T13:21:24.450 回答
-1

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

于 2013-09-27T13:08:13.233 回答