序
最近复习c/c++数组的传参,发现了一些问题,下面是一些总结和思考
正文
一维数组的传参/一级指针的传参/普通指针
在理解指针的基础上,一维数组的指针传递很简单,我们知道数组的数组名就是这个数组首元素的地址,所以将数组名传入函数就可以了
// 一维数组的传递0.cpp
# include <stdio.h>
void f(int *pArr, int len) {//这里传入数组的首地址和数组长度,就可以对数组进行操作了
int i;
for (i = 0; i < len; ++i)
printf("%d ", pArr[i] );
//等价于printf("%d ", *(pArr + i) );
}
int main(void) {
int a[5] = {1, 2, 3, 4, 5};//定义一个一维数组
f(a, 5); //a是 int *
return 0;
}
二维数组的传参/二级指针的传参
二维数组的传参就比较麻烦了,你需要搞明白,数组名是什么类型的指针。不然编译不通过。这边重点分析一下,方便对下面三级指针有更深的了解。
我们知道一维数组名是常量指针,我们可以将一维数组名赋给一个指针类型再对一维数组进行相关的操作,那二维数组名又是什么?
我们这样初始化一个二维数组int A[3][3]={1,2,3,4,5,6,7,8}
或者为int A[3][3]={ {1,2,3},{4,5,6},{7,8,9}};
从后面一个定义我们可以看出二维数组是由两个一维数组组成的也就是说二维数组是数组的数组。
问题
了解了二维数组那么二维数组名又是什么呢,是不是和一维数组名一样是个一维指针呢?看下面代码:
#include<stdio.h>
int main() {
int i, j;
int A[3][3] = {{0, 1, 2}, {3, 4, 5}, {6, 7, 8}};
printf("%#x\n", A);
printf("%#x\n", *A);
printf("%d\n", **A);
return 0;
}
从代码的结果可以看出只有**A
才能输出数组中的元素,*A
输出的是一个地址,那么我们是不是可以把二维数组名理解为指针的指针
。然后定义一个指针的指针 int **p
,将数组名赋给p
然后二维数组进行相关操作呢?答案是否定的
,看下面代码:
#include<stdio.h>
void main() {
int i, j;
int A[3][3] = {{0, 1, 2}, {3, 4, 5}, {6, 7, 8}};
int **p;
p = A;
printf("%d", **p);
}
但是这个代码编译不过,显示error
为“无法从int[3][3]转化为int **
,那就说明了二维数组名不是指针的指针
。那二维数组名
又是什么呢?看代码:
#include<stdio.h>
int main() {
int i, j;
int A[3][3] = {{0, 1, 2}, {3, 4, 5}, {6, 7, 8}};
int (*p)[3];
p = A;
for(i = 0; i < 3; i++) {
for(j = 0; j < 3; j++)
printf("%2d", p[i][j]);
//等价于printf("%2d", *(*(p + i) + j));
printf("\n");
}
return 0;
}
从这个结果我们可以看出我们可以先定义一个数组指针int (*p)[3]
,然后将A数组名
然后进行相关操作,即二维数组名是一个数组指针,然后对P+1
指向的是下一个数组
即下一行的地址
进行操作。
原因
二维数组名即数组地址,指向首行地址
,不是指针的指针。
表面上看,行地址即一维数组指针,而数组名指向行搜索就应该是指针的指针。 但是你考虑过没有,如果A[3][3]
,假设int**p=A
; 那么要对行递增执行p++
时,编译器如何知道列宽?
因为int**
是指指向一个int
类型的指针,其数据宽度是4字节
,内置宽度,因此p
将指向下一个整数元素地址,也就是p递增了4字节
,而不是3个int型数据的宽度
,这就错位了。
所以A[3][3]
的地址类型不是简单的指针的指针,而是行指针的指针,而行宽是由你定义的数组列数和元素类型所决定,int
类型就是4*3=12
个字节。这叫数据对齐。因此编译器在转换数组名时,会根据对齐要求而确定相应的指针类型,所以A
的地址类型应该是int ()[3]
,而不是int **
。 所以应该这样int (*p)[3]=A
; 其含义为,p
是一个指向(含3个int类型元素的一维数组或行的)
指针,其实本质上任何指针都是4字节(32位系统),你完全可以将一种指针类型强制转为任何其他类型,那为什么还要区分指针类型,就是为了指针运算时实现数据对齐,准确定位。
那么数组的传递也非常简单了
#include <stdio.h>
void subfun(int n, char (*subargs)[3] ) {//这个3指的是二级数组数据的个数,{0, 1, 2},比如012,有3个元素
int i,j;
for (i = 0; i < n; i++) {
for (j = 0; j < n; j++) {
printf("subargs[%d][%d] = %d\n", i, j, subargs[i][j]);
//等价于 printf("subargs[%d][%d] = %d\n", i, j, *(*(subargs + i) + j));
}
}
}
int main() {
char args[][3] = {{0, 1, 2}, {3, 4, 5}, {6, 7, 8}};
subfun(3, args);
return 0;
}
补充
我们知道字符串的本质也是一个字符型数组,所以看字符串二维数组的传参
#include <stdio.h>
void subfun(int n, char (*subargs)[4] ) {//这个4代表abc\0这个4个字符的长度,就是里面的数组长度
int i;
for (i = 0; i < n; i++) {//为什么这里指循环一遍,而上面的循环了两遍,因为%s,就是输入一个字符串的,即一个字符型数组
printf("subargs[%d] = %s\n", i, subargs[i]);
}
}
int main() {
char args[][4] = {"abc", "def", "ghi"};//这里的"abc" 等价于['a','b','c','\0'];也是一个字符型数组
subfun(3, args);
return 0;
}
三维数组的传参/三级指针的传参
有了之前两层铺垫,这个我们就比较好理解了
首先看下三维数组的首地址是什么
#include<stdio.h>
int main() {
int i, j;
int A[3][3][3] = {{{0, 1, 2}, {0, 1, 2}, {0, 1, 2}}, {{0, 1, 2}, {0, 1, 2}, {0, 1, 2}}, {{0, 1, 2}, {0, 1, 2}, {0, 1, 2}}};
printf("%x\n", A);
printf("%x\n", *A);
printf("%x\n", **A);
printf("%d\n", * **A);
return 0;
}
然后看看看怎样能正常输出这个数组
#include<stdio.h>
int main() {
int i, j, k;
int A[3][3][3] = {{{0, 1, 2}, {0, 1, 2}, {0, 1, 2}}, {{0, 1, 2}, {0, 1, 2}, {0, 1, 2}}, {{0, 1, 2}, {0, 1, 2}, {0, 1, 2}}};
int (*p)[3][3];//其实这里就是定义了首行指针的类型/宽度,就是p+1的单位,不然输入无法对其数据
p = A;
for(i = 0; i < 3; i++) {
for(j = 0; j < 3; j++) {
for(k = 0; k < 3; k++)
printf("%2d", *(*(*(p + i) + j)) + k);
printf("\n");
}
printf("\n");
}
return 0;
}
然后我们引入函数
#include <stdio.h>
void subfun(int n, int (*subargs)[3][3]) {
int i,j,k;
for (i = 0; i < n; i++) {
for (j = 0; j < n; j++) {
for (k = 0; k < n; k++) {
printf("subargs[%d][%d][%d] = %d ", i, j, k, subargs[i][j][k]);
}
printf("\n");
}
}
}
int main() {
int args[3][3][3] = {{ {0, 1, 2}, {0, 1, 2}, {0, 1, 2} }, {{0, 1, 2}, {0, 1, 2}, {0, 1, 2} }, {{0, 1, 2}, {0, 1, 2}, {0, 1, 2} } };
subfun(3, args);
return 0;
}
同样,这里放一个字符型的数组作为补充个
#include <stdio.h>
void subfun(int n, char (*subargs)[3][12]) {//这里的单位指的是这个首行指针的类型/宽度是 3行12列(单位)
int i,j;
for (i = 0; i < n; i++) {
for (j = 0; j < n; j++) { //为什么这里指循环2遍,而上面的循环了3遍,因为%s,就是输入一个字符串的,即一个字符型数组
printf("subargs[%d][%d] = %s\n", i, j, subargs[i][j]);
// printf("subargs[%d][%d] = %c\n", i, j, subargs[i][j][k]);//如果再循环一遍的话,可以输出helloword中的每个字符
}
}
}
int main() {
char args[3][3][12] = {{ "hello word1", "hello word2", "hello word3"}, {"hello word4", "hello word5", "hello word6"}, {"hello word7", "hello word8", "hello word9"}};
subfun(3, args);
return 0;
}
这里看到某大佬
的一篇文章,受到启发,这里放上原作者的链接!