如何解决没有数组的结构的生命周期
使用此代码:
#include <stdio.h>
struct S { int x; int y; };
struct S foo(int a,int b) {
struct S s = { a,b };
return s;
}
int main() {
int a;
a = foo(2,4).x;
printf("%d\n",a);
return 0;
}
它按预期工作。我担心的是返回的struct对象的生存期。我知道有关包含数组的结构的临时生存期的标准讨论,但是在这种情况下,结构中没有数组。
所以我猜想foo()
一结束,它的返回值就应该死了,对吧?但是显然,我们仍然可以访问x
成员。为什么?
解决方法
“ 我知道标准谈论包含数组的结构的临时生存期,但是在这种情况下,结构中没有数组。”
您的意思是本段:
具有结构或联合类型的非左值表达式,其中结构或联合包含具有数组类型的成员(递归包括所有包含的结构和联合的成员)是指具有自动存储期限的对象,并且临时寿命。 36)它的寿命始于对表达式求值,并且其初始值是表达式的值。当包含完整表达式的求值结束时,其生存期结束。任何试图使用临时生存期修改对象的尝试都会导致未定义的行为。具有临时生存期的对象的行为就好像是为了有效类型而用其值的类型声明了该对象一样。这样的对象不必具有唯一的地址。
36)当访问数组成员时,将隐式获取此类对象的地址。
来源:ISO / IEC 9899:2018(C18),§6.2.4/ 8
临时寿命是在包含数组成员的结构和联合的上下文中明确发明的,因为自array to pointer decay起,通过其名称访问数组成员将为您提供第一个指针的指针数组的元素,在C11中添加此段之前,它会在较早的C标准中调用未定义的行为。
克里斯·多德(Chris Dodd)解释得更好here。
因此,临时寿命(在标准中意指)与具有非数组成员的结构无关。
“ 所以我想
foo()
结束后,它的返回值应该是无效的,对吧?”
不。 foo()
返回struct S
对象的副本,而不是对本地struct S
对象的引用。请注意,foo()
的返回类型是struct S
,而不是struct S *
(指向struct S
的指针)。
“ 但是显然我们仍然可以访问
x
成员。为什么?”
因为您将副本退回给呼叫者。您尝试访问此副本的成员x
,而不是struct S
内部的s
对象foo()
。
对foo(2,4)
的调用返回函数内变量s
的副本。
此返回的(和临时的)副本的生存期为完整表达式(即赋值a = foo(2,4).x
)的末尾。
这意味着对a
的分配是在临时结构的生存期结束之前完成的,这意味着您显示的代码是正确且有效的。
例如,您可以阅读有关寿命的更多信息。 this reference。
, foo
返回的结构只是一个值(也称为右值)。它不是对象,并且没有生存期。
考虑一个函数int foo(void) { return 3; }
。这将返回int
的值为3,并且我们不能期望像printf("%p",(void *) &foo());
那样使用它的地址。 3只是计算机中使用的值,没有关联的存储。
类似地,给定struct S { int x,y; }
,struct S foo(void) { return (struct S) { 3,4 }; }
返回一个包含3和4的struct S
值。尽管我们经常将结构视为内存布局,但C标准将此返回值视为一个值。它是一个复合值,具有多个部分,但是它只是一个没有关联存储的值。它不是C模型中的对象。
同样,给定struct S { int x,y[1]; }
,struct S foo(void) { return (struct S) { 3,{ 4 } }; }
返回一个包含3的struct S
值和一个包含4的数组。这里,C标准将自身绘制到一个角上。它想支持从函数返回的结构,但是,当您访问数组时,例如foo().y[0]
,C 2018 6.3.2.1中的规则3表示数组已转换为指向其第一个元素的指针。指针必须指向存储,因此必须有一些对象指向。我想一种解决方案可能是说您不能在这样的结构值中单独使用数组。 (您可以通过将返回值复制到struct S x = foo();
中然后再使用x
中来使用返回值。)但是,C委员会采用的解决方案是为此类结构定义临时寿命。在C 2018 6.2.4 8中,它们的定义仅针对包含数组成员的结构和联合定义了临时生存期。
但是,在您的代码中,这无关紧要。因为您的foo(2,4)
返回一个值,所以您可以根据需要使用该值; foo(2,4).x
之所以有效,是因为它接受值的x
成员。无需担心任何对象的寿命,因为不涉及任何对象。
根据C标准
4 完整表达式是另一个表达式的一部分 表达式或声明符。以下每个都是完整的 表达式:不属于复合文字的初始化程序; 表达式语句中的表达式;选择语句的控制表达式(if或switch);控制 表达一阵子或做声明;每个(可选) for语句的表达式;返回中的(可选)表达式 声明。评估一个完整项目之间有一个顺序点 表达式和下一个完整表达式的评估 评估。
当包含完整表达式的求值结束时,临时对象的生存期结束。
因此在此表达式语句中
a = foo(2,4).x;
返回的结构类型对象是活动的,并且其数据成员x
的值已分配给在main中声明且具有main块范围的变量a
。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。