当前位置:C++技术网 > 资讯 > 如何返回函数内的二维数组

如何返回函数内的二维数组

更新时间:2017-05-20 14:41:20浏览次数:1+次

你好!我有一个函数,里面定义了一个静态的二维数组,我就想在调用这个函数时,用指针指向这个二维数组,我所知道的是,数组其实就是指针,但数组在传参时,会丢失长度,所以,我用

void GetRoutToolArray(int& len,const char** rouArray )

{

   static const char rout_str[][roulen] = {"rou060","rou070","rou080"};
   rouArray = rout_str;
}

会提示为什么使char**转换成rout_str[3][10],只有用以下的方式才行

void GetRoutToolArray(int& len,const (*rouArray)[50] )

{

   static const char rout_str[][roulen] = {"rou060","rou070","rou080"};
   rouArray = rout_str;
}

这样的话,是不是就是不能用char**来返回rout_str指针了呢


C++技术网会员解答:

    您好,感谢你对C++技术网的支持与信任。

    这个问题是二维数组和指针的混合使用问题,稍微有点绕。我详细的分析下。

    第二个函数书写有点小错误,纠正如下:

void GetRoutToolArray(int& len,const char(*rouArray)[50] )//char写漏了
{
    static const char rout_str[][50] = {"rou060","rou070","rou080"};
    rouArray = rout_str;
    len=sizeof(rout_str)/sizeof(rout_str[0]);//遗漏
}
    这个函数看起来没有问题了。但是却不能正常的工作。因为二维数组并不能正常的返回。不信可以下面的代码测试下:

int len =0;
const char (*pArr)[50]=0;
GetRoutToolArray(len,pArr);
for (int i=0;i<len;i++)
{
    printf("%s\n",pArr[i]);
}
    不仅不能工作,而且还要崩溃。因为pArr的值为0x00000000。为什么?怎么会这样,试试看吧。

    我们的指针参数rouArray是二维数组,要返回的数据也是二维数组,我们就是想要用二维指针返回二维数组数据。看起来有点绕,那么我们来简化一下。类似的,我们就是用变量来返回函数内变量的值。如下:

void test(int a)
{
    a=5;
}
    当你看到这个代码是,你会觉得好笑,这样的a怎么能够传回a的值呢?!!为什么?因为这个a和5属于同级的,同级并没有办法提拔同级。我把返回数据比作提拔,你会更好理解。而如果我们将函数改为这样:

void test1(int &a)
{
    a=5;
}
void test2(int *a)
{
    *a=5;
}
    此时a已经发生了变化,test1中的a变成了引用类型,相当于同级的人被赋予了临时长官的身份,他可以拥有提拔同级人的能力了,所以可以将5传到上级调用中。而test2的a已经正式变为上级了,自然可以将5传到上级。

    我们可以将指针的级数看成是军衔,等级越高,能力越强。所以二级指针可以提拔一级指针,一级指针可以提拔变量,变量是最底层的,没有提拔权限。在编程中就是参数返回值功能。

    自然而然,二级指针也无法提拔二级指针,一级指针无法提拔一级指针。所以GetRoutToolArray的应用就是二级指针提拔二级指针的案例。二维数组和指向一维数组的指针都是二级指针,只是形式不一样,本质是一样的。所以同级是不能提拔同级的。如果想GetRoutToolArray能够实现提拔即返回数据,可以将rouArray改为引用类型即可。当然也可以改为三级指针,也就是再取地址一下就可以了。改进之后如下:

//使用引用,调用代码无需改变,改动最少
void GetRoutToolArray3(int& len,const char(*&rouArray)[50] )
{
    static const char rout_str[][50] = {"rou060","rou070","rou080"};
    rouArray = rout_str;
    len=sizeof(rout_str)/sizeof(rout_str[0]);
}
     使用引用类型,临时赋予提拔能力,就可以传值了。而使用三级指针形式,如下所示:

void GetRoutToolArray3(int& len,const char(**rouArray)[50] )
{
    static const char rout_str[][50] = {"rou060","rou070","rou080"};
    *rouArray = rout_str;
    len=sizeof(rout_str)/sizeof(rout_str[0]);
}
    也就是在参数的指针上再加一级,在赋值的时候使用*取值后再赋值。这样就可以达到提拔作用。那么测试代码也需要同步改动,如下:

