C++中二维数组作为函数参数

Wesley13
• 阅读 601

在平时,我们经常会遇到将整个数组作为函数参数的情况,一维数组的情况,就是用数组名当形参和实参,传递的是数组的首地址。二维数组我们用的也很多,但是总是有各种问题,今天我总结一下

有个很重要的一点,字符串“China”在编译器眼里就是一个地址!操作字符串是通过它在内存中的存储单元的首地址进行的,这是字符串的终极本质

如果 "China", 存储在内存中的 0x3000 0x3001 0x3002 0x3003 0x3004 0x3005 .

s = "China" ,赋值了什么,地址。

其实真正的意义是 s ="China" = 0x3000;

首先我们从指针的角度重新认识下二维数组,int a[3][4],这里的a是一个行指针,指向的是二维数组的行首,a+1指向的是第二行,指的是a[1],它的类型是int(*)[4]。a[i]是一个元素指针,比如a[0]是第1行的一维数组的数组名,指向的是a[0][0],a[0]+1指向的是a[0][1]。它的类型是int*。

int (*p)[4],这个时候p和a是等价的,因为都是行指针,类型都是int(*)[4]。

(1)二维数组指定行数列数作为形参,实参是二维数组名

void f(int a[3][4]);  
void f(int a[][4]);
void f(int (*a)[4]);

上述三种都是等价的。但是不能省略数组的列数,因为实参传递的是二维数组的起始地址,主要原因是二维数组在栈内分配的内存是连续的,它的每一行都有相同的元素,这样,a[i][j] 和 *(*(a +i) +j)是一样的,程序是知道array+i的i实际上偏移了i*N个单位,这也导致了在二维数组array[3][3]中,使用下标array[2][1]和array[1][4]是访问的同一个元素,尽管后者的下标对于一个3*3矩阵来说是非法的,但这并不影响访问。省略了列数,内存不知道如何存放二维数组了。

缺点:该方式的缺点是,必须事先固定数组的行数列数,不太方便。如果我的数组事先不知道多大,这种方法就不适合了。

(2)二维数组看成一维数组访问,实参的形式有两种,但都是int* 型,一种是*a,一种是a[0],都指向的是一维数组的第一个元素

int a[2][2] = {2,3,4,5}; //4个元素时连续排列的内存段
//void f(int p[][2], int row, int col )//这种方式必须事先知道除第一维以外的维度的大小,不灵活
void f(int *p , int row, int col )//转化为一维数组来访问
{
    for(int i = 0; i < row; i++)
    {
        for(int j =0 ;j < col; j++)
        {
            cout<<p[i*col+j]<<" ";
        }
    }
    cout<<endl;
}

在被调函数中,寻址的方式可以是程序中的方式,也可以是*(p+i*col+j)的方式。

这里int** p =a;是错误的,因为p是int** 型,而a是int(*)[2]的。

解释下原因:(1)类型不同是很明显的(2)从指向的角度来说,a表示的是数组中a[0]的地址,也就相当于a[0][0]的地址,如果p=a的话,p也代表a[0][0]的地址,*p代表的是a[0][0]本身的值,**p访问的是地址为a[0][0](这里等于2)的内存空间,这是不允许的。

但是有一种情况就可以这么赋值

char *a[2]={"hello","world"}; char** p=a;

这些定义中a的类型不是int(*)[2],而是int* 了(按上述说应该是char*),p=a的话,*p=a[0],所以a代表的数组首地址,即a[0]的地址。*p也就是a[0]中存放的是"hello"的首地址,**p也就表示的是'h'了。这是合法的。

(3)二维数组通过二级指针传递,实参必须为指针

void subfun(int n, char **subargs)  
{  
    int i;   
    for (i = 0; i < n; i++) 
    {  
        printf("subargs[%d] = %s\n", i, subargs[i]);  
    }  
}  
  
void main()  
{  
    char *a[3];  
    char args[][5] = {"abc", "def", "ghi"};  
    a[0] = args[0];  //equals with a[0] = &args[0][0];  
    a[1] = args[1];  
    a[2] = args[2];  
    subfun(3, a);  //若此处为subfun(3, args);则会编译出错  
}

void print(float **tab,int rows,int cols)
{
    for(int i=0;i<rows;i++){
        for(int j=0;j<cols;j++){
            cout<<tab[i][j]<<" ";
        }
        cout<<endl;
    }
}
int main()
{
    float ta[2][3]={{1.0,2.0,3.0},{4.0,5.0,6.0}};
    float **p=new float *[2];//开辟行空间
    for(int i=0;i<3;i++)
           p[i]=new float[i];//开辟列空间
    for(int i=0;i<2;i++){    //赋值
        for(int j=0;j<3;j++){
            p[i][j]=ta[i][j];
        }
    }
    cout<<"ta: "<<endl;
    print(p,2,3);//打印
    //p的内存释放方式
    for(int i=0;i<3;i++)
        delete[]p[i];
    delete []p;
    return 0;
}

其实上述两个程序是相同的处理方式,都是又额外申请了一段二级指针指向的内存,然后把数组值拷贝到这一块内存中,用完后必须手动释放内存。这样就可以和被调函数的二级指针对应上了。

另外还有一种强制转换的传参方式。

实参传递:

int a[3][4];  
f((int **)a,3,4);

这样在被调用数组中对对元素a[i][j]的访问可以使用如下形式:

*((int *)a+n*i+j);  

注意不能使用a[i][j]来直接访问,因为编译器无法为其定位。

这里会有寻址方式的改变,比如什么时候下标寻址,什么时候只能指针寻址,暂时我没有找到权威的解答,先不说了。以后补充。

点赞
收藏
评论区
推荐文章
blmius blmius
2年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Jacquelyn38 Jacquelyn38
2年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
Wesley13 Wesley13
2年前
java将前端的json数组字符串转换为列表
记录下在前端通过ajax提交了一个json数组的字符串,在后端如何转换为列表。前端数据转化与请求varcontracts{id:'1',name:'yanggb合同1'},{id:'2',name:'yanggb合同2'},{id:'3',name:'yang
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
Stella981 Stella981
2年前
Opencv中Mat矩阵相乘——点乘、dot、mul运算详解
Opencv中Mat矩阵相乘——点乘、dot、mul运算详解2016年09月02日00:00:36 \牧野(https://www.oschina.net/action/GoToLink?urlhttps%3A%2F%2Fme.csdn.net%2Fdcrmg) 阅读数:59593
Stella981 Stella981
2年前
KVM调整cpu和内存
一.修改kvm虚拟机的配置1、virsheditcentos7找到“memory”和“vcpu”标签,将<namecentos7</name<uuid2220a6d1a36a4fbb8523e078b3dfe795</uuid
Easter79 Easter79
2年前
Twitter的分布式自增ID算法snowflake (Java版)
概述分布式系统中,有一些需要使用全局唯一ID的场景,这种时候为了防止ID冲突可以使用36位的UUID,但是UUID有一些缺点,首先他相对比较长,另外UUID一般是无序的。有些时候我们希望能使用一种简单一些的ID,并且希望ID能够按照时间有序生成。而twitter的snowflake解决了这种需求,最初Twitter把存储系统从MySQL迁移
Stella981 Stella981
2年前
JS 对象数组Array 根据对象object key的值排序sort,很风骚哦
有个js对象数组varary\{id:1,name:"b"},{id:2,name:"b"}\需求是根据name或者id的值来排序,这里有个风骚的函数函数定义:function keysrt(key,desc) {  return function(a,b){    return desc ? ~~(ak
Wesley13 Wesley13
2年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
2个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这