本文是对指针大部分内容的初步理解,帮助更好地学习指针。
目录
一.指针是什么
二.1.指针变量
1.整形变量是存放整形的变量,字符变量是存放字符的变量,那么指针变量就是存放指针,也就是存放地址的变量
2.那么,怎么找到一个内容的地址呢?可以使用&取地址操作符
32位环境中,000000AEE9F2FA74就是a所在的地址
3.如何用指针变量存放地址呢?
int表示存放的地址所指向的内容的类型是整形,*p表示p是一个指针变量,专门用来存放指针的。
指针变量的一般创建形式就是 (地址所指向内容的数据类型) *(指针变量名)=(地址)
二.2.不同类型的指针变量的差别
得到地址后,计算机读取所需内容还需要所读取的长度,单位是字节,比如说
创建一个整形变量 初始化为10,在调试过程中,输入a的地址,发现a存放在内存中的是
0a 00 00 00(16进制中a为10,b为11.......),因为a的数据类型为int,所以a的内容存放需要四个字节,那么读取a时就需要一次读取4个字节,才能将a的内容读取完全,用·int*存放a的地址,因为int的大小是四个字节,在解引用时,就会一次读取四个字节,保证全部读取,同理,用char*存放时,一次只会读取一个字节(一个字节是8个bit位,也就是8个二进制数字,把2进制数字用16进制数字来表示时,一个16进制数字可以表示4个2进制数字,所以一个字节会用2个16进制数字来表达)
可以看到,a在内存中存放的内容为39 30 00 00,用int*来存放指针时,一次读取四个字节,打印出的内容和a本身一样,但是用char*存放指针时,一次只能读取一个字节,只读取了39,换算成十进制也就是57,所以,不同类型的指针变量的差别就是读取内容的长度和地址加减的长度
(解引用和地址加减还没讲到,为了内容连贯先拿出来提及一下,后续会讲)
二.3.如何解引用指针变量
有了地址之后,我们可以通过 * ( 解引用)地址 来找到地址中所存放的内容
二.4.指针运算
1.指针加减整数
上面我们提到的不同类型的指针变量的差别就是读取内容的长度和地址加减的长度,这里我们运用数组来举例
int*类型的指针每次加减是 加减数*4 个字节 (也就是int类型的大小)
char*类型的指针每次加减是 加减数*1 个字节
在数组中,数组的内容是连续存放的地址由低到高,所以,可以利用*(指针+数组下标)的方式读取数组的内容
2.指针减指针
p为数组首元素地址,p2为数组最后一个元素的地址,p需要加4次1才能到达p2,所以p2-p=4
也就是,用数组末元素地址-数组首元素地址=数组元素个数-1
二.5.const修饰指针
变量是可以修改的,如果把变量的地址交给⼀个指针变量,通过指针变量的也可以修改这个变量。
不用const修饰*p时,*p是可修改的,const修饰*p表示p指向的对象不可通过*p修改,所以修饰后再用*p修改就会报错
const的使用方式:
1.const修饰*p(地址指向的对象),对象不可通过*p修改,但是可以直接通过变量名修改
格式:const int * p 或 int const * p(只要保证const在*p前面即可)
2.const修饰p(地址),地址不可通过p再改变
格式:int* const p
上述中p指代地址,*p指代地址指向的对象,int为对象的数据类型
三.void*指针和野指针
2.野指针:野指针就是指针指向的位置是不可知的(随机的、不正确的、没有明确限制的)
此文章中不再详细解释野指针及void*指针
四.二级指针
可以看到p1存放的地址就是p所在的地址,这就是二级指针。
那么如何使用二级指针呢?常见的使用方法通常是二次解引用。
*
p1存放的是p的地址,*p就是读取p所在地址的内容,也就是a所在的地址
那么**p,就是解引用a所在地址的内容,也就是读取变量a了。
五.指针数组
1.概念
我们常见的有 int arr【】整形数组,char arr【】字符数组,那么指针也会有数组,由指针组成的数组就叫做指针数组
2.指针数组的创建及初始化
我们经常使用的整形数组,比如 int arr[10]
表示创建一个10个元素的数组,每个元素的数据类型都是 int 类型。
那么同理
int* p[3]就是创建了一个3个元素的数组,每个元素的数据类型都是 int*
在所示代码中,都创建了3个元素的指针数组,只是解引用的方式不同,这涉及到数组的底层逻辑,会在后续提到。
3.指针数组模拟二维数组
将3个数组的首元素地址依次放入指针数组p中,因为数组中元素都是由低到高连续存放的,所以, 地址首元素+地址下标 就可以找到数组中该下标的元素的地址,再解引用,就可以读取其内容。
读取内容时,我们使用的是p[i][j]的方法,类似于二维数组。
4.数组读取元素的底层逻辑
4.1一维数组
arr1[1]读取的是数组中的第二个元素,也就是2
arr1是数组首元素的地址,arr1+1,就是数组第二个元素的地址,那么*第二个元素的地址,也可以找到第二个元素
在底层逻辑中,arr1[i]在运行过程中就会变成*(arr+1),这就是数组读取元素的底层逻辑
所以 arr1[1]==*(arr+1)。
4.2二维数组
首先,我们要知道,二维数组就是一维数组的数组,二维素组的每个元素都是一维数组,
arr[2][3]可以看作由2个元素组成,每个元素都是由3个元素组成的数组。
比如,在二维数组中,arr[1][2]读取数组中第二行,第三个元素。
在上述代码中,底层逻辑是先 *(arr+1),arr是二维数组数组名,也就是首元素地址,等价于第一个数组的地址,
所以,+1得到第二个数组的地址,等价于&arr[1],那么第一次解引用,*和&抵消,相当于得到第二个数组的数组名,也就是第二个数组首元素地址,再次解引用就可以访问第二个数组的元素。
由此也可以得到,二维数组传参的本质就是传递首个一维数组的地址。
六.数组指针变量和函数指针变量
六.1数组指针变量
1.数组指针变量就是存放数组地址的变量
2.数组指针变量的写法 格式 :数组元素数据类型(*变量名)[元素个数]=&元素名
比如
六.2函数指针变量
1.函数也有自己的地址,函数指针变量就是存放函数地址的变量
2.函数指针变量的写法
3.函数指针变量的使用
通过函数指针调⽤指针指向的函数,在这里,print==&print,p==(*p)