int len =0;
const char (*pArr)[50]= 0;
GetRoutToolArray3(len,&pArr);//取地址后接受数据
for (int i=0;i<len;i++)
{
    printf("%s\n",pArr[i]);
}
    改动地方就是传递的是二级指针的地址,形成三级指针。

    这样,通过引用的临时授权和升级永久授权,都可以提拔,实现传回数据的能力。

    另外,提问中问了char**和char rout_str[3][10]类型不同,我解释一下。然后再解释下,如何用char**来传递char rout_str[3][10]类型。

    char**是最原始最直接的二级指针形式,而char rout_str[3][10]则是在原始的基础上发生了变异进化,进化的部分是对每一级指针的作用进行了限制。第一级是[10],表示这一级指针能够操作的范围最多就只有10个字节,不能越界。第二级是[3],表示有三个一级的[10],也就是最多只能操作3个[10],超过后会越界。这是更高层次的抽象,将每一级的指针进行了限定。

   也就是说,虽然本源是一样的,但是char rout_str[3][10]进化到了一个特定的用途,形式上更高级,使用上更受限。所以在现代的char rout_str[3][10]进化的世界里,char rout_str[3][10]是现代人,cahr**是野蛮人。但是野蛮人有洪荒之力,无所不能。现代人为了区分两者的不同,就定义为了两种类型。但是野蛮人有洪荒之力,可以进行强制转换。所以,C/C++语言的强制类型转换就是洪荒之力,无比强大,又无比危险。

    所以,我们是可以用char**来传递char rout_str[3][10]类型的,要借助洪荒之力!!否则在现代世界里,无法完成。

    那么利用char**来传递char rout_str[3][10]传递数据的依据是什么呢?那就是类型的本质是一样的,都是二级指针。这有点想回归本源,才能使用洪荒之力!在我们现实的世界里,我们或许有一天,也可以回归宇宙的本源,通过量子纠缠实现任何物体的超距离传递。归根结底,我们都是有宇宙最小的粒子组成的。使用的工具就是宇宙的洪荒之力即超距作用的量子纠缠。C/C++编程世界里就是强制类型转换,基于最本源的二进制单位本质一样。二进制是计算机世界的最小粒子,强制类型转换好比是超距作用。哈哈哈,最近对量子物理感兴趣,就将这个联系类比在一起了。

    我们来看看如何实现:

void GetRoutToolArray(int& len,const char** &rouArray )
{
    static const char rout_str[3][10] = {"rou060","rou070","rou080"};
    rouArray = (const char**)rout_str;
    len=3;
}
    我们在参数里就用了char**类型,没有任何问题。不过在赋值时一定要进行强制类型转换,让最本质的类型得到统一,就可以利用最本源的方法进行传送。不过,我们传送的类型是进化后的类型,通过本源方法是传送了,但是如果要正确的使用,还得还原出进化后的类型。所以,测试代码也要做很大的改变。先看看这段代码:

void main()
{
    int len;
    const char** arr=0;
    GetRoutToolArray(len,arr);
    char (*pArr)[10] = new char[len][10];
    memcpy(pArr,arr,len*10);
    for (int i=0;i<len;i++)
    {
        printf("%s\n",pArr[i]);
    }
    delete []pArr;
}
    我们的arr就是最原始的类型,用来接收从底层世界(函数)传来的本质的数据。接收之后,我们要将其还原成进化后的类型,再来使用。所以我们要先准备一个进化后的类型,为了适配函数返回的不同的二维数组,我们使用new来动态创建。然后再使用mencpy将数据填充到进化后的类型,这样就像将原始灵魂注入了一个新生的身体里,这样就实现了传送。这就是基于本源的传送。然后后面的使用就和正常的进化后的类型一样了。

    当然,我们还可以进行优化,也就是不要char**类型做中转,直接用进化后的身体来接受原始数据,反正本源的数据是一样的,就用做好的模型依次接受就行了。代码如下:

void main()
{
    int len;
    char Arr[3][10]={0};
    const char *** arr = (const char***)&Arr;//arr作为简单的转换类型介质
    GetRoutToolArray(len,*arr);//此时接受的数据直接灌入Arr的身体,不再需要数据拷贝了。

    for (int i=0;i<len;i++)
    {
        printf("%s\n",((char (*)[10])*arr)[i]);//然后使用arr来访问.因为Arr只是一个二维数组的地址,不是指针,无法完成地址的穿越了。
    }
}
    如果你想直接将数据灌入Arr身体,还想用Arr直接接受,可以这样:

void main()
{
    int len;
    char (*pArr)[10] = new char[3][10];
    char (*pArr2)[10] = pArr;
    GetRoutToolArray(len,*(const char***)&pArr);//先将pArr通过取地址变成三维的,然后变为通用类型,再降级为二级和参数匹配
    for (int i=0;i<len;i++)
    {
        printf("%s\n",pArr[i]);
    }
    //delete [] pArr;//这里是错误的。因为pArr在GetRoutToolArray函数中已经被破坏
    delete [] pArr2;
}
    最后一个版本,就是用数组本身接受,再用数组本身去显示数据,如下:

void main()
{
    int len;
    char Arr[3][10]={0};
    GetRoutToolArray(len,*(const char***)&Arr);//此时接受的数据直接灌入Arr的身体,不再需要数据拷贝了。
    //const char *** arr = (const char***)&Arr;//arr作为简单的转换类型介质
    for (int i=0;i<len;i++)
    {
        printf("%s\n",((char (*)[10])*((const char***)&Arr))[i]);
        //看起来复杂多了。无非就是先转为通用的类型,再降级为二级通用指针,在转为正确的二维数组,再取元素
        //上面注释的一行代码可以用来简化来简化理解,分步实现
    }
}
    这些演化,都可以得到正确的效果,如下:

如何返回函数内的二维数组

    本解答中不仅包含了如何返回二维数组,更重要是形象的讲解了这些返回的哲学思想。还演示了各种使用方法。得到的结果都是一样的。如果能够将这个解答的代码看的游刃有余,指针的使用,就没有问题了。

    指针是C语言的灵活,在此可见一斑了。真的很灵活,很强大,只有你想不到的,没有做不到的。好好体会一下。