为什么在 C 允许的函数中返回堆栈分配的指针变量?
Why is returning a stack allocated pointer variable in a function allowed in C?
我已阅读以下post:
这表明指向堆分配变量的指针是 returned 是好的。但是,指针在技术上是 "stack allocated variable",然后会在 return 调用函数时被释放吗?
例如:
int* test(){
int arr[5];
int *ptr = arr;
return ptr; //deallocated ptr?
}
int *test2(){
int arr[5];
return arr;
}
测试中
另外,arr是一个指针,指向某个新创建的int数组arr,指向&arr[0]
对吗?如果arr
不是指针,为什么return满足函数return类型才有效?
既然 ptr 和 arr 都应该是堆栈分配的,为什么代码只能在 test()
而不是 test2()
中工作? test() 是否给出未定义的行为?
如果访问 returned 值,它们都将是 undefined behaviour。所以,其中 none 个是 "OK"。
您正在尝试 return 一个指向具有 auto
存储持续时间的块范围变量的指针。所以,一旦范围结束,变量的生命周期就结束了。
引用 C11
,章节 §6.2.4/P2,关于 生命周期(强调我的)
The lifetime of an object is the portion of program execution during which storage is
guaranteed to be reserved for it. An object exists, has a constant address, and retains
its last-stored value throughout its lifetime. If an object is referred to outside of its
lifetime, the behavior is undefined [...]
然后,从P5开始,
An object whose identifier is declared with no linkage and without the storage-class
specifier static has automatic storage duration, [...]
和
For such an object that does not have a variable length array type, its lifetime extends
from entry into the block with which it is associated until execution of that block ends in
any way. [...]
因此,在您的情况下,变量 arr
具有自动存储功能,并且其生命周期仅限于函数体。一旦地址被 returned 给调用者,尝试访问该地址的内存将是 UB。
哦,C 标准中没有 "stack" 或 "heap",我们只有变量的生命周期。
这两种情况在技术上是相同的。
在这两种情况下,都会返回指向 arr 的指针。虽然返回指针的值确实指向了用于包含 arr 的内存,但 arr 已经从内存中释放了。
因此,有时当您访问指针时,您仍然会在那里找到 arr 的内容,这些内容只是碰巧还没有被覆盖。其他时候,你可能会在这段内存被覆盖后访问它,并得到未定义的数据甚至段错误。
test
和 test2()
是等价的。它们 return 一个你不能解除引用的实现定义的指针,否则 UB 随之而来。
如果您不取消引用 returned 指针,调用 test()
或 test2()
不会导致未定义的行为,但这样的函数可能不是很有用。
进入一个函数后,一个新的栈帧被添加到栈中。堆栈帧是存储所有 autos(函数中声明的非静态变量)的地方。当我们离开函数时,return 值被放置在 CPU 中的寄存器(通常是 R0)中,然后减少堆栈指针以删除堆栈帧。然后我们 return 控制到我们调用函数的地方,我们从寄存器中得到 return 值。
所以在这种情况下你有 int arr[5]
,当程序进入函数时,一个新的栈帧被添加到栈中。在这个堆栈帧中,数组中有 5 个整数的内存,变量 arr
现在确实相当于指向数组中第一个元素的指针。当您 return 变量 arr
时,您正在 return 指向堆栈帧中数据的指针,当函数退出并且您 return 回到上一个函数时,堆栈然后减少指针以删除您刚刚退出的函数的堆栈帧。
指针仍指向内存中我们之前分配数组的位置。因此,当堆栈增加时,arr
指向的内存将被覆盖。更改 returned 值指向的数据可能会导致一些非常 "exciting" 的事情发生,因为我们不知道内存现在用于什么时候。
数组与指针示例:
char arr[5];
char * ptr = arr;
在这种情况下,编译器知道 arr
的大小,但不知道 ptr
的大小,因此我们可以执行 sizeof(arr) ,编译器将在编译时进行计算。到运行时,它们在内存中是等值的。
你似乎仍然对指针也是一个自动变量感到困惑,所以你担心 returning 它会无效,即使它指向一些有效的内存(比如,一个静态数组).
重要的是要记住,在 C 中,所有参数和 return 值的传递都是 按值完成的。 如果您 "return a pointer" 如 return p;
它与 "returned an integer" 中的机制完全相同,如 return i;
中:变量的 value 被复制到某处并由调用者获取。在 i
的情况下,该值可能是 42;在 p
的情况下,该值可能是 3735928559(或者换句话说,0xdeadbeef)。 value 表示内存中的位置,例如你的数组在它不复存在之前就存在了,因为函数returned。当你复制它时,地址不会改变超过 42 个变化,并且完全独立于曾经包含它的变量 p
的生命周期——毕竟,它是及时复制出来的。1
1这超出了问题的范围,但技术上概念上,为return 值。现代 C++ 对临时对象的生命周期和语义进行了更系统的分类。
我已阅读以下post:
这表明指向堆分配变量的指针是 returned 是好的。但是,指针在技术上是 "stack allocated variable",然后会在 return 调用函数时被释放吗?
例如:
int* test(){
int arr[5];
int *ptr = arr;
return ptr; //deallocated ptr?
}
int *test2(){
int arr[5];
return arr;
}
测试中
另外,arr是一个指针,指向某个新创建的int数组arr,指向&arr[0]
对吗?如果arr
不是指针,为什么return满足函数return类型才有效?
既然 ptr 和 arr 都应该是堆栈分配的,为什么代码只能在 test()
而不是 test2()
中工作? test() 是否给出未定义的行为?
如果访问 returned 值,它们都将是 undefined behaviour。所以,其中 none 个是 "OK"。
您正在尝试 return 一个指向具有 auto
存储持续时间的块范围变量的指针。所以,一旦范围结束,变量的生命周期就结束了。
引用 C11
,章节 §6.2.4/P2,关于 生命周期(强调我的)
The lifetime of an object is the portion of program execution during which storage is guaranteed to be reserved for it. An object exists, has a constant address, and retains its last-stored value throughout its lifetime. If an object is referred to outside of its lifetime, the behavior is undefined [...]
然后,从P5开始,
An object whose identifier is declared with no linkage and without the storage-class specifier static has automatic storage duration, [...]
和
For such an object that does not have a variable length array type, its lifetime extends from entry into the block with which it is associated until execution of that block ends in any way. [...]
因此,在您的情况下,变量 arr
具有自动存储功能,并且其生命周期仅限于函数体。一旦地址被 returned 给调用者,尝试访问该地址的内存将是 UB。
哦,C 标准中没有 "stack" 或 "heap",我们只有变量的生命周期。
这两种情况在技术上是相同的。
在这两种情况下,都会返回指向 arr 的指针。虽然返回指针的值确实指向了用于包含 arr 的内存,但 arr 已经从内存中释放了。
因此,有时当您访问指针时,您仍然会在那里找到 arr 的内容,这些内容只是碰巧还没有被覆盖。其他时候,你可能会在这段内存被覆盖后访问它,并得到未定义的数据甚至段错误。
test
和 test2()
是等价的。它们 return 一个你不能解除引用的实现定义的指针,否则 UB 随之而来。
如果您不取消引用 returned 指针,调用 test()
或 test2()
不会导致未定义的行为,但这样的函数可能不是很有用。
进入一个函数后,一个新的栈帧被添加到栈中。堆栈帧是存储所有 autos(函数中声明的非静态变量)的地方。当我们离开函数时,return 值被放置在 CPU 中的寄存器(通常是 R0)中,然后减少堆栈指针以删除堆栈帧。然后我们 return 控制到我们调用函数的地方,我们从寄存器中得到 return 值。
所以在这种情况下你有 int arr[5]
,当程序进入函数时,一个新的栈帧被添加到栈中。在这个堆栈帧中,数组中有 5 个整数的内存,变量 arr
现在确实相当于指向数组中第一个元素的指针。当您 return 变量 arr
时,您正在 return 指向堆栈帧中数据的指针,当函数退出并且您 return 回到上一个函数时,堆栈然后减少指针以删除您刚刚退出的函数的堆栈帧。
指针仍指向内存中我们之前分配数组的位置。因此,当堆栈增加时,arr
指向的内存将被覆盖。更改 returned 值指向的数据可能会导致一些非常 "exciting" 的事情发生,因为我们不知道内存现在用于什么时候。
数组与指针示例:
char arr[5];
char * ptr = arr;
在这种情况下,编译器知道 arr
的大小,但不知道 ptr
的大小,因此我们可以执行 sizeof(arr) ,编译器将在编译时进行计算。到运行时,它们在内存中是等值的。
你似乎仍然对指针也是一个自动变量感到困惑,所以你担心 returning 它会无效,即使它指向一些有效的内存(比如,一个静态数组).
重要的是要记住,在 C 中,所有参数和 return 值的传递都是 按值完成的。 如果您 "return a pointer" 如 return p;
它与 "returned an integer" 中的机制完全相同,如 return i;
中:变量的 value 被复制到某处并由调用者获取。在 i
的情况下,该值可能是 42;在 p
的情况下,该值可能是 3735928559(或者换句话说,0xdeadbeef)。 value 表示内存中的位置,例如你的数组在它不复存在之前就存在了,因为函数returned。当你复制它时,地址不会改变超过 42 个变化,并且完全独立于曾经包含它的变量 p
的生命周期——毕竟,它是及时复制出来的。1
1这超出了问题的范围,但技术上概念上,为return 值。现代 C++ 对临时对象的生命周期和语义进行了更系统的分类。