常用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;
}
以上代码在笔者的电脑上运行就直接报错崩溃了,崩溃信息:
Process finished with exit code 139 (interrupted by signal 11: SIGSEGV)
上面的代码返回一个局部变量a的地址,这个变量a紧在函数funTest内有效,当函数funTest结束了,变量a的生命周期也就结束了, 此时变量a所占用的内存空间将被释放,因此返回的指针地址将会被指向一个未知数,后续再使用这个指针是未定义的行为,可能会导致程序崩溃或者出现其他异常。
针对这样的危险代码行为,其实编辑器也已经给出了警告,所以说在开发过程中也不要以为的忽略警告哦。
为了杜绝此类行为的发生,还可以使用笔者之前的介绍的代码质量检测工具cppcheck进行检测,在开发过程中直接划线提醒。
介绍一款CPP代码bug检测神器。
我们修改一下funTest函数的变量a,使用static关键字修饰一下:
// 返回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依然存在,不会存在前面所说的变量地址被释放的问题。
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则会造成内存泄露, 这就引发了一个指针变量谁维护销毁的问题。一般默认规则是谁开发维护。
因此,针对这样的场景,笔者的建议是智能指针你值得拥有...
我们看看以下返回一个引用的例子代码:
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++是不允许直接返回一个数组的,如果您想要从函数返回一个一维数组,您必须声明一个返回指针的函数。
例如下面的写法是编译不通过的:
// 无法编译通过,不能返回一个数组
int[] funTest(){
int myArray[3] = {1, 2, 3};
return myArray;
}
正确的写法应该是:
int* funTest(){
static int myArray[3] = {1, 2, 3};
return myArray;
}
因而可以看出,其实返回一个数组的函数所遇到的坑其实就转换成了返回一个指针的函数所遇到的坑,这些坑的举例就如前面所说...