如果返回结构体类型变量(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 语言中指针的操作非常灵活,它也能指向结构体变量对结构体变量进行操作.在学习结构指针之前,需要再次加深对指针的认识.声明指针变量时所使用的数据类型修饰符实际上的作用是定义指针访问内存的范围,如果指针 ...
随机推荐
- iOS 碰撞检測以及事件响应
*/ //碰撞检測 //碰撞检測de过程 //碰撞检測 //碰撞检測 //碰撞检測 //UIApplication-> UIWindow-> UIController-> 视图控制器 ...
- Sampling and Estimation
Sampling and Estimation Sampling Error Sampling error is the difference between a sample statistic(t ...
- 简易C#动态加载dll(实现插件化)
可以通过该方法来实现程序插件化. 假设A,B两个类,A类为宿主,B类为插件需要加载到A类中: class Program { public interface IHellow { void Hello ...
- zabbix应用之Low-level discovery监控磁盘IO
参考文章: http://qicheng0211.blog.51cto.com/3958621/1599776/ zabbix自带的"Template OS Linux"模板支持监 ...
- Serviceability
http://hg.openjdk.java.net/jdk7u/jdk7u2/hotspot/file/6259c6d3bbb7/agent/doc/clhsdb.html http://blog. ...
- spring整合kafka项目生产和消费测试结果记录(一)
使用spring+springMVC+mybatis+kafka做了两个web项目,一个是生产者,一个是消费者. 通过JMeter测试工具模拟100个用户并发访问生产者项目,发送json数据给生产者的 ...
- ibtais中把clob数据类型转换成string并插入到数据库中
1,在xml中定义一个parameterMap <parameterMap id="stringToClob" class="com.a.b.c"> ...
- [小技巧]diff的文件夹忽略使用方式
当我们比较两个文件夹时经常需要忽略.svn或者.git,那么如下 diff -r -x ".git" -x "*.ko" -x "*.o" ...
- 判断一个string是否以数字开头
public static void main(String[] args) { Pattern pattern =null; String content = "30. ...
- QT全局宏变量的实现
qt中如何实现定义一个宏,在整个工程中都可以实现呢?下面我来写一个亲测的例子: pro中添加如下宏定义代码: DEFINES += HELLO=\"$$PWD/\" DEFINES ...