iis服务器助手广告广告
返回顶部
首页 > 资讯 > 后端开发 > 其他教程 >C++中的函数返回值问题
  • 231
分享到

C++中的函数返回值问题

2024-04-02 19:04:59 231人浏览 八月长安
摘要

目录1、返回值2、指针类型的函数——返回指针3、返回引用4、综合示例首先,强调一点,和函数传参一样,函数返回时也会做一个拷贝。 从某种角度上看,和传参一样,也

首先,强调一点,和函数传参一样,函数返回时也会做一个拷贝。

从某种角度上看,和传参一样,也分为三种:

  • 返回值:返回任意类型的数据类型,会将返回数据做一个拷贝(副本)赋值给变量;由于需要拷贝,所以对于复杂对象这种方式效率比较低(调用对象的拷贝构造函数、析构函数);例如:int test(){}或者 Point test(){}
  • 返回指针:返回一个指针,也叫指针类型的函数,在返回时只拷贝地址,对于对象不会调用拷贝构造函数和析构函数;例如:int *test(){} 或者 Point *test(){}
  • 返回引用:返回一个引用,也叫引用类型的函数,在返回时只拷贝地址,对于对象不会调用拷贝构造函数和析构函数;例如:int &test(){}或者 Point &test(){}

一般来说,在函数内对于存在栈上的局部变量的作用域只在函数内部,在函数返回后,局部变量的内存会自动释放。因此,如果函数返回的是局部变量的值,不涉及地址,程序不会出错;但是如果返回的是局部变量的地址(指针)的话,就会造成野指针,程序运行会出错。因为函数只是把指针复制后返回了,但是指针指向的内容已经被释放,这样指针指向的内容就是不可预料,调用就会出错。

1、返回值

int test1() {
  int a = 1;
  return a;
}

返回值是最简单有效的方式,他的操作主要在栈上,根据函数栈的特性局部变量a会在函数结束时被删除,为了返回a的值,需要产生a的复制。

如果a原子类型这当然也无所谓,但是如果a是大的对像,那么对a的复制将会产生比较严重的资源和性能消耗。

注:函数返回值本身因为没有名称或引用,所以是右值,是不能直接操作的。

2、指针类型的函数——返回指针

若函数的返回值是指针,该函数就是指针类型的函数。(即函数return一个指针,该指针可以是任何类型的)

1)指针类型的函数定义:

<类型> *函数名(参数)

int *test1() {
  int *b = new int();
  *b = 3;
  return b;
}

根据函数栈的特性也会产生复制,但是这个复制只是4(或8)字节,对于返回大型对像或数组来说可减少资源。但是返回指针资源的清理工作交给了调用者,这某种意义上违反了谁申请谁销毁的原则。

注:函数返回指针也是右值,同样无法操作。

2)说明:

  • 不要将非静态局部地址用作函数返回值:因为局部地址在离开函数后就失效了。
  • 可以在函数中用动态内存分配(new)的地址返回,但需要注意内存分配和释放不在同一级别,不要忘记释放,否则内存泄露;
  • 可以在主调函数中定义数组,函数中对该数组进行操作,然后返回其中一个元素的地址;

3、返回引用

1)引用类型函数的定义:

<类型> &函数名(参数)

int &test3() {
  int *c = new int();
  *c = 5;
  return *c;
}

引用是c++中新添加的概念,所以返回引用也是C++中相对于C来说所没有的。引用是值的别名,和指针一样不存在对大对像本身的复制,只是引用别名的复制。引用是左值,返回引用可以直接操作,也就可以进行连续赋值,最经典的示例是拷贝构造函数和运算符重载一般返回引用。

test3() +=3;

2)说明:

  • 和返回指针一样,不要将非静态局部变量的引用用作函数返回值:因为局部地址在离开函数后就失效了。
  • 和返回指针一样,用动态内存分配(new)的局部指针可以作为引用返回,但是和返回指针一样需要调用者自己去清理内存,否则内存泄露;

总结

