函数形参中的二维数组与二级指针

函数形参中的二维数组与二级指针

前言:

  二维数组在leetcode给出的模板函数中通过二级指针传递,同时会给出行数Row与列数Col数组,往往在函数中可直接使用a[i][j]遍历二维数组每一个元素。而实际在编写代码时声明的二维数组如果在栈中,此时在函数中通过a[i][j]遍历数组会出现段错误。为了探究其原因,本文对数组指针,指针数组以及函数形参中的二维数组与二级指针进行了总结。

1.数组指针与指针数组

[]运算符优先级高于* 因此[]先与p结合 
int *p[10]; 	// 指针数组 一个大小为10的int *型指针数组
int (*p)[10]; 	// 数组指针 一个指向大小为10的int型数组指针

2.二维数组中的数组名与解引用

例如:二维数组a[3][3],其中:
a			a的首地址			类型为:	int (*)[3]
&a			a的首地址			类型为:	int [3][3]
&a[0]		a的首地址			类型为:	int (*)[3]
&a[0][0]	a的首地址			类型为:	int *

  由以下代码可验证以上类型声明,同时说明栈中二维数组的内存地址连续排列如同一维数组一样,a[3][3]等价于a[9] = {1,2,3,4,5,6,7,8,9},此时a[2][1]等价于a[1][4],在此处不会发生数组越界或段错误,即a[i][j] = *(*(a+i)+j) = *((int *)a + i*Col + j)成立。

/* 二维数组中的数组名与解引用 */
#include <stdio.h>                                                          
int main(){
    int a[3][3] = {
        {1,2,3},
        {4,5,6},
        {7,8,9}
    };
    printf("a in stack\n");
    printf("a:%p\ta+1:%p\n",a,a+1);
    printf("&a:%p\t&a+1:%p\n",&a,&a+1);
    printf("&a[0]:%p\t&a[0]+1:%p\n",&a[0],&a[0]+1);
    printf("&a[0][0]:%p\t&a[0][0]+1:%p\n",&a[0][0],&a[0][0]+1);
}

在这里插入图片描述
  上述代码二维数组声明在栈中,而在使用malloc动态声明二维数组时,往往是先声明一个二级指针(int **a)再将每一行子数组的首地址存入,此时a[i][j] = *(*(a + i) + j)一定成立而不一定等于*((int *)a + i*Col + j),因为每一行子数组动态分配的地址空间不一定连续。

#include <stdio.h>   
#include <stdlib.h>
int func(int **array, int m, int n) {
    printf("array in heap\n");
    printf("array[i][j]\t*((int *)array + i * 3 + j)\n");
    for (int i = 0; i < m; ++i) {
        for (int j = 0; j < n; ++j) {                                       
            printf("%d\t\t", array[i][j]);
            printf("%d", *((int *)array + i * n + j));
            printf("\n");
            
        }
    }
    printf("\n");
    return 0;
}
int main() {
    int **array = (int **)malloc(sizeof(int *) * 3);
    for (int i = 0; i < 3; ++i) {
        array[i] = (int *)malloc(sizeof(int) * 3);
        for (int j = 0; j < 3; ++j) {
                array[i][j] = i * 3 + j + 1;
        }
    }
    func(array,3,3);
    return 0;
} 

在这里插入图片描述

3.函数形参中的二维数组与二级指针

  首先需要值得注意的是二维数组与二级指针没有任何关系,然后在向函数传递二维数组时需要注意其是在栈还是在堆中动态分配的。

  • 堆分配
    在上一节中已经提及了二维数组的堆分配,函数形参通常直接使用二级指针传递,在函数中可以直接通过a[i][j]遍历数组,但不能通过*((int *)a + i *Col + j)遍历。
  • 栈分配
    如果二维数组是在栈中分配此时情况复杂一些,常规的方式是以下三种形式:
// 1.int (*)[Col]形参类型
void fun(int a[Row][Col]);
void fun(int a[][Col]);
void fun(int (*a)[Col]);

  在以上三种方式中,在函数中可以直接通过a[i][j]遍历数组,也可以通过*((int *)a + i *Col + j)遍历数组。除此之外,栈中分配的二维数组也可以通过二级指针传递,然而在这种方式下不能直接通过a[i][j]遍历数组,只能通过*((int *)a + i *Col + j)遍历数组。

// 2.int **形参类型
void fun(int **a);

  数组名a的实际类型为int (*)[Col],在使用数组名a作为函数实参时,类型强制转化为int **,a[i][j]的解引用*(*(a + i) + j),实际上等效为**((int *)a + i + j),访问了值为a[i + j]的非法地址造成段错误。为了解决这个问题,可以在遍历前将二级指针(int **)转化为(int (*)[Col])指针,或者在实参传入前,将每一行子数组的首地址存入一个指针数组中。

// 仅适用于Col等长 例如:int a[3][3] = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};
1.int (*ptr)[Col] = (int (*)[Col])a;
  for (int i = 0; i < Row; ++i) {
  	for (int j = 0; j < Col; ++j) {
    	printf("%d\t", ptr[i][j]);
    }
  }
// 不仅适用于Col等长还适用于Col变长 例如:char str[][5] = {"a", "ab", "abc"};
2.int *ptr[Row] = {0};
  for (int i = 0; i < Row; ++i) {
  		ptr[i] = a[i];
  }
  fun(ptr);
  • 4
    点赞
  • 32
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值