C++函数返回指针和引用的坑

常用C++进行项目开发的童鞋们应该都知道,在C++中指针和引用是常用的语法了,而指针又是C++区别于其他高级语言的一大精髓。 针对C++中的指针和引用,博主之前也写过几篇技术博文进行了介绍了,忘记了的童鞋们可以温故知新:

C++之指针扫盲
C++之智能指针
C++之指针与引用
C++之右值引用

而今天我们再来看看在C++新手们针对指针和引用的使用经常犯的错误。

函数返回指针

在C++中针对一个函数返回指针的实现方式一般有三种:

  • 返回一个变量的地址

例如以下代码

c 复制代码
// 返回int指针地址
int * funTest(){
    int a = 101;
    return &a;
}

int main(int argc, const char *argv[]) {
    int *a = funTest();
    std::cout << "a的值:" << *a << std::endl;
    return 0;
}

以上代码在笔者的电脑上运行就直接报错崩溃了,崩溃信息:

vbnet 复制代码
Process finished with exit code 139 (interrupted by signal 11: SIGSEGV)

上面的代码返回一个局部变量a的地址,这个变量a紧在函数funTest内有效,当函数funTest结束了,变量a的生命周期也就结束了, 此时变量a所占用的内存空间将被释放,因此返回的指针地址将会被指向一个未知数,后续再使用这个指针是未定义的行为,可能会导致程序崩溃或者出现其他异常。

针对这样的危险代码行为,其实编辑器也已经给出了警告,所以说在开发过程中也不要以为的忽略警告哦。

为了杜绝此类行为的发生,还可以使用笔者之前的介绍的代码质量检测工具cppcheck进行检测,在开发过程中直接划线提醒。

介绍一款CPP代码bug检测神器

  • 返回一个使用static修饰的变量地址

我们修改一下funTest函数的变量a,使用static关键字修饰一下:

c 复制代码
// 返回int指针地址
int * funTest(){
    static int a = 101;
    return &a;
}

int main(int argc, const char *argv[]) {
    int *a = funTest();
    std::cout << "a的值:" << *a << std::endl;
    return 0;
}

运行发现程序并没有崩溃,而且是正确打印出了变量a的值。这是因为 使用static 表示将这个变量存储到全局区(static静态区), 此时就不受栈区管控,当函数funTest执行完毕后,变量a依然存在,不会存在前面所说的变量地址被释放的问题。

  • 使用动态分配内存new关键字
c 复制代码
int * funTest(){
    //动态分配的内存空间,手动delete后才会释放
    int* a = new int(101) ;
    return a;
}

int main(int argc, const char *argv[]) {
    int *a = funTest();
    std::cout << "a的值:" << *a << std::endl;
    return 0;
}

上述代码不会崩溃,也能正常运行,但是存在一个隐患就是返回的指针变量a如果忘记调用delete则会造成内存泄露, 这就引发了一个指针变量谁维护销毁的问题。一般默认规则是谁开发维护。

因此,针对这样的场景,笔者的建议是智能指针你值得拥有...

函数返回一个引用

我们看看以下返回一个引用的例子代码:

c 复制代码
int & funTest(){
    //动态分配的内存空间,手动delete后才会释放
    int a = 101 ;
    return a;
}

int main(int argc, const char *argv[]) {
    int a = funTest();
    std::cout << "a的值:" << a << std::endl;
    return 0;
}

笔者在CLion上测试也是直接崩溃了,原因也是和上面所说的返回一个局部变量的地址一样, 都是因为函数funTest结束后,变量a的生命周期结束了, 变量a也就是被释放了,再返回它的引用的话就是未定义的。至于为什么它们的原因是一样的呢?因为所谓引用,可以简单地理解为引用其实就是带const修饰的指针。

那么针对这个问题该如何修正呢?首先使用static关键字肯定是可以的。那么使用动态内存new的方式行不行呢?答案也是可行的,但是需要注意的一点就是如果一个引用 的值来源于一个指针,后来这个指针被delete掉了,那么再使用这个引用也是会造成崩溃的...

如何返回一个数组

那么问题来了,举一反三,如果想通过一个函数返回一个数组那该如何实现呢?

众所周知,C++是不允许直接返回一个数组的,如果您想要从函数返回一个一维数组,您必须声明一个返回指针的函数。

例如下面的写法是编译不通过的:

csharp 复制代码
// 无法编译通过,不能返回一个数组
int[] funTest(){
    int myArray[3] = {1, 2, 3};
    return myArray;
}

正确的写法应该是:

arduino 复制代码
int* funTest(){
    static int myArray[3] = {1, 2, 3};
    return myArray;
}

因而可以看出,其实返回一个数组的函数所遇到的坑其实就转换成了返回一个指针的函数所遇到的坑,这些坑的举例就如前面所说...

推荐阅读

C++进阶系列

关注我,一起进步。

相关推荐
用户8055336980315 小时前
不止三件套:QObject 属性系统全关键字与运行时反射!
c++·qt
BadBadBad__AK1 天前
线段树维护区间 k 次方和
c++·数学·算法·stl
卷无止境2 天前
Eigen 库如何借助 OpenMP 加速计算
c++·后端
卷无止境2 天前
OpenMPI、MPICH 与 OpenMP:关系、核心概念与架构全解
c++·后端
郝学胜_神的一滴3 天前
CMake 30:循环语法全解|foreach_while双循环精讲、迭代技巧与实战避坑指南
c++·cmake
Bigger4 天前
我写了一个AI图像视频生成工具,免费API+本地部署,分享给大家
人工智能·图像识别·音视频开发
卷无止境5 天前
C++ 的Eigen 库全解析
c++
卷无止境5 天前
现代 C++特性大盘点:一门脱胎换骨的老语言
c++·后端
郝学胜_神的一滴5 天前
CMake 27:缓存变量的特性、语法、类型与实操全解
c++·cmake
博客18007 天前
酷宝的使用方法,超好用的免费界面库,C++、MFC可用
c++·mfc·界面库·库来帮·酷宝