今天刚看完C++ Primer的第六章,正在对 Function 的一些常用知识做总结。当时我正好在牛客上找到一道很简单的排序题,想试着写一个排序 function 来返回排好序的数组。因为 function 是不能直接返回数组的,所以书上提供了好几种定义返回类型的方法:

  • typedef
  • using
  • type (*function(para_list))[dimension] //直接定义
    我比较喜欢其中的用 using 来做 type alias 的方法:言简意赅。比如我要定义一个返回 int 数组的 function,我就可以用如下的语句来声明这个function:
using arrT = int[10]; 
arrT* func(parameters..); // define function that return a int(*)[10] 

我在这里希望实现的是从 arguments 那里获得一个数组(传过来肯定就是指针了),然后返回一个指向原数组的指针。按照这样的想法,来的是指针,走的也是指针,所以应该没问题吧?于是我写了一个function:

arrT* return_p(arrT arr) { 
    statement...; 
    return *arr; //error: cant convert int* to int (*)[10] when returns 
} 

结果就报错了。为啥呢?
这里很好想明白:我们要返回一个指向 int[10] 的指针,但是传进来的指针似乎不是那个样子。没错,数组初始化转化成指针后直接就把维度给扔掉了,得到的只是一个指向数组第一个元素的指针。所以返回这个指针也肯定会出错了。

那要是非想用传递进来的指针操作这个数组,又不用定义一个新的指向 int[10] 的指针来做返回值,应该怎么办呢?经过一番搜索我找到了一种取巧的方法:

arrT* return_p(arrT arr) { 
    statement...; 
    return 0; 
} 

这里的 return 0 实际上指的返回一个 Nullptr 的指针。

这个指针神通广大,可以变身为任意类型的空指针,包括我们上面遇到的返回值:非要指向 int[10] 指针。所以用上 return 0以后这里返回了一个类型是指向 int[10] 的,但内容是 nullptr 的指针,这个返回值等于啥事也没有干;但是加上再试试呢:
编译器不报错了!
就本文的需求来说,我需要传递指针进去修改数组,这样目的也能达到,毕竟指针修改数组是不需要返回值的。(当然你可以直接定义 void 的函数啦,这个 return 0 只是我的意外发现)。

(by 2017-3-11) 当然如果真的要返回指针的话,我们应该这么这样写:

arrT* return_r(arrT* arr) { 
    statments.... 
    return (arrT *) arr; 
} 

不过相比指针,今天更大的惊喜是引用。相对而言引用比指针更有优势:自带 range for,同时也没有内存泄漏的危险:

arrT& return_r(arrT& arr) { 
    for (auto &i : arr) { 
        i*=2; 
    } 
    return arr; 
} 

(by 2017-3-11)我们也可以直接用 using 做数组引用的 type alias:

using arrT = int(&)[10]; 
arrT return_r(arrT arr) { 
    for (auto &i : arr) { 
        i*=2; 
    } 
    return arr; 
} 

传数组的引用十分方便,只需一个引用就可以得到数组的内容和维度。卧槽,网上各路豪杰传指针都得带一个维度啊去做条件判断啊!而且传指针的方法本身是无法知道数组末尾的位置的,那就意味着根本没有办法用简洁的 range for 了啊!

总结:

  • nullptr 类型的指针可以充当所有类型指针的返回值,即 return 0
  • range for 只能用于有明显起始结束位置的类型。
  • 需要返回数组 / 修改数组数据的时候,引用显然比较方便。

--
Update by Mar 11, 2017 :添加了一种新的返回引用的写法;解决了返回数组指针的问题。