二维数组作为函数参数

先上代码如下:

#include <stdio.h>
void disp(int a[][2]){printf("%d\n",a[0][0]);}
int main(){
    int a[][2] = {1,2,3,4};
    disp(a);
    return 0;
}

这是naive的实现,假设disp函数的定义如下呢?

void disp(int *a[2]){printf("%d\n",a[0][0]);}

编译提醒(没有错误)如下:

main.c:6:8: warning: incompatible pointer types passing 'int [2][2]' to
  parameter of type 'int **' [-Wincompatible-pointer-types]
    disp(a);

main.c:3:17: note: passing argument to parameter 'a' here
void disp(int *a[2]){printf("%d\n",a[0][0]);}
            ^

1 warning generated.

从编译结果来看,只是产生了warning,没有error,但是运行的时候出现段错误!这也从一方面验证了对于代码的warning也要保持足够警惕。

在Ubuntu下的提示如下:

note: expected ‘int **’ but argument is of type ‘int (*)[2]’

究其原因是上述函数声明的是指向2个元素的一维数组,每个元素类型为指向int的指针类型。为什么会导致这样的理解?’[]’的优先级比’*‘高。修正如下:

void disp(int (*a)[2]){printf("%d\n",a[0][0]);}

此时声明了一个指针,指向具有2个元素的一维数组。2表示的是二维数组的列,这样就满足了expected int。

返回数组

先看代码:

#include <stdio.h>
int* func(int* a,int len){
    //int* tmp = new int[len];
    for(int i = 0;i < len;i++){a[i]++;}
    return a;
}
int main(){
    int len = 4;
    int a[] = {1,2,3,4};
    int *b = func(a,len);
    for(int i = 0;i < len;i++){printf("%d ",b[i]);}
    return 0;
}

假设代码中取消注释行,重写函数func有:

int* func(int* a,int len){
    int tmp[len];
    for(int i = 0;i < len;i++){tmp[i]=a[i];}
    return tmp;
}

编译提示:

main.cpp:6:9: warning: address of stack memory associated with local variable
  'tmp' returned [-Wreturn-stack-address]
    return tmp;
           ^~~
1 warning generated.

很显然,从函数调用过程来说,当函数返回时,调用栈的空间全部释放。相比与原来的定义,是直接在传入的数组空间进行操作,所以直接返回即可。

解决的方法就比较清楚了,在局部函数内开辟在堆上的空间。

int* func(int* a,int len){
    int* tmp = new int[len];
    for(int i = 0;i < len;i++){tmp[i]=a[i];}
    return tmp;
}

注意,在调用该函数后进行内存空间的释放。考虑到空间的开辟和释放不在同一个位置,很容易出现忘记释放内存的情况,因此另外一种情况是在main函数中开辟一个空间,将该空间作为函数参数传递到func中,赋值后返回,这种方式和原始定义类似。

总结:参数传递要指定列数,返回要手动开辟和释放内存空间。

重要参考:

C++中数组的定义和初始化