当函数返回时,临时存储如何在C中工作?

时间:2010-07-26 21:18:52

标签: c memory-management

我非常了解C,但我对临时存储的工作原理感到困惑。

就像函数返回时一样,在该函数内发生的所有分配都被释放(从堆栈中或者实现决定这样做)。

例如:

void f() {
    int a = 5;
} // a's value doesn't exist anymore 

但是,我们可以使用return关键字将一些数据传输到外部世界:

int f() {
    int a = 5;
    return a;
} // a's value exists because it's transfered to the outside world

如果有任何错误,请阻止我。

现在这是奇怪的事情,当你使用arrays执行此操作时,它不起作用。

int []f() {
    int a[1] = {5};
    return a;
} // a's value doesn't exist. WHY?

我知道数组只能通过指针访问,并且你不能像其他数据结构那样传递数组而不使用指针。这是你无法返回数组并在外界使用它们的原因吗?因为它们只能通过指针访问?

我知道我可以使用动态分配将数据保存到外部世界,但我的问题是关于临时分配。

谢谢!

6 个答案:

答案 0 :(得分:10)

当您返回某些内容时,会复制其值。 a在第二个示例中,在函数外部存在;它的价值确实如此。 (它作为右值存在。)

在上一个示例中,您隐式将数组a转换为int*,并返回该副本。 a的生命终结,你指着垃圾。

永远不会有任何变量超出其范围。

答案 1 :(得分:1)

在第一个示例中,数据被复制并返回到调用函数,但是第二个返回一个指针,因此指针被复制并返回,但是指向的数据被清除。

答案 2 :(得分:0)

在C I的实现中(主要用于嵌入式8/16位微控制器),在调用函数时为堆栈中的返回值分配空间。

在调用函数之前,假设堆栈是这样的(行可以表示各种长度,但都是固定的):

[whatever]
...

当调用例程(例如sometype myFunc(arg1,arg2))时,C会将函数的参数(返回值的参数和空间,它们都是固定长度)抛出到堆栈,后跟返回地址以继续执行代码,并可能备份一些处理器寄存器。

[myFunc local variables...]
[return address after myFunc is done]
[myFunc argument 1]
[myFunc argument 2]
[myFunc return value]
[whatever]
...

当函数完全完成并返回到它被调用的代码时,它的所有变量都已从堆栈中释放出来(理论上它们可能仍然存在,但有< em>没有保证)

在任何情况下,为了返回数组,你需要在其他地方为它分配空间,然后将地址返回到第0个元素。

有些编译器会将返回值存储在处理器的临时寄存器中,而不是使用堆栈,但这种情况很少见(仅在某些AVR编译器上看到过)。

答案 3 :(得分:0)

当您尝试返回本地分配的数组时,调用函数会获得一个指针,指向用于的数组在堆栈中的位置。这可能会造成一些非常令人毛骨悚然的崩溃,在以后,其他东西会写入数组,并且会破坏堆栈帧...如果被破坏的帧在调用序列中很深,那么它可能直到很久才会显现出来。调试此类错误令人抓狂的是,真正的错误(返回本地数组)可以使其他一些绝对完美的功能爆炸。

答案 4 :(得分:0)

你仍然返回一个内存地址,你可以尝试检查它的值,但是它指向的内容在函数范围之外是无效的,所以不要将值与引用混淆。

答案 5 :(得分:0)

int []f() {
    int a[1] = {5};
    return a;
} // a's value doesn't exist. WHY?

首先,编译器不知道要返回的数组大小。我在使用你的代码时遇到了语法错误,但是使用了typedef,我得到了一个错误,表示函数不能返回数组,我知道。

typedef int ia[1];
ia h(void) {
    ia a = 5;
    return a;
}

其次,你不能这样做。你也做不到

int a[1] = {4};
int b[1];
b = a; // Here both a and b are interpreted as pointer literals or pointer arithmatic

虽然你没有这样写出来,并且编译器实际上甚至不需要为它生成任何代码,但是这种操作必须在语义上发生才能实现这一点,以便可以使用新的变量名称引用函数返回的值。如果将它包含在结构中,则编译器可以很好地复制数据。

此外,在声明和sizeof语句之外(如果编译器具有该扩展名,则可能是操作类型)每当数组名称出现在代码中时,编译器都会将其视为指针文字或指针算术块这会产生一个指针。这意味着return语句看起来就像你返回了错误的类型 - 指针而不是数组。

如果你想知道为什么不能这样做 - 它就是不能。编译器可以隐含地考虑数组,就好像它在结构中并使它发生一样,但这不是C标准所说的那样。