2.3使用3个一维数组,模拟实现一个二维数组
定义了三个整型数组arr1、arr2和arr3,分别存储了一组连续的整数。然后定义了一个整型指针数组arr,它包含了arr1、arr2和arr3三个数组的首元素地址。
for循环用于遍历arr数组中的每一个元素,即每个数组的首地址,然后在内层循环中,遍历了每个数组中的所有元素,并使用指针算术运算访问了每个元素的值并打印输出。
#include<stdio.h> int main() { int arr1[] = { 1,2,3,4,5}; int arr2[] = { 2,3,4,5,6}; int arr3[] = { 3,4,5,6,7}; int* arr[] = { arr1,arr2,arr3 }; int i = 0; for (i = 0; i <3 ; i++) { int j = 0; for (j = 0; j < 5; j++) { //printf("%d ", arr[i][j]); printf("%d ", *(arr[i] + j)); printf("%d ", *(*(arr+i) + j)); } printf("\n"); } return 0; }
*(*(arr+i) + j) == *(arr[i] + j) == arr[i][j] 三种写法等价
首元素地址加偏移量找到下标是这个元素的地址了,再解引用就找到此元素。
执行:
按自己的理解再写一个
图解:
用一个数组名表示首元素地址,把a,b,c放到arr中说明是把首元素地址存进去了,因为是个整型地址,所以是个整型指针,所以是int*arr[3]每个元素的类型是int*,本质是用一个指针数组,来管理3个int数组
#include<stdio.h> int main() { int a[] = { 1,2,3,4 }; int b[] = { 2,3,4,5 }; int c[] = { 3,4,5,6 }; int* arr[3] = { a,b,c }; int i = 0; for (i = 0; i < 3; i++) { int j = 0; for (j = 0; j < 4; j++) { printf("%d ", arr[i][j]); } printf("\n"); } return 0; }
执行:
arr[i][j]可写成*(arr[i]+j),一个整型指针(地址)加j向后偏移j个元素
2.4例题:
int main()
{
int* arr[3][3] = { 1 };
int p = **arr;
printf("%p", p);
}
3.数组指针
3.1 数组指针的定义
数组指针是指针?还是数组?
答案是:指针。
我们已经熟悉:
整形指针: int * pint; 能够指向整形数据的指针。
浮点型指针: float * pf; 能够指向浮点型数据的指针。
那数组指针应该是:能够指向数组的指针。
类比:
整型指针 -- 指向整型的指针int a = 10;
int* p = &a;
字符指针-指向字符的指针char ch = 'w';
char* pc = &ch;
数组指针–指向数组的指针
int arr[10] ;//数组
int (*pa)[10] = &arr;//取出的是数组的地址
char arr[10] -- char (*pc)[10] = &arr;
int*arr[5];-- int* (*pc ) [5] =&arr
下面代码哪个是数组指针?
int *p1[10];
int ( * p2 )[ 10 ];
//p1, p2 分别是什么?
答:int (*p2)[10];
p先和*结合,说明p是一个指针变量,然后指着指向的是一个大小为10个整型的数组。所以p是一个 指针,指向一个数组,叫数组指针,[]的优先级要高于*号的,所以必须加上()来保证p先和*结合。
易错点:
int main() { int a = 10; int* pa = &a;//1 int arr[10] = { 1,2,3,4,5,6,7,8,9,10}; int(*p)[10] = &arr;//2 int(*p2)[10] = arr;//3 }
对上述代码总结:
1.*说明pa是指针,int说明pa指向的a变量是整型
2.&arr取出的是数组的地址,只有数组的地址才需要数组来接收,类型是int[10]
3.arr是不能放进左边指针(类型是int[10])里面的,arr本身的类型是int*
3.2arr 和 &arr 有什么区别?
数组名绝大部分情况下是数组首元素的地址
但是有2个例外:
1. sizeof(数组名) - sizeof内部单独放一个数组名的时候,数组名表示的整个数组,计算得到的是数组的总大小
2. &arr - 这里的数组名表示整个数组,取出的是整个数组的地址,从地址值的角度来讲和数组首元素的地址是一样的,但是意义不一样
#include<stdio.h> int main() { int arr[10] = { 0 }; //printf("%d\n", sizeof(arr)); printf("%p\n", arr);//int * printf("%p\n", arr+1);//4 printf("%p\n", &arr[0]);//int* printf("%p\n", &arr[0]+1);//4 printf("%p\n", &arr);//int(*)[10] printf("%p\n", &arr+1);//40 return 0; }
上图可看出arr==&arr[0](地址值相等),也说明指针类型决定了指针+1到底加的是几,即为数组名是数组首元素地址,而&arr是整个数组的起始地址
我们已知的两种访问数组的方式:
int main() { int arr[10] = { 1,2,3,4,5,6,7,8,9,10 }; int sz = sizeof(arr) / sizeof(arr[0]); int i = 0; //使用指针来访问 int* p = arr; for (i = 0; i < sz; i++) { printf("%d ", *(p + i)); } printf("\n"); //下标的形式访问数组 for (i = 0; i < sz; i++) { printf("%d ", arr[i]); } return 0; }
3.3 数组指针的使用
使用数组指针访问并打印一维数组
理解:p == &arr,*p == *&arr,*p == arr
int main() { int arr[10] = { 1,2,3,4,5,6,7,8,9,10 }; int sz = sizeof(arr) / sizeof(arr[0]); int (* p)[10] = &arr;//注意这个地方不是arr,&arr是整个数组的地址,所以要用数组指针接收 int i = 0; //p --- &arr //*p --- *&arr //*p --- arr //虽然对,但是不推荐 for (i = 0; i < sz; i++) { printf("%d ", (*p)[i]); } printf("\n"); //虽然对,但是不推荐 for (i = 0; i < sz; i++) { printf("%d ", *((*p) + i)); } return 0; }
(*p)+i的意思:下标为i的元素的地址,*((*p)+i)得到下标为i的元素。
以上两种写法均正确,但是不推荐。
在自定义函数内打印二维数组
二维数组接收
打印二维数组传参的时候可以形参用二维数组接收。
void print1(int arr[3][5], int r, int c) { int i = 0; for (i = 0; i < 3; i++) { int j = 0; for (j = 0; j < 5; j++) { printf("%d ", arr[i][j]); } printf("\n"); } } int main() { int arr[3][5] = {1,2,3,4,5, 2,3,4,5,6, 3,4,5,6,7}; /*二维数组的数组名,也表示首元素的地址 二维数组的首元素是第一行 首元素的地址就是第一行的地址,是一个一维数组的地址*/ // print1(arr, 3, 5); return 0; }
数组指针接收
也可以用数组指针接收,并遍历数组
二维数组的数组名,也表示首元素的地址
二维数组的首元素是第一行
首元素的地址就是第一行的地址,是一个一维数组的地址
void print(int(*arr)[5], int r, int c) { int i = 0; for (i = 0; i < 3; i++) { int j = 0; for (j = 0; j < 5; j++) { //printf("%d ", *(*(arr + i) + j)); printf("%d ", arr[i][j]); //printf("%d ", (*(arr + i))[j]); } printf("\n"); } } int main() { int arr[3][5] = {1,2,3,4,5, 2,3,4,5,6, 3,4,5,6,7}; /*二维数组的数组名,也表示首元素的地址 二维数组的首元素是第一行 首元素的地址就是第一行的地址,是一个一维数组的地址*/ // print1(arr, 3, 5); return 0; }
*(*(arr + i) + j)--> arr 是二维数组的数组名,数组名是数组首元素地址,也就是二维数组的首元素第一行的地址,arr+i就是二维数组第 i 行地址,*(arr+i)得到二维数组第i行,也就是第i行的数组名,也就是arr[i],arr[i]数组名又相当于数组首元素地址,也就是第i行第一个元素的地址,即为&arr[i][0]
也就是说, *(arr+i) == arr[i] == &arr[i][0]
也可以这样写: printf("%d ", (*(arr + i))[j]);
(*(arr+i))[j])意思是访问完第i行之后再访问[j]列,可以写成arr[i][j]
易错点:
1.int arr[5];整型数组,数组有10个元素
2.int *parr1[10];指针数组,数组10个元素,每个元素都是int*类型的
3.int(*parr2)[10];parr2是数组指针,该指针指向一个数组,数组是10个元素,每个元素是int类型
4.int (*p)[10]=arr,如何强转?(int(*)[10])arr
5.int* p=&num,对p解引用的类型int
6.int(*p)[10]=&arr,&arr赋给p,p--arr,*p=*&arr=arr
7.int(*parr3[10])[5]
parr3是数组,数组中存放的指针,该指针指向的又是数组。
parr3和[10]结合说明parr3是数组,大小存10个元素,每个元素的类型是int(*)[5],
可以理解为指针数组里面存放的是数组指针。