在C时代函数只能返回值、指针两种,这两种返回的都是右值;前者对于返回对象时要进行拷贝,效率比较低(会执行对象的拷贝构造函数、析构函数),后者不会发生;

C++时代除了上面两种外,多了返回引用,这种返回时一种左值,特性和返回指针一样;

4、综合示例

1)返回栈内局部变量:

#include <iOStream>
using namespace std;
int fun1() {
    int i = 1;
    cout<<"fun1 i address"<<&i<<endl;
    return i;//ok,返回值是i值得拷贝
}
int *fun2() {//指针类型的函数
    int i = 2;
    int *ip = &i;
    cout<<"fun2 i address"<<ip<<endl;
    return ip; // Wrong!返回值是ip指针的拷贝,但该地址在函数结束后会释放变得无效
}
int main() {
    int r1 = fun1();
    cout<<"main fun1 return i address"<<&r1<<endl;
    cout << r1 << endl; // 1
 
    int *r2 = fun2();
    cout<<"main fun2 return i address"<<r2<<endl;
    //这里有可能出错:具体看对应的内存是否被覆盖,但总之该内存已无效
    cout << *r2 << endl;//0
 
    return 0;
}

输出:

fun1 i address0x7ffc49e9b69c
main fun1 return i address0x7ffc49e9b6b4
1
fun2 i address0x7ffc49e9b694
main fun2 return i address0x7ffc49e9b694
0

我们在看一个对象的例子:

#include <iostream>
 
using namespace std;
 
class Point {
  public:
    Point(int a,int b):x(a),y(b){}
    int getX();
    void setX(int x);
  private:
    int x,y;
};
 
int Point::getX(){
  return x;
}
void Point::setX(int a) {
  x = a;
}
Point func(int x) {
  Point p(x,100);
  cout<<"func1 p address:"<<&p<<endl;
  return p;//ok,发生一次Point拷贝
}
Point *func2(int x) {//指针函数
  Point p(x,200);
  cout<<"func2 p address:"<<&p<<endl;
  return &p;//wrong,返回值是p地址的拷贝,但该地址在函数结束后会被释放变得无效
}
main() {
  Point p = func(1);
  cout<<"main return p address:"<<&p<<endl;
  cout<<"main return p x:"<<p.getX()<<endl;
 
  Point *p2 = func2(2);
  cout<<"main return p address:"<<p2<<endl;
  cout<<"main return p x:"<<p2->getX()<<endl;
}

编译的时候会有一个警告:

test88.cpp: In function ‘Point* func2(int)’:
test88.cpp:26:9: warning: address of local variable ‘p’ returned [-Wreturn-local-addr]
   Point p(x,200);
         ^

输出:

func1 p address:0x7fff0f005270
main return p address:0x7fff0f005290
main return p x:1
func2 p address:0x7fff0f005270
main return p address:0x7fff0f005270
main return p x:6299776

结论:对于栈内局部变量,采用一般的返回值,实际上是对返回值的一次值拷贝,在内存里会有两个示例;对于指针类型函数的返回值,实际上是对地址的一次拷贝,内存只有一个示例,但该地址是一个非法的地址,在使用时会出现问题。

2)返回字符串:

通过 char* s = “Hello”; 的方式得到的是一个字符串常量 Hello,存放在只读数据段(.rodata section),把该字符串常量的只读数据段的首地址赋值给了指针 s,所以函数返回时,该字符串常量所在的内存不会被回收,所以能正确地通过指针访问。

#include <iostream>
using namespace std;
 
char *fun1() {
  char *s="hello";
  return s;//ok
}
 
int main() {
  char *c1 = fun1();
  cout<<c1<<endl;
  //常量,无法在修改 
  
  return 0;
}

3)静态变量:

可以把局部变量声明为static静态变量。这样变量存储在静态存储区,程序运行过程中一直存在。

int *fun3(){
  static int i = 5;
  cout<<"fun3 i address:"<<&i<<endl;
  return &i;
}
int main() {
 
  int *r1 = fun3();
  cout<<"main return i address:"<<r1<<endl;
  cout<<*r1<<endl;
}

