✨✨ 欢送大家来到贝蒂大讲堂✨✨
养成好习惯,先赞后看哦~
所属专栏:C语言学习
贝蒂的主页:Betty‘s blog
1. 引言
后面给大家介绍了一些指针的基本概念,明天就让咱们持续深刻指针的世界,和贝蒂一起战胜指针大魔王吧
2. 二级指针
指针变量也是变量,是变量就有地址,那咱们就把寄存指针变量地址的指针称为二级指针。
可能了解起来有点绕,咱们能够通过上面示意图演示一下
代码如下:
int a = 10; int* pa = &a;//一级指针,寄存a的地址 int** ppa = &a;//二级指针,寄存指针变量p的地址
- 不能间接把&&a赋值给ppa哦,因为&&在C语言中是且的意思”
(1)对ppa解援用,找到pa,也就是说*ppa==pa
(2)对pa解援用,找到a,也就是说**ppa==a
int* b = *ppa;//找到a的地址 int c = **ppa;//找到a
- 顺次内推咱们能够衍生出三级指针,四级指针。
3. 数组与指针的关系
3.1 数组名的了解
咱们在后面学习数组时就明确,数组名是首元素地址,然而解说的不够深刻,明天就让咱们深刻理解一下吧~
首先让咱们察看一下如下代码
#include <stdio.h>int main(){ int arr[10] = { 1,2,3,4,5,6,7,8,9,10 }; //&arr[0],arr,&arr的区别 printf("&arr[0] = %p\n", &arr[0]);//首元素地址 printf("arr = %p\n", arr);//一维数组数组名 printf("&arr = %p\n", &arr);//对整个数组取地址 return 0;}
从后果来说&arr[0],arr,&arr到底有什么区别呢?
让咱们再看看上面这段代码
#include <stdio.h>int main(){ int arr[10] = { 1,2,3,4,5,6,7,8,9,10 }; printf("&arr[0] = %p\n", &arr[0]); printf("&arr[0]+1 = %p\n", &arr[0] + 1); printf("arr = %p\n", arr); printf("arr+1 = %p\n", arr + 1); printf("&arr = %p\n", &arr); printf("&arr+1 = %p\n", &arr + 1); return 0;}
输入后果:
- &arr[0]与arr+1都是跳过4个字节,相当于跳过1个整型元素。
&arr+1跳过40个字节,相当于10个整型,也就是整个数组。
总结:arr与&arr[0]都是首元素地址,指向数组第一个元素。&arr以首元素地址示意,然而指向的是整个数组。
3.2 sizeof与数组名
咱们晓得sizeof实际上是获取了数据在内存中所占用的存储空间,单位是字节。
让咱们看看上面这段代码吧
#include <stdio.h>int main(){ int arr[10] = { 1,2,3,4,5,6,7,8,9,10 }; printf("%d\n", sizeof(arr));//计算大小 return 0;}
输入后果:40
不晓得大家有没有纳闷?如果数组名是首元素地址的话,咱们晓得在32位机器上大小为4,在64位机器上大小为8。那为什么是40呢?
其实数组名就是数组⾸元素(第⼀个元素)的地址,然而有两个例外:
• sizeof(数组名),sizeof中独自放数组名,这⾥的数组名表⽰整个数组,计算的是整个数组的⼤⼩,单位是字节
• &数组名,这⾥的数组名表⽰整个数组,取出的是整个数组的地址(整个数组的地址和数组⾸元素的地址是有区别的)
3.3 数组与指针等价关系
假如有一个一维数组和一个二维数组
int arr[5]={1,2,3,4,5};int arr[3][3]={{1,2,3},{4,5,6},{7,8,9}}
咱们要拜访它的每个元素,有哪些办法呢~
- 数组拜访
int arr1[5] = { 1,2,3,4,5 }; for (int i = 0; i < 5; i++) { printf("%d ", arr1[i]); }
int arr2[3][3] = { {1,2,3},{4,5,6},{7,8,9} };for (int i = 0; i < 3; i++){ for (int j = 0; j < 3; j++) { printf("%d ", arr2[i][j]); } printf("\n");}
- 指针拜访
for (int i = 0; i < 5; i++){ printf("%d ", *(arr1+i));}
for (int i = 0; i < 3; i++){ for (int j = 0; j < 3; j++) { printf("%d ", *(*(arr2 + i) + j)); }}
通过对下面代码的察看,咱们能够总结如下法则
- arr[i]与*(arr+i)等价。
- arri与((arr+i)+j)等价。
3.4 指针数组
(1) 指针数组的概念
指针数组顾名思义就是寄存指针的数组 ,数组中每个元素都是指针,寄存的都是地址。
int*parr1[5];//寄存五个整型指针变量char*parr2[5];//寄存五个字符指针变量float*parr3[5];//寄存五个浮点数指针变量
代码示例
int arr1[] = { 1,2,3 }; int arr2[] = { 4,5,6 }; int arr3[] = { 7,8,9 }; //将每个数组的首元素地址都存进去 int* parr[3] = { arr1,arr2,arr3 };
示意图:
(2) 指针数组的了解
int main(){ int arr1[] = { 1,2,3 }; int arr2[] = { 4,5,6 }; int arr3[] = { 7,8,9 }; int* parr[3] = { arr1,arr2,arr3 }; printf("%p\n", parr);//打印指针数组首元素地址,也就是打印寄存arr1空间的地址 printf("%p\n", parr[0]);//arr1数组首元素地址 printf("%p\n", *parr);//arr1首元素地址 printf("%d\n", **parr);//相当于对arr1首元素地址解援用,指的的是1 printf("%d\n", *parr[0]);//也相当于对arr1首元素地址解援用,为1 printf("%d\n", *parr[1]);//相当于对arr2首元素地址解援用,为4 return 0;}
输入后果:
012FFE30
012FFE6C
012FFE6C
1
1
4
(3) 模仿二维数组
通过上述咱们对指针数组的了解,咱们能够间接来模拟出二维数组。
代码如下:
int main(){ int arr1[] = { 1,2,3 }; int arr2[] = { 4,5,6 }; int arr3[] = { 7,8,9 }; //将每个数组的首元素地址都存进去 int* parr[3] = { arr1,arr2,arr3 }; int i = 0; int j = 0; for (i = 0; i < 3; i++) { for (j = 0; j < 3; j++) { printf("%d ", parr[i][j]); } printf("\n");//换行) } return 0;}
- 模仿的二维数组并不是真正的二维数组,因为二维数组在内存中是间断存储的,而模仿进去的数组内存存储并不间断
3.5 数组指针
(1) 数组指针的概念
同理,指针数组的实质是一个数组;那么数组指针的实质就是个指针,指向一个数组的指针。
int(*parr1)[5];//指向一个有五个元素的整型数组char(*parr2)[5];//指向一个有五个元素的字符数组float(*parr3)[5];//指向一个有五个元素的浮点数数组
(2) 数组指针的了解
int main(){ int arr[5] = { 1,2,3,4,5 }; int(*parr)[5] = &arr; //对数组名取地址代表整个数组的地址 printf("%p\n", parr);//整个数组的地址个别用数组首元素地址示意 printf("%p\n", parr[0]);//相当于*(parr+0)==arr,首元素地址 printf("%p\n", *parr);//首元素地址 printf("%d\n", **parr);//相当于对首元素地址解援用,指的的是1 printf("%d\n", *parr[0]);//也相当于对首元素地址解援用,为1 printf("%d\n", *parr[1]);//等价于*(*(parr+1)),parr+1跳过一个数组大小的地址,越界拜访 return 0;}
输入后果:
012FF6F0
012FF6F0
012FF6F0
1
1
-858993460(越界拜访,随机数)
示意图:
3.6 指针数组与数组指针的区别
可能有许多小伙伴区别不分明指针数组与数组指针,然而如果写成指针的数组,数组的指针,可能更好了解。接下来让咱们具体分析一下吧?
首先咱们要分明一个优先级程序:()>[]>*
- 在int*parr[]中,parr先与[]联合(数组),而parr后面申明的变量类型是int*。所以这是一个数组,数组中每个元素的类型是int*的指针,这一类咱们统称为指针数组。
- 在int(*parr)[]中,parr先与联合(指针),而后除开(parr)是一个int []的数组类型。所以这是一个指针,这个指针指向的是一个数组,这一类咱们称为数组指针。
3.7 字符串
咱们先看一下上面这段代码
char arr1[]="im betty";char arr2[]={'a','b','c','\0'};
这是一种常见的字符串的示意模式,以'\0'作为其结尾标记。
然而还有另外一种示意模式,代码如下
//const能够省略 const char* p1 = "im betty"; const char* p2 = "abc";
咱们晓得const润饰在*前,不能扭转指针变量所指向的值,所以这个字符串是不能扭转的,这种字符串咱们称为常量字符串
- 实质上是将字符串中的首元素地址寄存进指针变量。
晓得这些之后,让咱们来看一道题吧
输入什么?
#include <stdio.h>int main(){ char str1[] = "hello bit."; char str2[] = "hello bit."; const char* str3 = "hello bit."; const char* str4 = "hello bit."; if (str1 == str2) printf("str1 and str2 are same\n"); else printf("str1 and str2 are not same\n"); if (str3 == str4) printf("str3 and str4 are same\n"); else printf("str3 and str4 are not same\n"); return 0;}
输入后果:
为什么会呈现这种后果呢,那是因为这⾥str3和str4指向的是⼀个同⼀个常量字符串。C/C++会把常量字符串存储到独自的⼀个内存区域(常量区),当⼏个指针指向同⼀个字符串的时候,他们理论会指向同⼀块内存。然而⽤雷同的常量字符串去初始化不同的数组的时候就会开拓出不同的内存块,每个数组地址就会不同。所以str1和str2不同,str3和str4雷同。
3.8 数组传参
(1) 一维数组传参
咱们在之前学习函数时候就讲过一维数组传参,让咱们来温习一下吧。
代码如下
void print(int arr[])//写成数组的模式{ int i = 0; for (i = 0; i < 10; i++) { printf("%d ", arr[i]); }}int main(){ int arr[10] = { 1,2,3,4,5,6,7,8,9,10 }; print(arr);//将数组传递给print函数 return 0;}
咱们传参是传的数组名,咱们晓得数组名是首元素的地址,既然是地址,天然就能用指针来承受,所以就有了另外一种写法。
void print(int*p)//用指针来接管{ int i = 0; for (i = 0; i < 10; i++) { printf("%d ",*(p+i)); }}
(2) 二维数组的传参
先让咱们看看个别二维数组是如何传参的吧
void print(int arr[][3])//行能够省略,列不能够{ int i = 0; for (i = 0; i < 3; i++) { int j = 0; for (j = 0; j < 3; j++) { printf("%d ", arr[i][j]); } printf("\n"); }}int main(){ int arr[3][3] = {{1, 2, 3}, { 4,5,6 }, { 7,8,9 }}; print(arr);//将数组传递给print函数 return 0;}
那么指针接管如何写呢,还是int*p吗,咱们晓得二维数组能够看成把每一行当做一个元素的一维数组,数组名首元素地址天然是第一行元素的地址,所以要用数组指针来接管哦~
代码如下:
void print(int(*p)[3])//明确元素个数{ int i = 0; for (i = 0; i < 3; i++) { int j = 0; for (j = 0; j < 3; j++) { printf("%d ",*(*(p+i)+j)); } printf("\n"); }}