前言
🎈大家好,我是何小侠🎈
🍃大家可以叫我小何或者小侠🍃
💐希望能通过写博客加深自己对于学习内容的理解💐
🌸也能帮助更多人理解和学习🌸
兰生幽谷,不为莫服而不芳。舟在江海,不为莫乘而不浮。君子行义,不为莫知而止休。— 出自《淮南子·说山训》
解释:兰花生长在无人的山谷,不会因为没人佩戴而不散发芳香;船在江河湖海上,不会因为没有人乘坐而不浮在水上;君子行使自己的道义,不因没有人理解而停止。
这篇博客我们将会深入的理解指针和学习指针的用法
字符指针⁉️
- 字符指针最基本的用法如下。
int main() { char ch = 'w'; char* p = &ch; *p = 'a'; return 0; }
这确实是一个本基本的代码我们都会使用。
我们看看在内存
中是怎么变化的。
首先将’ w ‘的对应的ASCII
值存在ch中,然后用 p这个指针变量存放ch的地址,通过给解引用p赋值,成功改变ch中的字符由’ w ‘变成’ a ’
当然这都是16进制的表示方法,自己算一算便可知道。
- 字符指针的第二种用法。
int main() { char* p = "abcdefg"; printf(p); printf("\n%c", *p); return 0; }
我们看第一行代码时要注意,这里是将一个常量字符串"abcdefg"的首地址
放在了p中,我们通过这个首地址找到这个常量字符串。并不是把这个字符串放在p里面。
那么打印的结果就应该是 :
abcdefg
a
没错确实是这样。
那我们是否能够改变*p的内容呢?
int main() { char* p = "abcdefg"; printf(p); printf("\n%c", *p); *p = 'w'; printf("\n%c", *p); return 0; }
其实 * p = ‘w’;这种写法是不对的,为什么呢?因为我们的"abcdefg"是一个常量字符串常量字符串是不能够修改的!!
指针数组🐽
我们都知道,整型数组是存放整型的数组,字符数组是存放字符的数组。
那么指针数组就比较明显,肯定是存放指针的数组。
- 那么它的一般形式是什么呢?
int main() { char* pc = "abcde"; char* p1 = pc; char* p2 = pc+1; char* p3 = pc + 2; char* p4 = pc + 3; char* p5 = pc + 4; char* p[5] = { p1,p2,p3,p4,p5 }; for (int i = 0; i < 5; i++) { printf("%c\n", *p[i]);//[]先结合,因为优先级最高。 } return 0; }
我们为了与上面的字符指针对应,例子举的相似一点。
我们知道p1 ~ p5 存储的就是对应的abcde的地址,而类型决定应该放什么元素,而这里指针数组是字符指针数组,所以类型就是char *.
我们想一下,在我们传参的时候,arr数组名就是用char * 来接受的,为什么呢?就是因为实参就是地址,char * 类型也就是需要地址来作为数组元素,那么这里的初始化就应该很好理解了。
- 但是我们一般会这样来使用它
int main() { int arr1[] = { 1,2,3,4,5 }; int arr2[] = { 2,3,4,5,6 }; int arr3[] = { 3,4,5,6,7 }; int* p[3] = { arr1,arr2,arr3 }; int i = 0; int j = 0; for (i = 0; i < 3; i++) { for (j = 0; j < 5; j++) { printf("%d", *(*(p + i) + j)); } printf("\n"); } return 0; }
我们这里要讲的是最后的输出部分
。
printf("%d", *(*(p + i) + j));
这里为什么要这样写呢?还有没有其他写法?
我们知道arr[3] = *(arr +3);
为什么呢? arr[3]
其实是下标为3的元素,那么
arr+3
就是下标为3的元素的地址,我解引用不就是这个元素吗?
这里也差不多。
我们可以这样理解,先看成是一个一维数组,那么*(p+i)
不就是拿到arr1,或者arr2,arr3
的地址吗?加入我们拿到arr1
的地址。
那么*(p )
就是arr1这个数组的首地址, * (arr1+j)
也就很明显了。
那么我们换一种写法呢?
这样写:*( p[ i ]+j )
这样写:p[ i ][ j ]
为什么可以当作二维数组看呢?因为二维数组其实也可以理解为多个一维数组的拼接。但是我觉得还是我上面描述的要更清晰一些。
这样写: (*(p + i))[ j]
数组指针🦑
我们上面讲的是指针数组,是数组。因为最后是数组这个名词,而数组指针是指针,是指向数组的指针。
- 区分指针数组和数组指针
int *p1[10]; int (*p2)[10]; //p1, p2分别是什么?
p1是指针数组,为什么呢?我们讲过,p1先与[ ]结合确认是一个数组,再与结合说明是指针数组。
而p2就是我们要讲到的数组指针,用()让 p2 优先结合说明是一个指针,然后确认指针类型,是int [ 10 ],也就是数组指针
&数组名与数组名的区别
这真的是一个非常重要的知识点。
我们先来看一段代码
int main() { int arr[] = { 1,2,3,4,5,6 }; printf("%p\n", arr); printf("%p\n", arr+1); printf("%p\n", &arr); printf("%p\n", &arr+1); return 0; }
我们用计算机一算便知。
这里为什么是这样呢?
- 实际上&arr,取出的是整个数组的地址,整个数组的地址+1也就应该跳过一个数组。
- arr是首元素地址,+1当然只会跳过一个元素。
大家觉得这和我们的指针是不是有点类似呢?
我们知道 arr传参我们可以用 int * arr这个指指来接受,这个我们已经是老生常谈了,上面也解释过了。
那么&arr用什么来接收?
当然用数组指针
。
整型指针变量存储的是一个整型元素的地址!
那么数组指针变量应该存储的是数组的首元素地址,由于类型是数组指针,+1当然就会跳过一个数组的大小。
那么我们就可以得出结论,&arr的数组名就是 int (*p)[ ]。
数组指针的用法
int main() { int arr[10] = {1,2,3,4,5,6,7,8,9,0}; int (*p)[10] = &arr;//把数组arr的地址赋值给数组指针变量p //但是我们一般很少这样写代码 return 0; }
我们一般这么写。
void print(int(*p)[5],int i ,int j ) { for (i = 0; i < 3; i++) { for (j = 0; j < 5; j++) { printf("%d ",(*(p+i))[j]);//不要写成 *p+i, //因为*比加号的优先级要高 //printf("%d ", *(*(p + i) + j)); } printf("\n"); } } int main() { int arr[][5] = { {1 ,3 ,4} ,{2,3,4,5,6}, {1,2,3,4} }; print(arr,3,5); return 0; }
可能有点跳跃不过没有关系,为什么二维数组传首地址能够用数组指针接收呢?
我们知道二维数组的数组名代表第一行的地址,第一行就相当于是一个一维数组,所以说可以。
学了指针数组和数组指针我们来一起回顾并看看下面代码的意思 :
int arr[5]; int *parr1[10]; int (*parr2)[10]; int (*parr3[10])[5];
- 是一个普通的整型数组,5个元素,每个元素是int
- 是一个指针数组,存放的是整型指针,每个元素是int *
- 是一个数组指针,指向的是整型数组,数组每个元素是int
- 是一个指针数组,指针的类型是数组指针,也就是说是一个存放指向数组的指针的数组
总结🍊
这篇博客值得我们学习了指针进化的第一部分,后续还有几部分我也会陆续发出来,请大家期待。
最后如果这篇博客有帮助到你,欢迎点赞关注加收藏
如果本文有任何错误或者有疑点欢迎在评论区评论