输出:

fun3 i address:0x602078
main return i address:0x602078
5

4)数组:

数组是不能作为函数的返回值的。因为编译器会把数组名认为是局部变量(数组)的地址。返回一个数组,实际上是返回指向这个数组首地址的指针。函数结束后,数组作为局部变量被释放,这个指针则变成了野指针。但是声明数组是静态的,然后返回是可以的。

int *fun4() {
  static int a[2]={4,5};
  cout<<"fun4 a[] address:"<<&a<<endl;
  return a;
}
int main() {
  int *r2 = fun4();
  cout<<"main return a[] address:"<<r2<<endl;
  cout<<*r2<<endl;
}

输出:

fun4 a[] address:0x60207c
main return a[] address:0x60207c
4

5)堆内变量:

函数返回指向存储在堆上的变量的指针是可以的。但是,程序员要自己负责在函数外释放(free/delete)分配。

int *fun5() {
  int *j = new int;
  *j = 99;
  cout<<"fun5 j address:"<<j<<endl;
  return j;
}
int main() {
 
  int *r3 = fun5();
  cout<<"main return j address:"<<r3<<endl;
  cout<<*r3<<endl;
  *r3 = 100;
  cout<<*r3<<endl;
  delete r3;
}

输出:

fun5 j address:0x208b010
main return j address:0x208b010
99
100

综上,C++的函数返回和函数传参有所不同,返回值和传参一样也有三种类型:

  • 使用一般(传统)的函数返回,对于复杂对象会涉及到拷贝效率问题;
  • 使用指针类型的函数会有很多限制和弊端(容易内存泄露);
  • 引用类型的函数又是一个鸡肋;

所以一般C++函数都是用传址的方式进行双向数据绑定,而返回值仅仅是一个成功或失败的标志。

以上为个人经验,希望能给大家一个参考,也希望大家多多支持编程网。

--结束END--

本文标题: C++中的函数返回值问题

