1.1 什么是指针?
指针是什么?
指针理解的2个要点:
总结:指针就是地址,口语中所说的指针通常指的是指针变量
那我们就可以这样理解:
内存:
指针变量:
我们可以通过&(取地址操作符)取出变量的内存起始地址,把地址可以存放到一个变量中,这个变量就是指针变量
int main() { int a = 10; //在内存中开辟一块空间 //是向内存中的栈区空间申请4个字节的空间,这4个字节用来存放10这个数值 int* p = &a; //这里我们对变量a,取出他的地址,可以用&操作符 //a变量占用4个字节的空间,这里是将a的4个字节的第一个字节的地址 存放在p变量中,p就是一个指针变量 return 0; }
我们用图来表示
总结:
指针变量,是用来存放地址的变量(存放在指针中的值都会被当成地址处理)
这里的问题是:
经过仔细的计算和权衡,我们发现一个字节给一个对应的地址是比较合适的
对于32位的机器,假设有32根地址线,那么假设每根地址线在寻址的时候产生高电平(高电压)和低电平(低电压),就是1或者0
2^32字节的大小等于4GB
对于64位的机器也同理
这里我们就明白:
总结:
1.2 指针和指针类型
当有这样的代码
要将&num(num)的地址保存到 p 中,我们就知道 p 是一个指针变量
我们给指针变量相应的类型:
这里可以看到,指针的定义方式是:type+*
其实:
char*类型的指针式为了存放char类型变量的地址
short*类型的指针式为了存放short类型变量的地址
int*类型的指针式为了存放int类型变量的地址
那么指针类型的意义式什么?
1.2.1 指针的+1/-1操作
指针类型决定了指针+1/-1跳过了几个字节
即指针类型决定了指针向前或者向后走一步有多大
1.2.2 指针的解引用
我们把int*换成char*
指针类型是有意义的
指针类型决定了指针进行解引用操作的时候,访问几个字节
比如:一个int*访问4个字节,一个char*只访问1个字节
1.3 野指针
概念:野指针就是指针指向的位置是不可知的(随机的、不正确的、没有明确限制的)
1.3.1 野指针成因
1.3.2 如何规避野指针
VS中,局部变量未初始化的时候,里面存放的是‘cc cc cc cc’这样的值
1.4 指针运算
1.4.1 指针+-整数
p指向的是数组首元素的地址,p+i是数组中下标为 i 的元素的地址
在这个例子中,p+i其实是跳过了 i*sizeof(int) 个字节
所以我们可以认为
1.4.2 指针-指针
指针-指针的前提:两个指针指向同一块区域,指针类型也是相同
指针-指针差值的绝对值是两个指针之间的元素个数
1.4.3 指针的关系运算
将这样的代码简化如下
实际上,在绝大部分的编译器上是可以顺利完成任务的,但是我们还是应该避免这样写,因为标准并不保证它可行
标准规定:
允许指向数组元素的指针与指向数组最后一个元素后面的那个内存位置的指针比较,但是不允许与指向第一个元素之前的那个内存位置的指针进行比较
可以从前往后遍历,但是不要从后往前遍历
1.6 二级指针
指针变量也是变量,是变量就有地址
二级指针变量存放一级指针变量的地址
同理,也有三级指针变量,存放二级指针变量的地址
a的地址存放在p中,p的地址存放在pp中
p是一级指针,pp是二级指针
*pp通过对pp中的地址进行解引用,这样找到的是p,*pp访问的其实就是p
pp先通过*pp找到p,然后对p进行解引用操作:*p,这样最终找到了a
1.7 字符指针
在指针的类型中我们知道有一种指针类型为字符指针 char* ;
一般使用:
还有一种使用方法:
代码 const char* pstr = "abcdef";
特别容易让我们以为是把字符串 abcdef 放到字符指针 pstr 里了,但是,本质是把字符串 abcdef的首字符的地址放到了pstr中
上面代码的意思是把一个常量字符串的首字符 a 的地址存放到指针变量 pstr 中
注意:
C/C++会把常量字符串存储到单独的一个内存区域,当几个指针指向同一个字符串的时候,他们实际会指向同一块内存。但是用相同的常量字符串去初始化不同的数组的时候就会开辟出不同的内存块
1.8 函数指针
顾名思义,函数指针是指向函数的指针, 存放的是函数的地址
&函数名就能够得到函数的地址
函数名也是函数的地址
调用函数指针变量
举个例子(函数指针类型):
我们可以分析一个例子来理解
我们可以简化一下这个代码
1.8.1 回调函数
回调函数就是一个通过函数指针调用的函数
如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数
回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外 的一方调用的,用于对该事件或条件进行响应
1.8.1.1 qsort()函数
我们演示一下qsort函数的使用
qsort是一个库函数,底层使用的快速排序的方式,对数据进行排序的
这个函数可以直接用来使用,这个函数可以用来排序任何类型的数据
首先,我们先了解一下qsort函数的用法
这个函数一共有四个参数,我们能看到,第四个参数是一个函数指针
对四个参数的解释是
直译过来的意思是
排序的时候:
也就是不同类型的数据,比较大小的方法是有差异的
1.8.1.2 代码示例
qsort使用的时候需要包含<stdlib.h>头文件
代码效果就是这样
1.8.1.3 void*
这里我们解释一下void*的用法
2.1 指针数组
指针数组是指针还是数组?
答案:是数组,是存放指针的数组
数组我们已经知道整型数组,字符数组等
字符数组 - 存放字符的数组 char arr[7]; 整型数组 - 存放整型的数组 int arr[6]; 指针数组 - 存放指针(地址)的数组
使用指针数组模拟一个二维数组
但是这跟二维数组不一样,之前我们讲到,二维数组内存是连续的,指针数组是模拟的二维数组
他的原理是:通过arr找到arr1,arr2,arr3;再分别通过arr1,arr2,arr3找到数组内部的元素
2.2 数组指针
2.2.1 数组指针的定义
数组指针是指针?还是数组?
答案是:指针
我们已经熟悉:
那数组指针应该是:能够指向数组的指针
下面代码哪个是数组指针?
int *p1[10]; int (*p2)[10]; //哪个是数组指针
int (*p)[10]; //解释:p先和*结合,说明p是一个指针变量,然后指着指向的是一个大小为10个整型的数组。所以p是一个 指针,指向一个数组,叫数组指针。 //这里要注意:[ ]的优先级要高于*号的,所以必须加上()来保证p先和*结合。
2.2.2 &数组名和数组名
对于下面的数组:
arr 和 &arr 分别是啥?
我们知道arr是数组名,数组名表示数组首元素的地址
那&arr数组名到底是啥?
我们看一段代码:
可见数组名和&数组名打印的地址是一样的
难道两个是一样的吗?
我们再看一段代码:
根据上面的代码我们发现,其实&arr和arr,虽然值是一样的,但是意义应该不一样的。
实际上: &arr 表示的是数组的地址,而不是数组首元素的地址
&arr 的类型是: int(*)[10] ,是一种数组指针类型
数组的地址+1,跳过整个数组的大小,所以 &arr+1 相对于 &arr 的差值是40
2.2.3 数组指针的使用
那数组指针是怎么使用的呢?
既然数组指针指向的是数组,那数组指针中存放的应该是数组的地址
看代码:
数组指针也是指针,存放的是数组的地址
3.1 指针和数组的关系
指针和数组的关系就是:
数组的数组名是数组首元素的地址,而地址是可以存放到指针变量中的
由此可见:
数组名表示数组首元素的地址
但是有两个例外
既然可以把数组名当成地址存放到一个指针中,我们就可以使用指针来访问数组的元素
3.2 数组传参和指针传参
在写代码的时候难免要把【数组】或者【指针】传给函数,那函数的参数该如何设计呢?
数组传参,形参可以写成数组形式、指针类型
3.2.1 一维数组传参
3.2.2 二维数组传参
总结:二维数组传参,函数形参的设计只能省略第一个[ ]的数字。
因为对一个二维数组,可以不知道有多少行,但是必须知道一行多少元素。
这样才方便运算
3.2.3 一级指针传参
3.2.4 二级指针传参
3.3 函数指针数组
char* arr[5]://字符指针数组 - 数组 - 存放的是字符指针 int* arr[6]://整型指针数组 - 数组 - 存放的是整型指针
那么函数指针数组同理
函数指针数组 - 数组 - 存放的是函数指针 - 存放的是函数的地址
我们举个例子
这里int (*pfArr[4])(int,int)意思是
pfArr[4]是一个函数指针数组,这个数组有4个元素,每个元素都是一个函数指针
每个函数指针指向的是参数为(int,int),返回值为int的函数,即int(*pf)(int,int)
对比一下:
int(*p)(int,int) //函数指针 int(*p[4])(int,int) //函数指针数组
3.4 指向函数指针数组的指针
指向函数指针数组的指针是一个指针,指针指向一个数组,数组的元素都是函数指针
我们推一下
对比一下,函数指针数组和指向函数指针数组的指针
int (*pfArr[4])(int, int) //函数指针数组 int (*(*p)[4])(int, int) //指向函数指针数组的指针
版权声明:
本文来源网络,所有图片文章版权属于原作者,如有侵权,联系删除。
本文网址:https://www.mushiming.com/mjsbk/8253.html