如果返回结构体类型变量(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 语言中指针的操作非常灵活,它也能指向结构体变量对结构体变量进行操作.在学习结构指针之前,需要再次加深对指针的认识.声明指针变量时所使用的数据类型修饰符实际上的作用是定义指针访问内存的范围,如果指针 ...
随机推荐
- python(28)获得网卡的IP地址,如何在其他文件夹中导入python模块
获得第几块网卡的ip地址: 如何在其他文件夹中导入模块 import sys sys.path.append('/search/chen/tool')#你的代码存放的目录 from Get_Ip im ...
- HttpClient 教程 (五)
第五章 HTTP客户端服务 5.1 HttpClient门面 HttpClient接口代表了最重要的HTTP请求执行的契约.它没有在请求执行处理上强加限制或特殊细节,而在连接管理,状态管理,认证和处理 ...
- Datax将本地文件导入Hbase数据库!!!酷酷酷
Hbase Writer的json文件链接: https://github.com/alibaba/DataX/blob/master/hbase11xwriter/doc/hbase11xwrite ...
- 阅读ANSI C,寻找乐趣和裨益——const char **与char **为何不兼容
#include<stdio.h> void foo1(const char**p) { } void foo2(const char*p) { } int main(int argc,c ...
- python 实现ARP攻击
注:使用这个脚本需要安装scapy 包 最好在linux平台下使用,因为scapy包在windows上安装老是会有各种问题 #coding:utf-8 #example :sudo python ar ...
- 【C#】Event事件的订阅和发布
学习笔记:学习了委托和事件的用法.事件是一种特殊的委托,记录下事件从订阅到发布的流程. 学习资料源于视频:http://www.maiziedu.com/course/510-6891/ 新建一个De ...
- 修改Linux内核参数,减少TCP连接中的TIME-WAIT
一台服务器CPU和内存资源额定有限的情况下,如何提高服务器的性能是作为系统运维的重要工作.要提高Linux系统下的负载能力,当网站发展起来之后,web连接数过多的问题就会日益明显.在节省成本的情况下, ...
- 补充下.net知识
问题1: public int getvalue(int a) { try { a = a + ; ; } catch (Exception) { throw; } finally { a = a + ...
- CDH离线安装
1. 安装准备 系统:Centos 6 Cloudera Manager分配如下: 安装版本:CDH-5.8.0 所需安装文件 CDH相关 CDH-5.8.0-1.cdh5.8.0.p0.42-el6 ...
- EcmaScript对象克隆之谜
先谈谈深拷贝 如何在js中获得一个克隆对象,可以说是喜闻乐见的话题了.相信大家都了解引用类型与基本类型,也都知道有种叫做深拷贝的东西,传说深拷贝可以获得一个克隆对象!那么像我这样的萌新自然就去学习了一 ...