大家好,我是一名刚学C语言两个月不到的菜狗子,以下是我两周以来学习指针的详细笔记,笔记内容可以说是结合了鹏哥C语言,翁恺C语言,以及CSDN上的一些大佬的分享,也加入了我很多的见解(每一个我自己的想法都有对应的代码验证)。
这是一篇给新手用来查漏补缺或者学习指针中较高级(对我来说比较高级)知识的笔记,主旨并不是讲明白最基础的概念,不建议完全没学过的新手用来入门C语言,但是较为复杂,难懂的内容我都会尽力做到讲明白,至于其中我也不会的两个题就丢出来给大家思考和讨论了(我记得好像就两个题,文章中会进行说明,下次博客更新的时候会提供答案,提前说明我会挖几个坑下次填哈哈)
以下为文章的目录,每一块标题都用黄色标注出来了,读者可以按需翻找
1.指针的定义 2.野指针 3.指针的基础运算
4.字符指针 5.指针数组 6.数组指针
7.二级指针 8.指针与数组指针的步长 9.一级指针传参和二级指针的传参
10.void*空指针 11.函数指针 12.函数指针数组
13.回调函数 14.指针相关作业含sizeof()、strlen()与指针的结合
废话一下,标题不让写萌新或者新手,我只能被迫写了一个大一新生,哭死,这水平根本不配代表大一新生
指针:
定义及基础知识
1.在内存中取一个字节为一个内存单元,每个字节里面存放一个指针
比如int a = 10;这个代码,其中a占四个字节,我们取a的地址的时候其实拿到的是四个字节中的第一个字节的地址
2.指针在32位系统中占4个字节,在64位系统中占8个字节
3.不同类型的指针都占同样的字节,那么指针类型的意义是什么?
(1)指针类型决定了指针解引用的权限有多大,即能操作几个字节
(2)指针类型决定了,指针走一步能走多远(详见例一)
例一
整型指针加一与char类型指针加一的结果不同
Int类型加一相当于跳过一个整型,char类型加一相当于跳过一个char类型
所以两个结果一个是+4一个是+1
具体的作用就是比如一个数组p指针,每次给p加一,那么每次跳过多少个字节取决于这个指针的类型,那么设置指针的类型就有意义了
野指针:
指向的位置是不可知的就是野指针
原因:
1.指针未初始化
2.指针越界访问
3.指针指向的空间释放了
例一:指针未初始化
没有初始化指针的时候里面存的是一个随机值,指向的是内存中某一个位置,这个值指向的是不是这个程序都不一定,所以再将20赋给这个值,然后再访问的时候就会出现非法访问内存了
例二:指针越界
例三:
相当于订的房间住完退订后,再想去住就会有问题了
综上,解决方法就是
1.使用指针的时候要记得初始化(但不知道要初始化为什么地址的时候,给一个NULL即可)
2.C语言本身是不会检查数据的越界行为的,自己要注意
3.为了防止野指针的出现,可以在开始的时候设置为NULL,使用前判断一下是不是NULL,使用完设置为NULL
指针的运算:
例一:指针相减
输出的结果是9,为什么?
指针-指针得到的是两个指针之间的元素个数
原因是什么?王八屁股——龟腚
指针和指针相减的前提是:两个指针指向的是同一块空间
为什么不用学习指针加指针?
有些运算是有意义的,有些是没有意义的,比如日期减日期得到的是相差的天数,但是日期加上日期没有什么实际的意义,指针的加减同理
例二:用指针求字符串的长度
这个代码可以指针-指针的方式进行运算
代码如下:
注意:
王八的屁股——龟腚
允许使用数组的最后一个的下一位进行比较,但是不允许第一位的前一位,总之就是访问数组的时候要++不要—,就是可以往后越界但是不能往前越界
字符指针:
常量字符串的概念:
Char arr[] = “abcdef”; 就是把这串字符放进arr[]数组中去,输入和输出函数都可以用
Char* arr = “abcdef”; 就是定义了一个常量字符串,里面的值是不能修改的只能用于输出函数,不能用于输入函数,被当作常量并且存储在内存静态区
Char*不仅能存储单个字符,也能存储字符串
存储字符串的时候是把首字符的地址存储起来了
例一:字符串不同方式定义时产生的区别
输出的结果是:
因为前两个是分别开辟了两块不同的空间
但是三和四是将同一块空间存储的内容的地址给了不同的指针,实际上三和四还是指向的是同一个地址,相当于把一个常量赋值给了a和b两个变量
指针数组:
定义:存放指针的数组,本质上是一个数组,存放的指针
格式如:int* arr[]
这样就是存放整型指针的数组了
例一:指针数组的初始化
菜狗写法
以下是较高级写法
这样就能遍历这个数组了
输出的时候也可以写成arr[i][j]
为什么输出的时候是用*(arr[i]+j)在后面讲解步长的时候有具体说明
数组指针:
本质上是一种指针,指向数组的指针
格式如:int (*parr)[10] = &arr;
先对parr解引用表示这是一个指针,指向的是有10个元素的数组,如果没有括号就成了一个数组了([]的优先级比*要高),所以可以把arr的地址放在*parr里面
Parr就是一个数组指针,存放的是数组的地址,*parr就相当于一个数组名
例一:数组指针与指针的加一跨过的字节大小不同
看似两个输出的结果是一致的,但是实际上类型是完全不一样的
第一个输出arr的是数组类型
第二个输出&arr的是指针类型,这个指针指向arr[10]这个数组
因为类型不一样,所以加一跨过的字节也是不一样的
这样输出的结果:
第一个跨过一个整型,就是4个字节,第二个跨过一个数组,就是10个字节
结论:
综上其实&arr和arr虽然值是一样的,但是意义是不一样的
&arr取得是数组的地址,而不是数组首元素的地址,当其地址+1的时候跳过的是整个数组的大小
补充:数组名是数组首元素的地址,但是有例外
1.sizeof数组名表示整个数组,计算的是整个数组大小
2.&数组名表示整个数组,取出的是整个数组的地址
例二:用指针的方式来访问数组元素
(*pa)+i就是让首元素的地址加i,其中*pa就相当于数组名,输出的结果是一个地址
所以要在进行一次解引用,*((*pa)+i)这样才是这个地址对应的值
输出的结果就是遍历这个数组
一般情况下一维数组不用数组指针,写起来很麻烦关键
例三:数组指针在二维数组的应用
二维数组的数组名表示首元素的地址,二维数组的首元素是第一行的地址