目录
前言:
一.指针基础
1.1内存单元
1.2内存单元和指针的大小
二.指针变量
2.1指针类型
2.2野指针
三.指针运算
四.指针和数组的关系
4.1下标与解引用的等价替换
4.2指针数组
五.二级指针
❤博主CSDN:啊苏要学习
▶专栏分类:C语言◀
C语言的学习,是为我们今后学习其它语言打好基础,C生万物!
开始我们的C语言之旅吧!✈
前言:
接下来我们进入C语言里的重头戏---指针。指针是一个很好用的东西,学好指针,无异于打下半片江山。
一.指针基础
指针实际上是用来指向内存空间的一个变量。可能读者会觉得博主说得轻描淡写,我们先抛开先前对指针的恐惧,一步一步来讲,先从内存的基础讲起。
1.1内存单元
内存是存取数据的一块大空间,我们知道,无论是什么事物,不好好利用都会有挥霍完的一天,相应的,要想高利用率的使用好内存空间,计算机前人们在设计的时候,就把内存空间划为一个一个小小的内存单元。
那又如何区分这些长的一样的内存单元呢?在生活中,一栋栋商品房,房型差不多,如何让住户知道自己买的房间在哪里咧,假设有亲戚朋友要来我家,如何让他们知道我住哪里呢?我想,给个编号,给个地址不就好了~,现实中如此,计算机也如此,使用一个个编号给内存单元定义地址。
这个编号是使用地址线(电线)的通电,不通电这两种状态转换成数字信号1,0给内存单元编号,这个编号也就是它们的地址啦。
补充:32位电脑有32根地址线,每一根地址线可以产生0或1的数字信号,总共有2的32次方种组合,也就是可以管理2的32次方个内存单元。64位电脑就可以管理2的64次方个内存单元。
1.2内存单元和指针的大小
那么一个内存单元多大合适呢?在以前我们常说4G、8G内存的电脑。我们姑且来算一下吧,假如一个内存单元只能存储一个bit位,那么32位机器内存大小为2的32次方个bit位:
二进制第33位的1代表2的32次方,我们现在转换成Gb单位需要除以8(Byte)、除以1024(Kb)、除以1024(Mb)、除以1024(Gb)。
由于程序员的除法取商,再除1024就是0了。所以现在是转换到Mb的单位,512Mb就是0.5G,这未免也太小了吧,听过最小的内存也是4G~,那一个内存单元从一个bit位变成一个字节呢?0.5G乘以8变成4G,可以接受~
总结:计算机中一个内存单元是一个字节的大小。
指针是存放内存单元地址的,一个地址的产生前面也说过。如果是32位平台,就用32根地址线生成32个数字信号来编号,那一个地址就是32个bit位,所以指针大小就是4个字节;如果是64位平台,指针大小为8个字节,这是固定的,不会根据数据类型改变指针大小。
总结:指针大小如果在32位平台下是固定4个字节,在64位平台下是固定8个字节。
二.指针变量
如何创建指针变量呢?请看值一段代码:
#include <stdio.h>
int main()
{
int a = 0;
int* pa = &a;
*pa = 10;
printf("%d\n", a);
return 0;
}
2.1指针类型
和创建普通变量是一样的,指针类型+变量名创建整型指针变量pa。&取地址操作符取出整型变量a的地址放到指针变量pa里面,*解引用pa的意思是找到pa存放的地址对应的内存单元,也就是找到了a,*pa相当于a。
char*、short*、int*、float*、double*的大小分别是多少?都是4个字节的空间大小(32位),那char*类型的指针变量也可以存放整型变量的地址是毋庸置疑的,但这样做对吗?我们看代码:
将a的地址放到char*的指针里面是能放进去的,只是编译器会有一个警告,报的是类型不兼容:
这就是我们这里要讲的指针类型的意义:
- 指针变量的类型决定了指针解引用时改变多少个字节
- 指针变量的类型决定了指针加减运算时跳过多少个内存单元
我们使用整型指针可以把-1完全变成0,而使用字符指针却改不成,这是为什么呢?请看:
*pc只改了一个字节,(内存中用十六进制表示,两个十六进制位等于一个字节,16是2的四次方),所以a的补码为0xffffff00;打印的时候转换成原码~
以上解释就是指针类型决定指针访问权限。还有一个指针决定指针加减运算跳过多少个内存单元的问题,博主简单说一下。
一个指向整型的指针+1,指针变量里的地址会指向哪一个内存单元呢?我们看代码:
整型a的起始地址是c4,本来a占有的内存单元有c4、c5、c6、c7这四个内存单元,一个内存单元一个字节,整型变量是四个字节大小嘛。然后使用它的首地址代表a的地址,整型值10就放在这四个内存单元里。p存放的值是a的地址,所以&a和p是一样的。
打印p+1不是等于c5,而是c8,为什么呢?这里我们就得了解指针类型决定指针跳过多少个内存单元。可以这样理解,整型指针加1的意思是:指向下一个整型(当然下一个不一定是整型),只是这样说而已。
2.2野指针
野指针就像野孩子一样,没有人管,这是非常危险的。野指针的常见情况如下:
- 没有初始化的局部指针变量。
- 指针指向的变量销毁掉了。
- 数组越界访问。
局部变量没有初始化是随机值,一个局部指针变量如果没有初始化,那它也是一个随机值;指针变量是存放地址的变量,随机值存放在指针里面会被当成地址看待。如果此时不小心对指针进行了解引用操作,就会对不属于我们的内存单元里的内容进行修改,造成破坏,导致程序挂掉。
指针指向的空间释放掉了,指针就变成了野指针。请看代码:
a是test函数里的局部变量,在test函数里a存在,然后返回a的地址,a出了代码块,生命周期结束,被释放掉了,可是*p还是接收到a变量的地址,但a已经不属于我们了,此时p就是野指针。
数组越界访问变成野指针在后面讨论指针和数组的关系时讲~
在创建指针变量的时候,如果不知道指向谁,初始化为NULL(空指针) |
指针使用完后,赋值成空指针 |
在使用指针之前,用if测试指针现在放的是不是NULL,因为空指针不能解引用 |
三.指针运算
指针的运算有:指针加减整数,指针减指针,指针大小关系比较。
指针加减整数前面讲过了,就是什么样的真正类型,跳过多少个内存单元。我们主要是看一下指针减去指针和指针关系比较。
指针减去指针表示的是之间的元素个数;这里是9个元素。可以这样理解&arr[9]-&arr[0]下标9减0是9。
补充:指针相减的绝对值才是元素个数,因为指针相减可以是负数。指针与指针相减的前提是指针类型相同、指向同一块空间,基本用在数组里面。
指针的关系运算:
#include <stdio.h>
#define n_ele 5
//方式一:
int main()
{
float* vp = NULL;
float varr[n_ele];
for(vp = &varr[n_ele]; vp > &varr[0];)//第三个条件省略
{
*--vp = 0;
}
return 0;
}
//方式二:
int main()
{
float* vp = NULL;
float varr[n_ele];
for(vp = &varr[n_ele-1]; vp >= &varr[0]; vp--)
{
*vp = 0;
}
return 0;
}
使用两个指针的关系比较充当for循环里的入口条件测试。
第一种方式vp第一次从下标为5的位置和下标为0的位置比较,注意下标为5的位置其实不在数组内;*--vp是先把指向数组下标为5的指针变成指向数组下标为4的指针,再*解引用找到内存空间改成数值0,没有越界操作。
第二种方式第一次从下标为4开始比较,最后停止的时候,是用指向相当于数组下标为-1的指针和数组下标为0的比较,不符合循环条件,退出循环,也没有越界访问。这两种方式完成的功能都一样,把数组元素都赋值成0,哪一种好呢?
其实第一种方式会更好,因为他是C语言标准允许的。C语言标准规定:允许指向数组元素的指针和指向数组最后一个元素的下一个位置的指针进行比较,但不允许和指向第一个元素前一个位置的指针进行比较。
四.指针和数组的关系
相信在前面,读者多多少少感觉到了数组的下标访问,和指针运算之间有种莫名其妙但又说不出来的感觉。其实数组的和指针是非常相似的!
以前说过数组名一般表示的是首元素地址,既然是地址,其实也就是指针!但和指针不同的是arr它表示的是首元素地址,也就是说 arr = arr + 1;这句代码试图将arr指向第二个元素,这是不允许的。如果是这种情况 :p = arr + 1;指针p在没有再次修改的情况下,都是指向数组第二个元素。
4.1下标与解引用的等价替换
arr[0]访问的是数组的第一个元素,用指针可以*(parr+0)访问;arr[1]访问的是数组的第二个元素,用指针可以*(parr+1)访问;所以下标访问实际上就是让arr(首元素地址)加上一个整数【指针加减整数运算】后,解引用访问。
总结:arr[i] = *(arr+i) = *(parr+i) = parr[i]
阴间写法:*(arr+i)和*(i+arr)道理是一样的,前面是arr[i],后面可以这样写i[arr]!!!
4.2指针数组
指针数组,存放指针的数组~
#include <stdio.h>
int main()
{
char* arr[3] = {"hello,world","C so good","I like it"};
//字符串常量时首字母地址,也就是说"hello,world"实际上表示的是h的地址
//是字符的地址,用字符指针char* ,arr是一个接收字符指针的数组---指针数组。
return 0;
}
五.二级指针
指针是存放变量的地址的,指针变量也是变量,它也有地址。存放一级指针变量地址的指针叫做二级指针。
一级指针pa的类型是int*,这颗*不是解引用的那颗星,而是用来标志pa是一个指针的,这是一种理解;另一种理解是*pa的类型是int,也就是说pa解引用后指向的是一个整型。
二级指针ppa的类型是int**。一样的道理,可以把靠近pa的一颗星看做是一个标志ppa是一个指针,然后是指向int*的;也可以是*pa后是一个一级整型指针。
指针的内容不止有这些,学习指针的路还很长,后面再回~
结语:希望读者读完有所收获!在学C的路上,祝福我们能越来越C!✔
读者对本文不理解的地方,或是发现文章在内容上有误等,请在下方评论区留言告诉博主哟~,也可以对博主提出一些文章改进的建议,感激不尽!最后的最后!
❤求点赞,求关注,你的点赞是我更新的动力,一起努力进步吧。