如果返回结构体类型变量(named return value optimisation,NRVO)
貌似这是一个非常愚蠢的问题,因为对于具有良好素质的程序员而言,在C中函数返回类型为结构体类型是不是有点不合格,干嘛不用指针做传入传出呢?
测试环境:Linux IOS 3.2.0-45-generic-pae #70-Ubuntu SMP Wed May 29 20:31:05 UTC 2013 i686 i686 i386 GNU/Linux
gcc (Ubuntu/Linaro 4.6.3-1ubuntu5) 4.6.3
Copyright (C) 2011 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
此处谈下如果在C函数返回类型为大的结构体类型:
C++中严格区分初始化和赋值,但是C中没有区分初始化和赋值。
//该程序引述自:http://bbs.chinaunix.net/forum.php?mod=viewthread&action=printable&tid=1651248
//此链接中也有关于此文的讨论
#include <stdio.h>
#include <stdlib.h>
#include <string.h> const char *str = "Hello World\n";
typedef struct
{
int m_Member1;
int m_Member2;
char m_String[];
}FUNCTION_STRUCT; FUNCTION_STRUCT ReturnStruct(void)
{
FUNCTION_STRUCT internalData;
internalData.m_Member1 = ;
internalData.m_Member2 = ;
strcpy(&(internalData.m_String[]), str); return internalData;
} int main(void)
{
FUNCTION_STRUCT externalData;
externalData = ReturnStruct(); int a = externalData.m_Member1;
int b = externalData.m_Member2;
int c = a + b;
#if 0
printf("%d, %d, %s",
externalData.m_Member1,
externalData.m_Member2,
&externalData.m_String[]);
#endif return ;
}
看下29行,实际上无论对于C或者C++(以文中开始处的测试环境为依据),ReturnStruct()都是有一个隐含的参数,其数据类型就是FUNCTION_STRUCT*,其存储空间在caller的栈中。
对C:
ReturnStruct中internalData在ReturnStruct函数的栈空间,当其执行到“return internalData"之前,会把internalData中的数据一个一个的拷贝到隐含参数所指向的空间中。那么开始传入的隐含参数与externalData的地址空间是否相同呢?答:当为”#if 0“时隐含参数与externalData的地址空间相同,故此时只有”一次“生成internalData+”一次“拷贝到externalData(编译器完成),当为“#if 1"时,隐含参数与externalData的地址空间不同,因此当从ReturnStruct中返回时,在caller中由编译器插入一些操作,将隐含参数指向的空间拷贝到externalData的地址空间,故此时只有”一次“生成internalData+"两次”拷贝(编译器完成);如果我们用指针作为传入传出参数,对C而言,效率可以大大提高,因为只需”一次“赋值到externalData。对于C而言,如果函数返回大的结构提类型,将callee中的栈帧的相应值拷贝到caller中的临时参数的地址空间是不可避免的,可能的区别就在于:在caller中是否要将临时参数所所指的地址空间的数据拷贝到目标空间(临时参数所指的地址空间与目标空间相同,则不用拷贝)。
所以对于C函数而言,如果写出的函数返回大的结构体数据类型,真的可以说不是一名合格的程序员(感觉返回小的结构体数据类型也不好啊,当然对于mips而言,返回8字节空间大小的结构体数据类型而言,直接用寄存器就可以了),即使出于可读性而言也不应该如此设计。
问题在于C++中,要是有程序员如此设计,我还真的不知该如何评价,因为当我们在函数中返回一个类类型对象时,有时既可以与显式的使用指针设计的函数效率相同,而且可读性也大大加强。感觉这与C++中严格区分初始化和赋值有关(我不确定)。
When certain criteria are met, an implementation is allowed to omit the copy construction of a class object,
even if the copy constructor and/or destructor for the object have side effects. In such cases, the implementation
treats the source and target of the omitted copy operation as simply two different ways of referring to
the same object, and the destruction of that object occurs at the later of the times when the two objects
would have been destroyed without the optimization.) This elision of copy operations is permitted in the
following circumstances (which may be combined to eliminate multiple copies):
— in a return statement in a function with a class return type, when the expression is the name of a
non-volatile automatic object with the same cv-unqualified type as the function return type, the copy
operation can be omitted by constructing the automatic object directly into the function’s return value
— when a temporary class object that has not been bound to a reference (12.2) would be copied to a class
object with the same cv-unqualified type, the copy operation can be omitted by constructing the temporary
object directly into the target of the omitted copy [Example:
class Thing {
public:
Thing();
˜Thing();
Thing(const Thing&);
};
Thing f() {
Thing t;
return t;
}
Thing t2 = f();
Here the criteria for elision can be combined to eliminate two calls to the copy constructor of class Thing:
the copying of the local automatic object t into the temporary object for the return value of function f()
and the copying of that temporary object into object t2. Effectively, the construction of the local object t
can be viewed as directly initializing the global object t2, and that object’s destruction will occur at program
exit. —end example]
上面时引述链接中所涉及的一段说明,我不是太明白,附下如下一段代码和运行结果,可以说明上文中的内容
#include <iostream> class BASE
{
private:
int val;
public:
BASE(void):val()
{
std::cout << "BASE constructor" << std::endl;
std::cout << "own address: " << this << std::endl;
} BASE(const BASE& base)
{
std::cout << "BASE copy constructor" << std::endl;
std::cout << "parameter address: " << &base << std::endl;
std::cout << "own address: " << this << std::endl;
val = base.val;
} BASE& operator= (const BASE& base)
{
std::cout << "BASE assignment" << std::endl;
std::cout << "parameter address: " << &base << std::endl;
std::cout << "own address: " << this << std::endl;
val = base.val;
return *this;
} ~BASE(void)
{
std::cout << "BASE deconstructor" << std::endl;
std::cout << "own address: " << this << std::endl;
}
}; BASE getBASE(void)
{
BASE base;
std::cout << "in getBASE base address: " << &base << std::endl;
return base;
} int main(void)
{
BASE base_one = getBASE();
std::cout << "***********" << std::endl;
BASE base_two;
std::cout << "***********" << std::endl;
base_two = getBASE();
return ;
}
BASE constructor
own address: 0xbfecb0d4
in getBASE base address: 0xbfecb0d4
***********
BASE constructor
own address: 0xbfecb0d8
***********
BASE constructor
own address: 0xbfecb0dc
in getBASE base address: 0xbfecb0dc
BASE assignment
parameter address: 0xbfecb0dc
own address: 0xbfecb0d8
BASE deconstructor
own address: 0xbfecb0dc
BASE deconstructor
own address: 0xbfecb0d8
BASE deconstructor
own address: 0xbfecb0d4
首先,该程序范例不好,我没写/找出好点的范例。
关键处在于:
“BASE base_one = getBASE();"
" BASE constructor
2 own address: 0xbfecb0d4
3 in getBASE base address: 0xbfecb0d4
4 ***********"
仅调用了一次构造,而且程序的可阅读性加强了。
注意g++的命令行参数 -fno-elide-constructors
如果返回结构体类型变量(named return value optimisation,NRVO)的更多相关文章
- 如果返回结构体类型变量(named return value optimisation,NRVO) ------ 续
为什么? <More C++ idioms>: 3. Algebraic Hierarchy 程序执行的流程与自己想的不一样: Number Number::makeReal(double ...
- C语言函数不能返回数组,但可以返回结构体
为什么C语言函数可以返回结构体,却不可以返回数组?有这样的问题并不奇怪,因为C语言数组和结构体本质上都是管理一块内存,那为何编译器要区别对待二者呢? C语言函数为什么不能返回数组? 在C语言程序开发中 ...
- 【C语言入门教程】7.1 结构体类型变量的定义和引用
前面学习了变量和数组这些简单的数据结构,它们的特点是必须使用规定的数据类型.例如数组被定义为整型后,它的所有存储单元都是由整型构成.现实生活中某一类事物的共同属性可能是由不同的数据类型组成的集合,或者 ...
- c++调用python系列(1): 结构体作为入参及返回结构体
最近在打算用python作测试用例以便对游戏服务器进行功能测试以及压力测试; 因为服务器是用c++写的,采用的TCP协议,当前的架构是打算用python构造结构体,传送给c++层进行socket发送给 ...
- abap函数返回结构体类型
1: 定义一个结构体 T-CODE se11 2: 选择 structure 3:输入相应的字段 4:激活 5:创建一个function module zfm_return_table,返回类型为 ...
- Android JIN返回结构体
一.对应类型符号 Java 类型 符号 boolean Z byte B char C short S int I long J float ...
- C++学习(二十四)(C语言部分)之 结构体1
1.结构体 存放多个不同类型的数据 但是是相关联的 数组 存放多个相同类型的数据 结构体是存放多个相关联的不同类型的数组 struct 定义一个结构体类型 自定义类型 2.结构体定义方式 定义类型最通 ...
- (C/C++) 用函数返回一个结构体
方法一: 参数里含有指向指针的指针. 注意:如果函数参数里只有一个指向结构体的指针,是无法正确地返回结构体的值的.原因在于在编译的时候,会对入参p产生一个备份_p. 参考此文:http://www.c ...
- 【C语言入门教程】7.3 结构体指针的定义和引用
C 语言中指针的操作非常灵活,它也能指向结构体变量对结构体变量进行操作.在学习结构指针之前,需要再次加深对指针的认识.声明指针变量时所使用的数据类型修饰符实际上的作用是定义指针访问内存的范围,如果指针 ...
随机推荐
- git使用(二)----创建版本库
创建版本库(操作都是在linux环境下) 什么是版本库呢?版本库又名仓库,英文名repository,其实就是一个目录,可以进行增删查改 创建一个目录,这里在根目录下创建一个git_home目录mkd ...
- js检查页面上有无重复id的代码分享
用js代码检查一个页面上是否用重复的id. 方法一: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" ...
- VS15 Visual Assist X破解
Visual Assist X是一款非常好的Microsoft Visual Studio插件,支持C/C++,C#,ASP,Visual Basic,Java和HTML等语言,Visual Assi ...
- redis性能提升
1.redis请求执行原理redis客户端与redis服务器之间使用TCP协议进行连接,一个科幻可以通过一个socket连接发送多个请求命令,但每个请求命令发出后client通常会阻塞并等待redis ...
- python(25):Unicode 转成中文
代码转换如下: if __name__ == "__main__": data = "\u5c71\u5cb3\u548c\u4e00\u5207\u4e18\u9675 ...
- 20个angularjs开发工具
AngularJS是那些渴望以动态方式来设计web app的web开发人员最偏爱的框架之一.如果你是一个希望启动AngularJS项目的开发人员,那么你可能需要帮助来挑选出趁手的工具…… 在Value ...
- 孙鑫VC++视频教程笔记
写在前面的话:在学习孙鑫老师的VC++视频时,为了加深自己对知识的深入理解,就做了下面的笔记. 第一讲: 第二讲: 第三讲: 第四讲: 第五讲: 第六讲: 第七讲: 第八讲: 第九讲: 第十讲: 第十 ...
- shell学习笔记之条件(二)
test或者[ #检查文件是否存在 if test -f read.c then ... fi if [ -f read.c ] then ... fi #如果then和if在同一行上,就应该用;把i ...
- Centos7 squid安装与配置
装squid yum install -y squid 安装httpd(用于后面生成密码文件) yum install -y httpd 或者 yum install httpd-tools -y 配 ...
- [转]解决 Eclipse项目红感叹号
原文地址:http://www.cnblogs.com/hakuci/archive/2012/01/06/2314143.html 原因:显示红色感叹号是因为jar包的路径不对 解决:在项目上右击B ...