C语言二维数组传参理解

二维数组传参问题

刷力扣时遇到二维数组传参问题,使用度娘、谷歌,还查看了力扣上的相关讨论写一下自己的理解。

问题引入

使用c语言来作为解答问题语言时,力扣给出的模板:

int largestOverlap(int** img1, int img1Size, int* img1ColSize, int** img2, int img2Size, int* img2ColSize){

}

/*
	img1、img2是两个图像(0,1像素矩阵)
	img1Size,img2Size为图像行数
	img1ColSize,img2ColSize为图像列数
	
	该题中两个图象大小相同所以 只对img1进行理解分析即可。
*/

(个人理解:)
img1ColSizeint *类型,可以理解为一个数组,因为一个二维数组列数不一定相同

img1 = [[1,2,3],
		[2,3],
		[3,4]]
img1Size = 3, img1ColSize= [3,2,2]

​ 这同样是二维矩阵,力扣给出这种类型参数应该是考虑了通用情况,835这题是n*n的,列数相同(img1ColSize 每个元素相等)。

​ 当我提交code时没有ac,59个运行示例有两个没有通过,在Clion上进行Debug(15行 断点)

/* main函数:*/

int main() {

    //30*30的二维矩阵,太长了使用'...'代替
    int img1[][30] = {{1,0,...}
                      {0,0,...}};
    int img2[][30] = {{***}};
    
    int imgColSize[30];
    for (i = 0; i < 30; i++) {
        imgColSize[i] = 30;
    }
    
    largestOverlap(img1, 30, imgColSize, img2, 30, imgColSize);
    
    return 0;
}
/* largestOverlap函数 */
#define POINT(X, Y) ((X<<5) + (Y))
for (i = 0; i < img1Size * img1Size; ++i) {
    if (img1[i]) {
        listA[len_a++] = POINT(i / img1Size, i % img1Size);
    }
    if (img2[i / img2Size][i % img2Size]) {
        listB[len_b++] = POINT(i / img2Size, i % img2Size);
    }
}
/*
	遍历两个图像,将图像中 1 的点保存在对应数组中
	POINT 使用高位存储 点的行,低5位存储点的列(题目中明确表示图像最多是30*30)
	
	第7行报错,Exception 0xc0000005 encountered at address 0x3973bf: Access violation reading location 0x00000000
	无法读入
*/

起初认为是越界,但在检查后发现并没有,因此在代码上面尝试直接读取img1[0][0]

尝试几种读取方式:

//int c = img1[0][0];××
//int c = *((*img1 + i) + j);×
int c = *((int *) img1 + 0*30 + 0);

只有第三种方式读取成功。WTF?

二维数组存储

计算机内存存储二维数组和一维数组没什么区别,都是在连续内存中进行存储:比如3*3矩阵

内存: 0 0 0 0 0 0 0 0 0

一维:a[0] a[1] …………………………… a[8]

二维:a[0][0] …………………………………a[2][2]

这个知识点我之前是知道的,但我的疑惑在于为什么我使用二维数组的读取方式(img1[0][0])读不出来呢?

继续查资料。。。

数组名

(个人理解:)

数组名也就是数组首元素地址。

数组名也就是数组首元素地址。

数组名也就是数组首元素地址。

类比一下一维数组:数组名是a[0]的地址。

一维数组二维数组
a[0]的地址a[0]的地址
a[0]是一个int元素的地址a[0]是一个int*元素的地址

二维数组中的元素是一维数组。

一个包含3个元素的一维数组int a[3]可以看成:一个存储int的一维数组,数组长度为3

一个3行3列的二维数组int img[3][3]可以看成:一个存储int [3](列)的一维数组,数组长度为3(行)

个人认为这也就是二维数组在使用int [][]这种形式传参或者初始化时能省略行但不能省略列的原因:

因为在参数传递或者变量初始化时要给出参数类型,在一维数组中参数类型就是int,而二维数组中参数类型不是int 而是int [列数]。

int[3][5] type = int[5]

int[3][4] type = int[4]

所以二维数组的数组名是首个一维数组的地址:一个数组地址。


回到刚才的Debug:由于我在main函数中使用img1来传参,那么此时img1是一个数组地址,就应该用数组指针来接收int(*img)[30] 也可以写成int img[][30]

个人理解:(*img) 和img[]没区别 就像一维数组中 a[i] = *(a+i)一样

