在C或C++中返回结构是否安全?

jze*_*eda 80 c c++ struct function return-type

我的理解是不应该这样做,但我相信我已经看过这样的例子(注意代码在语法上不一定正确,但想法就在那里)

typedef struct{
    int a,b;
}mystruct;
Run Code Online (Sandbox Code Playgroud)

然后这是一个功能

mystruct func(int c, int d){
    mystruct retval;
    retval.a = c;
    retval.b = d;
    return retval;
}
Run Code Online (Sandbox Code Playgroud)

我明白我们应该总是返回一个指向malloc'ed结构的指针,如果我们想做这样的事情,但我很肯定我已经看到了做类似这样的事情的例子.它是否正确?就个人而言,我总是要么返回一个指向malloc'ed结构的指针,要么只是通过引用函数进行传递并修改那里的值.(因为我的理解是,一旦函数的范围结束,可以覆盖用于分配结构的任何堆栈).

让我们在问题中添加第二部分:这是否因编译器而异?如果是这样,那么桌面编译器的最新版本的行为是什么:gcc,g ++和Visual Studio?

关于此事的想法?

Pab*_*ruz 73

这是非常安全的,这样做并没有错.另外:它不会因编译器而异.

通常,当(像你的例子)你的结构不是太大时,我会认为这种方法甚至比返回malloc结构更好(这malloc是一项昂贵的操作).

  • 如果其中一个字段是char*,它仍然是安全的吗?现在结构中会有指针 (3认同)
  • @ user963258实际上,这取决于你如何实现复制构造函数和析构函数. (3认同)
  • @PabloSantaCruz这是一个棘手的问题.如果是考试问题,如果需要考虑所有权,审查员可能会期望"不"作为回应. (2认同)
  • @CaptainGiraffe:是的.由于OP没有澄清这一点,并且他/她的例子基本上都是**C**,我认为它更像是一个**C**问题,而不是**C++**问题. (2认同)
  • @Kos有些编译器没有NRVO?从哪一年?另请注意:在C++ 11中,即使它没有NRVO,它也会调用移动语义. (2认同)

Luc*_*ore 68

这是非常安全的.

你是按价值回归的.如果您通过引用返回,将导致未定义行为的原因是什么.

//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;
}
Run Code Online (Sandbox Code Playgroud)

您的代码段的行为完全有效并已定义.它不会因编译器而异.没关系!

我个人总是要么返回一个指向malloc的结构的指针

你不应该.应尽可能避免动态分配内存.

或者只是通过引用该函数进行传递并修改其中的值.

此选项完全有效.这是一个选择问题.通常,如果要在修改原始结构时从函数返回其他内容,则执行此操作.

因为我的理解是,一旦函数的范围结束,可以覆盖用于分配结构的任何堆栈

这是错的.我的意思是,它是正确的,但你返回一个你在函数内创建的结构的副本.从理论上讲.在实践中,RVO可能并且可能会发生.阅读返回值优化.这意味着虽然retval在函数结束时似乎超出了范围,但它实际上可能是在调用上下文中构建的,以防止额外的副本.这是编译器可以自由实现的优化.

  • +1提及RVO.这种重要的优化实际上使这种模式对于具有昂贵的复制构造函数(如STL容器)的对象是可行的 (3认同)

Jos*_*eld 9

mystruct离开函数时,函数中对象的生命周期确实结束.但是,您在return语句中按值传递对象.这意味着将对象从函数中复制到调用函数中.原始对象消失了,但副本继续存在.


Bas*_*tch 7

不仅struct在C中返回一个是安全的(或者class在C++中,其中struct-s实际上是class-es与默认public:成员),但很多软件正在这样做.

当然,当class在C++中返回时,该语言指定将调用一些析构函数或移动构造函数,但是在很多情况下,编译器可以对其进行优化.

此外,Linux x86-64 ABI指定通过寄存器(&)返回struct带有两个标量(例如指针或long)值的a ,因此非常快速和有效.因此对于该特定情况,返回这样的双标量字段可能比执行任何其他操作更快(例如,将它们存储到作为参数传递的指针中).%rax%rdxstruct

然后返回这样一个双标量字段struct要快得多 - malloc并且返回一个指针.


ebu*_*sov 5

这是完全合法的,但是对于大型结构,需要考虑两个因素:速度和堆栈大小。

  • 听说过返回值优化吗? (2认同)
  • 我想说的只是在完成一些分析后才担心多余的副本。 (2认同)