C++中二维数组作为函数参数
在平时,我们经常会遇到将整个数组作为函数参数的情况,一维数组的情况,就是用数组名当形参和实参,传递的是数组的首地址。二维数组我们用的也很多,但是总是有各种问题,今天我总结一下
有个很重要的一点,字符串“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]来直接访问,因为编译器无法为其定位。
这里会有寻址方式的改变,比如什么时候下标寻址,什么时候只能指针寻址,暂时我没有找到权威的解答,先不说了。以后补充。
C++中二维数组作为函数参数的更多相关文章
- C-指针,二级指针,二维数组作为函数参数使用,C语言链表(详解)
一级指针 int *p; //表示定义一个int型(4字节)的指针p &p //表示p自身的地址位置 p ...
- [语法]C语言中二维数组做输入参数
C语言中二维数组做输入参数时, 可以同时指定各维长度, 可以只指定第二维的长度, 不可以只指定第一维的长度, 不可以各维长度都不指定. 一句话总结:要指定至少指定第二维,都不指定是不行的. 具体栗子如 ...
- C语言二维数组作为函数参数
设有整型二维数组a[3][4]如下:0 1 2 34 5 6 78 9 10 11 它的定义为: int a[3][4]={{0,1,2,3},{4,5,6,7} ...
- C语言中将二维数组作为函数参数来传递
c语言中经常需要通过函数传递二维数组,有三种方法可以实现,如下: 方法一, 形参给出第二维的长度. 例如: #include <stdio.h> void func(int n, char ...
- C语言二维数组作为函数的参数
前言:今天在实现装配线调度程序时候,用到了二维数组,并将其作为函数的参数.在写程序的时候,遇到一些问题,即二维数组做函数的参数应该如何正确表示.我写程序的错误如下程序所示: #include < ...
- C语言 数组做函数参数退化为指针的技术推演
//数组做函数参数退化为指针的技术推演 #include<stdio.h> #include<stdlib.h> #include<string.h> //一维数组 ...
- [C++程序设计]用数组名作函数参数
1. 用数组元素作函数实参 #include <iostream> using namespace std; int max_value(int x, int max) { return ...
- C语言学习笔记 (005) - 二维数组作为函数参数传递剖析
前言 很多文章不外乎告诉你下面这几种标准的形式,你如果按照它们来用,准没错: //对于一个2行13列int元素的二维数组 //函数f的形参形式 f(int daytab[2][13]) {...} / ...
- C/C++中二维数组和指针关系分析
在C/c++中,数组和指针有着密切的关系,有很多地方说数组就是指针式错误的一种说法.这两者是不同的数据结构.其实,在C/c++中没有所谓的二维数组,书面表达就是数组的数组.我猜想是为了表述方便才叫它二 ...
随机推荐
- Kioptrix Level 2
简介 Vulnhub是一个提供各种漏洞环境的靶场平台. 个人学习目的:1,方便学习更多类型漏洞.2,为OSCP做打基础. 下载链接 https://www.vulnhub.com/entry/kiop ...
- SAPCAR使用说明
1.首先看一下SAPCAR的功能usage:create a new archive:SAPCAR -c[vir][f archive] [-P] [-C directory] [-A filen ...
- 04--Docker数据卷和数据卷容器
.为什么要使用数据卷: Docker容器产生的数据,如果不通过docker commit生成新的镜像,使得数据做为镜像的一部分保存下来,那么当容器删除后,数据自然也就没有了.为了能保存数据在docke ...
- [Noip模拟题]Seq
题目描述 由于hyf长得实在是太帅了,英俊潇洒,风流倜傥,人见人爱,花见花开,车见车载.有一群MM排队看hyf.每个MM都有自己独特的风格,由于hyf有着一颗包容的心,所以,什么风格的MM他都喜欢-- ...
- Hadoop2.7.7阿里云安装部署
阿里云的网络环境不需要我们配置,如果是在自己电脑上的虚拟机,虚拟机的安装步骤可以百度.这里是单机版的安装(也有集群模式的介绍)使用Xshell连接阿里云主机,用命令将自己下载好的安装包上传到服务器 # ...
- RecyclerView 源码分析(二) —— 缓存机制
在前一篇文章 RecyclerView 源码分析(一) -- 绘制流程解析 介绍了 RecyclerView 的绘制流程,RecyclerView 通过将绘制流程从 View 中抽取出来,放到 Lay ...
- Centos 7 关机和重启 命令
1,关机命令 1 shutdown -h now/0 2 halt 3 init 0 4 poweroff 5 举例: 6 shutdown -h 3 ------3分钟后关机(可用shutdown ...
- JavaScript中创建对象的三种方式!
JavaScript中创建对象的三种方式! 第一种 利用对象字面量! // 创建对象的三种方式! // 1 对象字面量. var obj = { // 对象的属性和方法! name: 'lvhang' ...
- KDB调试 — ARM
1 寄存器 1.1 通用寄存器 A64指令集可以看到31个64位通用(整数)寄存器,分别是R0-R30. 在64位上下文中,这些寄存器通常使用名称x0-x30来表示; 在 ...
- Redis 常见问题总结
1. 简单介绍一下 Redis 呗! 简单来说 Redis 就是一个使用 C 语言开发的数据库,不过与传统数据库不同的是 Redis 的数据是存在内存中的 ,也就是它是内存数据库,所以读写速度非常快, ...