在 C 或 C++ 中返回结构是否安全?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/9590827/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Is it safe to return a struct in C or C++?
提问by jzepeda
What I understand is that this shouldn't be done, but I believe I've seen examples that do something like this (note code is not necessarily syntactically correct but the idea is there)
我的理解是不应该这样做,但我相信我已经看到过这样的例子(注意代码不一定在语法上是正确的,但想法是存在的)
typedef struct{
int a,b;
}mystruct;
And then here's a function
然后这是一个函数
mystruct func(int c, int d){
mystruct retval;
retval.a = c;
retval.b = d;
return retval;
}
I understood that we should always return a pointer to a malloc'ed struct if we want to do something like this, but I'm positive I've seen examples that do something like this. Is this correct? Personally I always either return a pointer to a malloc'ed struct or just do a pass by reference to the function and modify the values there. (Because my understanding is that once the scope of the function is over, whatever stack was used to allocate the structure can be overwritten).
我知道如果我们想要做这样的事情,我们应该总是返回一个指向 malloc 结构的指针,但我很确定我已经看到了做这样的事情的例子。这样对吗?就我个人而言,我总是要么返回一个指向 malloc 结构的指针,要么只是通过对函数的引用进行传递并修改那里的值。(因为我的理解是,一旦函数的作用域结束,用于分配结构的任何堆栈都可以被覆盖)。
Let's add a second part to the question: Does this vary by compiler? If it does, then what is the behavior for the latest versions of compilers for desktops: gcc, g++ and Visual Studio?
让我们为问题添加第二部分:这是否因编译器而异?如果是,那么最新版本的桌面编译器的行为是什么:gcc、g++ 和 Visual Studio?
Thoughts on the matter?
对此事的想法?
采纳答案by Pablo Santa Cruz
It's perfectly safe, and it's not wrong to do so. Also: it does not vary by compiler.
这是完全安全的,这样做并没有错。另外:它不会因编译器而异。
Usually, when (like your example) your struct is not too big I would argue that this approach is even better than returning a malloc'ed structure (malloc
is an expensive operation).
通常,当(如您的示例)您的结构不太大时,我会争辩说这种方法甚至比返回 malloc 结构更好(这malloc
是一项昂贵的操作)。
回答by Luchian Grigore
It's perfectly safe.
这是完全安全的。
You're returning by value. What would lead to undefined behavior is if you were returning by reference.
你是按价值返回的。如果您通过引用返回,则会导致未定义行为。
//safe
mystruct func(int c, int d){
mystruct retval;
retval.a = c;
retval.b = d;
return retval;
}
//undefined behavior
mystruct& func(int c, int d){
mystruct retval;
retval.a = c;
retval.b = d;
return retval;
}
The behavior of your snippet is perfectly valid and defined. It doesn't vary by compiler. It's ok!
您的代码段的行为完全有效且已定义。它不会因编译器而异。没关系!
Personally I always either return a pointer to a malloc'ed struct
我个人总是要么返回一个指向 malloc'ed 结构的指针
You shouldn't. You should avoid dynamically allocated memory when possible.
你不应该。您应该尽可能避免动态分配内存。
or just do a pass by reference to the function and modify the values there.
或者只是通过引用传递函数并修改那里的值。
This option is perfectly valid. It's a matter of choice. In general, you do this if you want to return something else from the function, while modifying the original struct.
这个选项是完全有效的。这是一个选择的问题。通常,如果您想从函数返回其他内容,同时修改原始结构,则可以这样做。
Because my understanding is that once the scope of the function is over, whatever stack was used to allocate the structure can be overwritten
因为我的理解是,一旦函数的作用域结束,用于分配结构的任何堆栈都可以被覆盖
This is wrong. I meant, it's sort of correct, but you return a copy of the structure you create inside the function. Theoretically. In practice, RVOcan and probably will occur. Read up on return value optimization. This means that although retval
appears to go out of scope when the function ends, it might actually be built in the calling context, to prevent the extra copy. This is an optimization the compiler is free to implement.
这是错误的。我的意思是,这有点正确,但是您返回了您在函数内创建的结构的副本。理论上。在实践中,RVO可以而且很可能会发生。阅读返回值优化。这意味着虽然retval
在函数结束时看起来超出范围,但它实际上可能是在调用上下文中构建的,以防止额外的副本。这是编译器可以自由实现的优化。
回答by Joseph Mansfield
The lifetime of the mystruct
object in your function does indeed end when you leave the function. However, you are passing the object by value in the return statement. This means that the object is copied out of the function into the calling function. The original object is gone, but the copy lives on.
mystruct
当您离开函数时,函数中对象的生命周期确实结束。但是,您是在 return 语句中按值传递对象。这意味着对象被从函数复制到调用函数中。原始对象消失了,但副本存在。
回答by Basile Starynkevitch
Not only it is safe to return a struct
in C (or a class
in C++, where struct
-s are actually class
-es with default public:
members), but a lot of software is doing that.
不仅struct
在 C 中返回 a (或class
在 C++ 中返回 a ,其中struct
-s 实际上是class
带有默认public:
成员的-es )是安全的,而且很多软件都在这样做。
Of course, when returning a class
in C++, the language specifies that some destructor or moving constructor would be called, but there are many cases where this could be optimized by the compiler.
当然,class
在 C++ 中返回 a 时,该语言指定将调用某些析构函数或移动构造函数,但在很多情况下,编译器可以对其进行优化。
In addition, the Linux x86-64 ABIspecifies that returning a struct
with twoscalar (e.g. pointers, or long
) values is done thru registers (%rax
& %rdx
) so is very fast and efficient. So for that particular case it is probably faster to return such a two-scalar fields struct
than to do anything else (e.g. storing them into a pointer passed as argument).
此外,Linux x86-64 ABI指定返回struct
带有两个标量(例如指针或long
)值的 a 是通过寄存器 ( %rax
& %rdx
) 完成的,因此非常快速和高效。因此,对于这种特殊情况,返回这样一个双标量字段可能struct
比做任何其他事情(例如,将它们存储到作为参数传递的指针中)更快。
Returning such a two-scalar field struct
is then a lot faster than malloc
-ing it and returning a pointer.
返回这样一个双标量字段struct
比malloc
-ing 并返回一个指针要快得多。
回答by ebutusov
It's perfectly legal, but with large structs there are two factors that need to be taken into consideration: speed and stack size.
这是完全合法的,但是对于大型结构,需要考虑两个因素:速度和堆栈大小。
回答by haberdar
A structure type can be the type for the value returned by a function. It is safe because the compiler is going to create a copy of struct and return the copy not the local struct in the function.
结构类型可以是函数返回值的类型。这是安全的,因为编译器将创建 struct 的副本并返回该副本而不是函数中的本地 struct。
typedef struct{
int a,b;
}mystruct;
mystruct func(int c, int d){
mystruct retval;
cout << "func:" <<&retval<< endl;
retval.a = c;
retval.b = d;
return retval;
}
int main()
{
cout << "main:" <<&(func(1,2))<< endl;
system("pause");
}
回答by maress
It is perfectly safe to return a struct as you have done.
像您所做的那样返回结构体是完全安全的。
Based on this statement however: Because my understanding is that once the scope of the function is over, whatever stack was used to allocate the structure can be overwritten,I would imagine only a scenario where any of the members of the structure was dynamically allocated (malloc'ed or new'ed), in which case, without RVO, the dynamically allocated members will be destroyed and the returned copy will have a member pointing to garbage.
然而,基于此声明:因为我的理解是,一旦函数的作用域结束,用于分配结构的任何堆栈都可以被覆盖,我只能想象结构的任何成员被动态分配的情况( malloc'ed 或 new'ed),在这种情况下,如果没有 RVO,动态分配的成员将被销毁,返回的副本将有一个成员指向垃圾。
回答by Filippo
The safety depends on how the struct itself was implemented. I just stumbled on this question while implementing something similar, and here is the potential problem.
安全性取决于结构本身的实现方式。我只是在实施类似的事情时偶然发现了这个问题,这是潜在的问题。
The compiler, when returning the value does a few operations (among possibly others):
编译器在返回值时会执行一些操作(可能还有其他操作):
- Calls the copy constructor
mystruct(const mystruct&)
(this
is a temporary variable outsidethe functionfunc
allocated by the compiler itself) - calls the destructor
~mystruct
on the variable that was allocated insidefunc
- calls
mystruct::operator=
if the returned value is assigned to something else with=
- calls the destructor
~mystruct
on the temporary variable used by the compiler
- 调用拷贝构造函数
mystruct(const mystruct&)
(this
是编译器自己分配的函数外的临时变量func
) ~mystruct
在内部分配的变量上调用析构函数func
mystruct::operator=
如果返回值被分配给其他东西,则调用=
~mystruct
对编译器使用的临时变量调用析构函数
Now, if mystruct
is as simple as that described here all is fine, but if it has pointer (like char*
) or more complicated memory management, then it all depends on how mystruct::operator=
, mystruct(const mystruct&)
, and ~mystruct
are implemented.
Therefore, I suggest cautions when returning complex data structures as value.
现在,如果mystruct
是那样简单,这里描述的一切都很好,但是如果它有指针(像char*
)或更复杂的内存管理,那么这一切都取决于如何mystruct::operator=
,mystruct(const mystruct&)
以及~mystruct
实现。因此,我建议在将复杂数据结构作为值返回时要小心。
回答by perilbrain
I will also agree with sftrabbit , Life indeed ends and stack area gets cleared up but the compiler is smart enough to ensure that all the data should be retrieved in registers or someother way.
我也同意 sftrabbit ,生命确实结束了,堆栈区域被清除了,但编译器足够聪明,可以确保所有数据都应该以寄存器或其他方式检索。
A simple example for confirmation is given below.(taken from Mingw compiler assembly)
下面给出一个简单的确认例子。(取自Mingw编译器程序集)
_func:
push ebp
mov ebp, esp
sub esp, 16
mov eax, DWORD PTR [ebp+8]
mov DWORD PTR [ebp-8], eax
mov eax, DWORD PTR [ebp+12]
mov DWORD PTR [ebp-4], eax
mov eax, DWORD PTR [ebp-8]
mov edx, DWORD PTR [ebp-4]
leave
ret
You can see that the value of b has been transmitted through edx. while the default eax contains value for a.
可以看到b的值已经通过edx传输了。而默认的 eax 包含 a 的值。
回答by Igor Polk
It is not safe to return a structure. I love to do it myself, but if someone will add a copy constructor to the returned structure later, the copy constructor will be called. This might be unexpected and can break the code. This bug is very difficult to find.
返回结构是不安全的。我喜欢自己做,但是如果有人稍后将复制构造函数添加到返回的结构中,则会调用复制构造函数。这可能是意料之外的,并且可能会破坏代码。这个bug很难发现。
I had more elaborate answer, but moderator did not like it. So, at your expence, my tip is short.
我有更详细的答案,但版主不喜欢它。所以,以你的代价,我的小费很短。