2024-07-22 笔记 - 4
2024-07-23 补充笔记
【指针数组】
顾名思义,就是用来存储地址的数组,所有的元素全中存储的全部都是地址,每一个元素都可以理解为是一个指针变量。
char* arr[10];
int* arr2[10];
char** arr3[5];
① 举例,多种访问方式
int arr[5] = {1, 2, 3, 4, 5}, arr2[5] = {6, 7, 8, 9, 10}, arr3[5] = {11, 12, 13, 14, 15};
int* a[3] = { arr, arr2, arr3 };
//这个指针变量中每一个元素的类型都是int*;
//可以这样访问
for(int i; i < 3; i ++)
{
for(int j; j < 5; j ++)
printf("%d ", *(a[i] + j) );
//Or: printf("%d ", *(*(a + i) + j) );
//Or: printf("%d ", *(*(a + i) + j) );
//Or: printf("%d ", a[i][j] );
printf("\n");
}
//结果为:
1 2 3 4 5
6 7 8 9 10
11 12 13 14 15
拓:其实[]可以当做偏移量,就如上面的a[i][j]
下面再举例子:
int arr[5] = {1, 2, 3, 4, 5};
int* p = arr;
访问方式(两种):比如访问a[0]
p[0];
*( p + 0 );
② char** ch[5]是什么意思??
声明了一个指针数组,该数组包含5个元素,每个元素都是一个指向char
类型的指针。也可以说是,它是一个可以存储5个字符串的数组。
简单来说,就是存储字符串首地址的数组。
char arr[5], arr2[5], arr3[5];//声明字符串数组
char* parr[3] = { arr, arr2, ar3 };//声明指针数组
//或者
char* ch[5]; // 声明指针数组
// 分配内存并初始化
ch[0] = "Hello";
ch[1] = "World";
ch[2] = "This";
ch[3] = "Is";
ch[4] = "C";
【数组指针】
- 整型指针 - 指向整型的指针 - 存放整型的地址
- 字符指针 - 指向字符的指针 - 存放字符的地址
- 数组指针 - 指向数组的指针 - 存放数组的地址
数组指针简单来说就是指向【整个一维数组】的指针。
一定要记住:下面会用到,很关键,【数组指针】p 存放的是 【整个】【一维数组】的地址!!!
1)区分 【指针数组】 和 【数组指针】
指针数组 - int* parr1[10];
数组指针 - int (*parr2)[10];
- 指针数组 - 存放指针的数组,存放地址的数组
- 数组指针 - 指向一个和数组的指针
2) 疑惑?int*p, arr[10]; p = arr
与int (*pa)[10], arr[10];pa = &arr
性质上面来说不是一样的吗???
是不一样的。
注意中括号中的数字是不能去掉的,必须添加,否则报错。
int arr[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
int* p;
p = arr;//p 指向的是arr首元素a[0]的地址
因为 arr 本质上还是首元素的地址,而 p 接收到的实际上是元素地址,并不是所谓的整个数组的地址。(首元素的地址 和 整个数组的地址 虽然说表示的地址的含义不太一样,但是打印出来却是一样的)
int arr[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
int (*pa)[10];
pa = &arr; // pa 现在指向 arr 整个数组的地址
而 &arr 其实表示的是整个数组的地址,然后 p 接收到的实际上是 arr整个数组的地址,而非首元素的地址。(首元素的地址 和 整个数组的地址 虽然说表示的地址的含义不太一样,但是打印出来却是一样的)
3) 数组名表示的不同的含义???(三种)
-
数组名 - 首元素地址(一般情况下,表示的都是首元素的地址)
-
数组名 - sizeof,单独放一个数组名,计算的是整个数组的的大小 (特殊)
如果写的是arr+0,+1这种不算,这不算数组名
-
数组名 - &数组名,数组名表示的依然是整个数组,所以&数组名取出的是整个数组的地址 (特殊)
下方例子从值的角度去讲,是一样的,打印的地址是一样的。
int arr[10];
printf("%p\n", arr);//元素的地址
printf("%p\n", &arr[0]);//元素的地址
printf("%p\n", &arr);//整个数组的地址
4) &arr
再次强调一遍:&arr - 表示的是整个数组的地址
int arr[10];
&arr - 在这表示的是整个数组的地址
那怎么存 &arr 呢???
这时候就会用到【数组指针】了,下面是用法
//注:数组和数组指针中的中括号中的数字必须是相同的。
int arr[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
int (*pa)[10] = &arr;
//pa = &arr;
而至于(*pa)[10]的意思就是,定义了一个指针,这个指针指向一个长度为 10 的存放int的数组。
而这个数组指针pa的类型是是什么呢???
pa的类型就是**int (*)[10]
就是这个数组指针的类型。**
5) 【数组指针】指向【指针数组】
int* arr[10] = {0};
int* (*p)[5] = &arr;
解释:【数组指针p】 指向一个**【指针数组】,而这个指针数组的类型就是int*
,所以表示指针数组的时候,类型不能写为int**,必须要写为int*
6) 数组指针的【错误】用法与【正确】用法??*pa
是什么???
错误用法 - 用在【一维数组】
*pa就是数组首元素的地址,下面是解释。
首先,pa是指向整个数组的,指向的是arr,存放的地址是&arr
其次,*pa 就是 *(&arr),意思就是arr数组名,所以,*pa意思就相当于数组名,数组名是什么呢? - 数组名是数组首地址。或者我一个老师这么教我,这个叫做降级,将行地址降级成为元素地址,就是首地址了。
注:这样写只是知道这样是正确的,并且有这个写法就可以了,但是建议不要这么写,因为很麻烦,很别扭,并不推荐。
数组指针不是这么用的。
正确用法 - 用在【二维数组】
下面更一步去阐述如何使用数组指针
在一维数组中,如果想代表【整个一维数组】的地址,就要写成 &arr
而在二维数组中,想表示【一维数组】的地址(一行的地址)则不需要这样,arr就是表示第0行【整个一维数组】的地址,arr ± 数字,是加减的整个数组,比如,arr表示的额就是arr[0],arr + 1 –> 表示的是加一个数组(行+1) == &arr[1]
下面演示的是直接 解引用 二维数组的 地址 去访问元素:
int arr[4][4] = { 1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8 };
for (int i; i < 4; i++)
{
for (int j; j < 4; j++)
printf("%d", *(*(arr + i) + j) );//这样也可以正确打印,效果和arr[i][j]一样
printf("\n");
}
//逐个拆分解释
arr - 第0行整个一维数组的【地址】(行地址) == arr[0]
*arr - 第0行整个一维数组的【首地址】(首元素的地址) - 解引用了第0行一维数组的地址 == &arr[0][0]
*(*arr) - 第0行,第0个元素,这就不是地址了,而是指的元素 == arr[0][0]
注:*(*(arr + i) + j) == *(arr[i] + j) == arr[i][j]
下面进一步用【数组指针】去访问二维数组中的元素:
要记住一点,数组指针(*p)[10],p存放的一定要是整个数组的地址。所以在一维数组的时候要存入&arr,而在二维数组的时候就是arr了
int arr[4][4] = { 1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8 };
int (*p)[4] = arr;//在这里,p中存储的就是&arr[0], 即整个第0行数组的地址
//Or: p = arr;
for (int i; i < 4; i++)
{
for (int j; j < 4; j++)
printf("%d", *(*(p + i) + j) );//这样也可以正确打印,效果和arr[i][j]一样
printf("\n");
}
下面这几种都可以表示为元素
//*(*(p + i) + j) == *(p[i] + j) == p[i][j]
注:*(*(p + i) + j) == *(p[i] + j) == p[i][j]
7) 二级指针变量(***)
【二级指针变量】就是存放一级指针变量地址的指针变量。
如下就是,
p1 - 一级指针 - 存放的是变量的地址。
p2 - 二级指针 - 存放的是一级指针的地址,所以要写两个星号。
eg:
char ch = 'a';
char* p1 = &ch;
char** p2 = &p1;
8) int (*parr[3])[5]
是什么???
可以理解为存放多个数组指针的数组,或者存放多个数组的地址的数组。
之前我们知道,数组指针可以存放一个数组的地址,那么我们可不可以有一个数组专门去存放好几个数组的地址呢???
那么就用到了
int (*parr[3])[5]
,
int(*)[5]
就是表示的可以存放元素类型为int并且长度为5的【整个一维数组】的地址。
- 如果写成
int (*p)[5]
,就表示了存放的是【1个】【一维数组的地址】- 如果写成
int (*p[3])[5]
,就表示了存放的是【3个】【一维数组】的地址
下面是具体用法:
用于【一维数组】时:
int arr1[5], arr2[5], arr3[5];
int (*parr[3])[5] = {&arr1, &arr2, &arr3};
如何访问呢???假设每个数组中已经存入了相应的数值,打印每个数组中的元素
for(int i = 0; i < 3; i++)
{
printf("第%d个数组:\n", i + 1);
for(int j = 0; j < 5; j++)
{
printf( "%d, ", *(*parr[i] + j) );
}
printtf("\n");
}
用于【二维数组】时:
int arr1[5][5], arr2[5][5], arr3[5][5];
int (*parr[3])[5] = {arr1, arr2, arr3};
如何访问呢???假设每个数组中已经存入了相应的数值,打印每个数组中的元素
for(int i = 0; i < 3; i++)
{
printf("第%d个二维数组数组:\n", i + 1);
for(int j = 0; j < 5; j++)
{
for(int k = 0; k < 5; k ++)
printf( "%d, ", *(*(parr[i] + j) + k) );
}
printtf("\n\n\n");
}
9) 【指针数组】 – 总结
int arr[4][4] = { 1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8 };
int (*p)[4] = arr;//在这里,p中存储的就是&arr[0], 即整个第0行数组的地址
//Or: p = arr;
for (int i; i < 4; i++)
{
for (int j; j < 4; j++)
printf("%d", *(*(p + i) + j) );//这样也可以正确打印,效果和arr[i][j]一样
printf("\n");
}
下面这几种都可以表示为元素
//*(*(p + i) + j) == *(p[i] + j) == p[i][j]
-
重要:【数组指针】 p 存放的是【整个一维数组】的地址,一定谨记:存放的不是首元素的地址!!!!
-
S数组指针的类型如何表示呢?? –>
int (*)[4]
-
二维数组中,下面举例去理解
arr == &arr[0]
- 第 0 行整个数组的地址*arr == &arr[0][0]
- 首元素的地址**arr == arr[0][0]
- 首元素arr + i == &arr[i]
- 第 i 行整个数组的地址*(arr + i) == &arr[i][0]
- 第 i 行 首元素的地址*(arr + i) + j == &arr[i][j]
- 第 i 行 第 j 列的元素的地址*(*(arr + i) + j) == arr[0][0]
- 第 i 行 第 j 列的元素 -
int (*p)[4] 如何用呢???怎么给数组指针 p 赋值呢???
-
一维数组 p = &arr(整个一维数组的地址)
-
二维数组 p = arr(第0行整个一维数组的地址)
这样就可以让p当做arr直接去访问二维数组了。
-
-
p ± 数字 到底 ± 偏移的量是多少???
拿指arr为例吧,理解数组指针的时候,将p当做arr即可
arr + 1: 因为arr代表的意思是第 0 行的【整个一维数组】的地址,所以 +1 所代表的就是偏移了整个数组,就偏移到了第 1 行 的【整个一维数组】的地址 *(arr + i) + 1: 意思就是 第 i 行的数组的 第 j 个元素 的地址,可以理解为就是&arr[i][j] *(*(arr + i) + 1): 解引用*(arr + i) + 1,得出来就是arr[i][1]元素
-
int (*parr[3])[5]
存放【整个数组的地址】的数组,或者说是存放【数组指针】的数组//存放整个一维数组的地址 int arr1[5], arr2[5], arr3[5]; int (*parr[3])[5] = {&arr1, &arr2, &arr3};
//存放整个二维数组的地址 int arr1[5][5], arr2[5][5], arr3[5][5]; int (*parr[3])[5] = {arr1, arr2, arr3};