本文链接: https://www.lsjlt.com/news/168573.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • C++中的函数返回值问题
    目录1、返回值2、指针类型的函数——返回指针3、返回引用4、综合示例首先,强调一点,和函数传参一样,函数返回时也会做一个拷贝。 从某种角度上看,和传参一样,也...
    99+
    2022-11-13
  • C#函数out多个返回值问题
    目录C#函数返回多个参数数值C#调用一个函数通过out返回多个变量值/数据举例总结C#函数返回多个参数数值 通过out/Ref实现,声明函数时用out指定返回变量。 写了一个DEMO...
    99+
    2023-02-24
    C#函数 C# out C#多个返回值
  • C语言中函数返回值不一致问题
    目录C语言函数返回值不一致函数的返回值注意事项函数的返回值注意事项总结C语言函数返回值不一致 在运行成程序上有时会发现函数内部的值与返回到主函数的值会相差很多出现随机值,但是它们的地...
    99+
    2023-02-24
    C语言函数 函数返回值不一致 C语言函数返回值
  • C++中cin的返回值问题
    目录cin的返回值cin流输入的返回值问题cin的返回值 今天在用STL时用到while(cin>>s1>>a>>s2>>b)这样的语...
    99+
    2022-11-13
  • Shell中函数返回值超出问题
    1、前言   快半年没有写博客了,荒废了很久,工作中的杂事太多,自己越来越懒了。为了鞭策自己成长,还是要坚持写写博客,记录自己的成长。 2、shell函数介绍 语法: [ function ] funna...
    99+
    2022-06-04
    函数 返回值 Shell
  • 关于ShellExecute函数返回值问题
    ShellExecute函数的返回值是一个HINSTANCE类型的值,用于表示执行结果。具体返回值的含义如下:- 如果函数执行成功,...
    99+
    2023-08-08
    ShellExecute
  • C#函数out多个返回值问题怎么解决
    今天小编给大家分享一下C#函数out多个返回值问题怎么解决的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。C#函数返回多个参数...
    99+
    2023-07-05
  • C语言中函数返回值不一致问题如何解决
    本文小编为大家详细介绍“C语言中函数返回值不一致问题如何解决”,内容详细,步骤清晰,细节处理妥当,希望这篇“C语言中函数返回值不一致问题如何解决”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。C语言函数返回值不一致...
    99+
    2023-07-05
  • C++中stack的pop()函数返回值解析
    目录stack的pop()函数返回值全部demo分析C++的返回值优化从函数返回值RVOstack的pop()函数返回值 int temp = s.pop(); co...
    99+
    2022-11-13
  • MYSQL 使用GROUP_CONCAT函数返回值缺失问题
    MySQL中的GROUP_CONCAT函数用于将查询结果集中的多行数据合并为一行,常用于统计分组聚合操作。然而,在使用GROUP_CONCAT时,可能会遇到截断问题,即合并后的字符串长度超过了MySQL的配置限制,导致结果不完整。本文将介绍...
    99+
    2023-09-15
    mysql 数据库 bash
  • 如何解决Shell中函数返回值超出问题
    这篇文章主要介绍“如何解决Shell中函数返回值超出问题”,在日常操作中,相信很多人在如何解决Shell中函数返回值超出问题问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”如何解决Shell中函数返回值超出问题...
    99+
    2023-06-09
  • js递归函数返回值问题如何解决
    在JavaScript中,递归函数的返回值问题可以通过以下方法解决:1. 使用全局变量:定义一个全局变量,递归函数在每次调用时更新全...
    99+
    2023-09-13
    js
  • java递归函数返回值问题怎么解决
    在递归函数中,可以使用一个参数或者一个全局变量来保存计算结果,然后在递归调用的过程中更新这个参数或者变量的值,最终返回最终的结果。例...
    99+
    2023-09-26
    java
  • python函数的返回值
    返回值:return1.没有返回值    #不写return    #只写return:结束一个函数    #return None2.有一个返回值    #可以返回任何数据类型    #只要返回就可以接收到    #如果在一个程序中有多个...
    99+
    2023-01-30
    函数 返回值 python
  • C#调用C++DLLbool返回值始终为true的问题
    目录C#调用C++ DLL bool返回值始终为true问题描述问题原因解决方案C#调用C++的DLL返回值为bool时,值混乱解决方案C#调用C++ DLL bool返回值始终为t...
    99+
    2022-11-13
    C#调用C++ DLL C++ DLL C++ DLL bool返回值 bool返回值为true
  • C/C++ 引用作为函数的返回值方式
    目录case1:用返回值方式调用函数case2:用函数的返回值初始化引用的方式调用函数case3:用返回引用的方式调用函数case4:用函数返回的引用作为新引用的初始化值的方式来调用...
    99+
    2022-11-13
  • PHP函数的返回值类型和返回值说明
    PHP是一种类型松散的编程语言,这意味着它允许开发人员在运行时更轻松地修改变量类型。但是,在编写函数时,开发人员必须清楚函数的返回类型和返回值说明,以确保函数将返回正确的数据类型和结构。PHP函数的返回值类型在5.0版本之前,PHP函数无法...
    99+
    2023-05-18
    PHP函数 返回值类型 返回值说明
  • C语言返回值指针的函数详解
          #include<stdio.h> void main() { int a[5] = { 1,3,5,7,9 }; ...
    99+
    2022-11-13
  • C# 函数返回多个值的方法详情
    目录引言1.使用ref参数2.使用out参数修饰符3. 使用元组类4.使用C#7 ValueTuple5. 使用结构或类引言 根据 C# 语言规范,不可能从一个方法返回多个值。使用 ...
    99+
    2022-11-13
  • C语言函数怎么返回多个值
    这篇文章主要介绍“C语言函数怎么返回多个值”,在日常操作中,相信很多人在C语言函数怎么返回多个值问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”C语言函数怎么返回多个值”的疑惑有所帮助!接下来,请跟着小编一起来...
    99+
    2023-07-05
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作