这只是为了澄清我在C/C中长期以来的一些困惑:如果我的理解是正确的......在C/C中,你可以通过函数的值(复制)返回一个局部结构体,但不能返回一个局部数组,这是真的吗?例如(我对事件的理解已经在注解中陈述,其中?
表示一个Assert,我不完全有信心做出):
#include <stdio.h>
struct my_structure { int x; int y; };
struct my_structure foo(int a, int b)
{
// declare struct variable (will allocate 4 + 4 bytes on foo()'s stack frame for 2 ints)
struct my_structure ms;
// do some kind of processing on the struct variable; just for example, let's say...
ms.x = a*a;
ms.y = b*b;
// ? return by value: will COPY the 4 + 4 bytes into caller (main)
return ms;
}
int main() {
struct my_structure retval; // will allocate 4 + 4 bytes on the main()'s stack frame
retval = foo(10, 20); // ? will copy foo()'s local struct `ms` into the 4 + 4 bytes allocated in above declaration
printf("%d, %d\n", retval.x, retval.y); // 100, 400
}
如果我没有错的话,在上面的代码示例中,不存在返回任何类型的地址的问题--所有的事情都是纯粹根据值和复制来处理的,所以这样返回结构体应该是安全的,尽管对于大型结构体来说效率不高。
现在来看看函数局部数组,我看过一些帖子(比如this),说你不能从函数返回局部数组。我想知道的是为什么你不能像使用结构体那样,从被调用函数的堆栈向调用者的堆栈按字节复制数组值(就像上面的代码示例)?
这是我的猜测:按名称返回数组时,C/C++编译器会隐式将名称转换为指向第一个元素的指针(数组到指针的衰减?)所以不是返回一个副本 *,而是最终返回一个数组的基址,当变量超出作用域时,该数组不会指向任何东西 *(这是非常糟糕的,因此不应该这样做)。这不是结构体的情况,其中通过名称返回结构体变量,简单地返回结构变量作为一个副本(没有衰减到指针)。我是正确的还是我的理解有缺陷?任何建议都是受欢迎的。谢谢。
编辑:我的问题主要是针对C的,但是因为C应该是一个超集,我假设它也适用于C?
2条答案
按热度按时间vq8itlhq1#
为什么你不能像用结构体那样把数组值按字节从被调用函数的堆栈复制到调用者的堆栈...
C本可以允许这一点--而且确实允许了。
一些早期的C编译器允许它--差不多吧。
但是现在C语言不允许复制带有
=
的数组。现在,代码可以通过复制
struct
来复制struct
中的数组。在代码评审中解释为什么需要这样做会很有趣。
至于“为什么”,我怀疑既然这被认为是一种糟糕的做法,就没有必要支持它。
pgvzfuti2#
在C/C中,可以通过函数值(复制)返回局部结构体,但不能返回局部数组,这是真的吗?
这在C语言中是正确的。C是一种不同的语言,但我非常肯定它在那里也是正确的。C++***不是***C的超集,但它们有一个共享的遗产和一个公共的子集。一般来说,假设对C语言正确的语句对C也正确,或者 * 反之亦然 * 是不安全的。
如果我没有错的话,在上面的代码示例中,不存在返回任何类型的地址的问题---所有的事情都是纯粹根据值和复制来处理的,所以以这种方式返回结构体应该是安全的,尽管对于大型结构体来说效率不高。
C语义允许复制结构体,包括返回它们。这意味着复制结构体 * 值 *,而不是它们的地址。不会出现别名,如果副本存储在对象中,则该对象有自己的生命周期,独立于源对象的生命周期。
现在来谈谈函数局部数组,我看过一些帖子(比如这个帖子),说你不能从函数返回局部数组。我想知道的是为什么你不能像用结构体那样,把数组值按字节从被调用函数的堆栈复制到调用者的堆栈(就像上面的代码示例)?
谁是“你”?C语言的设计不是这样的。它本来可以这样做,但它没有。你甚至不能用C语言“表达”它(在C中为),因为数组类型的表达式会自动转换(“衰减”)到指向大多数上下文中的第一数组元素的指针,包括当它们作为函数参数出现或者出现在
return
语句中的时候。这或多或少和你假设的一样。是的,这样的指针在它所指向的对象的生命周期结束时失效。对于自动对象,这不晚于分配它的函数执行的终止。但是,你当然可以通过
memcpy()
函数来执行这样的复制,你只需要数据应该复制到的地址和数据的大小,而且你必须在源对象和目标对象的生命周期重叠的时候执行。