进行测试:

void arrPrint2(int (*arr)[30], int rowSize) {
    int i, j;
    for (i = 0; i < rowSize; i++) {
        for (j = 0; j < 30; j++) {
            printf("%2d", *(*(arr + i) + j));
//            printf("%2d",arr[i][j]);
        }
        printf("\n");
    }
}

使用上面两种方式都能读取二维数组中的元素了。

传参测试:

我定义下列三个函数:

void arrPrint1(int arr[][30], int rowSize) {
    int i, j;
    for (i = 0; i < rowSize; i++) {
        for (j = 0; j < 30; j++) {
            printf("%2d", arr[i][j]);
        }
        printf("\n");
    }
}

void arrPrint2(int (*arr)[30], int rowSize) {
    int i, j;
    for (i = 0; i < rowSize; i++) {
        for (j = 0; j < 30; j++) {
           	printf("%2d",arr[i][j]);
        }
        printf("\n");
    }
}

void arrPrint3(int **arr, int rowSize, int colSize) {
    int i, j;
    for (i = 0; i < rowSize; i++) {
        for (j = 0; j < colSize; j++) {
            printf("%2d", arr[i][j]);
        }
        printf("\n");
    }
}
arrPrint1
// main 函数:
img1 = {int[30][30]}0x00efecc4{0x00efecc4{1,0,0,...}}
/* img1的值为0x005cea30 也就是其首元素(第一行数组)的地址 img1 */
arr[0] = {int[30]}0x00efecc4{1,0,0,...}}
*arr = {int*}0x00efecc4{1}
*(arr+0)={int*}0x00efecc4{1}
arrPrint2
// main 函数:
img1 = {int[30][30]}0x00efecc4{0x00efecc4{1,0,0,...}}
/* img1的值为0x005cea30 也就是其首元素(第一行数组)的地址 img1 */
arr[0] = {int[30]}0x00efecc4{1,0,0,...}}
*arr = {int*}0x00efecc4{1}
*(arr+0)={int*}0x00efecc4{1}
arrPrint3
// main 函数:
img1 = {int[30][30]}0x00efecc4{0x00efecc4{1,0,0,...}}
/* img1的值为0x005cea30 也就是其首元素(第一行数组)的地址 img1 */
arr[0] = {int*}0x00000001{???}}
*arr = {int*}0x00000001{???}}
*(arr+0)={int*}0x00000001{???}}

顿悟

临睡前突然灵光乍现:

main函数中传参:二维数组名img1 根据上面分析img1表示数组第一行的地址,所以传递过去的只是一个地址。如何对待这个地址要看用什么类型的参数来接收。

arrPrint1把该地址以int arr[][30]形式来接收,此时arr = img1也是二维数组名,所以在arrPrint1中编译器把img1传进来的地址当作二维数组名来处理,所以arr[0] ,*arr不会去解引用该地址内部的值,编译器会把arr当作数组名,去取arr的首元素地址(也就还是img1传进来的地址)。

eg:假设  地址0x00efecc4中内容为 int 1
img1 = 0x00efecc4
arr = 0x00efecc4
*arr not = *(0x00efecc4)=1
*arr = 0x00efecc4(把arr当作数组名来处理)
arr[0] = 0x00efecc4(同理)

arrPrint2同理

arrPrint3arr = img1但此时arr是二维指针,arr[0]表示*(arr+0)也就是对arr进行解引用,也就是对地址解引用,该地址是img1首元素地址,也就是首元素(第一行数组的地址)也就是(第一行数组首元素地址)因此对该地址解引用会直接得到地址存储的int值,再解引用就会指向0x00000001的内容,不可读。


int**

使用int**接收参数来处理二维数组必须手动malloc

 int *test1[30];
    int numColSize[30];
    int i, j;
    for (i = 0; i < 30; i++) {
        numColSize[i] = 30;
        test1[i] = (int *) malloc(sizeof(int) * numColSize[i]);
        for (j = 0; j < numColSize[i]; j++) {
            test1[i][j] = img1[i][j];
//            printf("%2d",test1[i][j]);
        }
//        printf("\n");
    }

此时使用arrPrint3可以正确读取元素。因为传参test1的地址和test1[0]地址不相同。

表达可能有些许混乱,仅作为学习笔记,如果有幸让大家看到,欢迎批评指正,一起交流学习。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值