在开始学习C语言指针前,我们可以先将指针分为2个部分,由易到难,便可掌控指针。
- 初始指针1(了解基本的指针变量用法)
- 初始指针2(了解数组用指针如何计算,以及实现冒泡排序,指针数组)
初识指针1
1.1内存和地址
在现实生活中我们知道某个人的家庭地址,就可以通过这个地址来找到这个人,而内存在存储数据的时候,也会给相应的地址,在调试窗口内存中我们可以看到内存都是有地址的,而且我们可以通过地址来找到相对应的数据,
所以我们给地址取了一个名字叫指针,那指针就等于地址。
如果我们创建一个指针那么指针也是有在内存中占有空间的,在x86的环境下指针占用4字节,在x64的环境下指针占用8个字节。
所以指针的大小跟指针的类型无关。
1.2指针变量和&地址
我们在创建变量的时候,相应系统就会自动为我们分配好内存,内存的地址是有系统决定的,我们创建了一个int类型的变量a, 它的内存就如下图。
那么我们怎么a变量的地址呢?这里只要使用&a就可以取出a的地址,当然&a取出来的是地址中较小的地址,不过我们有了首地址也能找到后面的地址。
指针变量:
用&地址操作符取出地址后,那么我们应该保存在哪里呢?
其实就是保存在我们的指针变量当中。
这里注意指针变量的类型一定要跟指向变量的类型一致。
这里我们在俩理解*(接引用)操作符
在给指针初始化的的时候,解引用是来表示这是个指针变量,如果后续要使用指针该如果操作?
这段代码就将a的值修改成了0,解引用也已理解为,通过地址找到那个值。
指针类型的意义:
既然指针大小一样,为什么还有不同的类型呢?
指针的类型决定了指针可以访问地址的多少个字节,后面的(chart*)为强制转换类型,上面我们也说过指针指向的变量类型要与指针一致。
指针的加减:
指针的类型决定了它加1跳过几个字节
const修饰指针:
如果我们不想某个值被修改那么可以在指针的变量和指针的地址前加上const这样就不能被修改了。
但是需要注意const加在*前面可以防止使用地址修改,但是不能防止使用指针变量修改,这时候我们可以把指针变量也const上,这就都没有办法修改a的值了。(const可以增加代码的健壮性)
指针运算:
指针+指针
指针-指针
指针关系运算
野指针:
野指针就是指指向的空间是随机的,不确定的。
造成野指针一般有三个原因
第一:指针未初始化
第二:指针越界访问
第三:指针指向的空间已经释放
在其次如果我不使用指针的时候,可以及时将指针赋值为NULL,这样下在给指针赋值的时候可以进行判断如果为空就不使用。
当然我们也有库函数来判断是否指针为空,那就assert断言。
assert.h的头文件就包含有assert断言 ,而且当我们的程序运行没有问题,且不需要assert断言的时候就可以用NOEBUG宏关键字来取消断言。
assert也是增加代码的健壮性。
strlen的模拟实现和传址调用 :
观察以上代码,我们可以得出a跟b是肯定交换不了的,因为形参是实参的一份临时拷贝,重新回到主函数时,形参的空间就会被电脑回收。
这时候我们就可以用到指针变量了,因为指针变量是通过地址来进行修改。
这种穿变量地址的方式就叫:传址调用。
下面我们来看一下strlen的模拟实现:
初始指针2
数组名的理解:
一般我们在&地址的时候取的都是数组的首元素地址
但是有两个例外就是
sizeof(arr)这时的arr数组就是整个数组的地址
&arr这时的arr数组也是整个数组的地址
这时我们用vs来测试一下&arr但是看到地址还是一样的,不过我们在对地址进行后续访问的范围是不同的。
这时我们可以看到arr+1在x64的环境下只跳过的八个字节,而&arr+1却跳过了整个数组。
使用指针访问数组:
一维数组传参的本质:
我之前都是在主函数里用sizeof计算数组大小,那么我们可以创建一个函数,在函数内部计算一个数组的大小吗?
数组传参的本质就是传参数组的首元素地址
所以我们在函数内部不能使用sizeof计算出数组内元素的大小。
冒泡排序:
冒泡排序的核心思想就是两两相邻的元素进行比较
二级指针:
有变量就有地址,那么我们的指针变量肯定也是有地址的,那么指针变量存放在哪里呢?
**ppa先通过解引用找到*pa在通过*pa找到b,就成功修改了b的值。
指针数组:
我们知道数组里面的每一个数的类型都是由数组变量类型决定的
那么如果是int*arr【5】里面肯定也都是指针(地址)
那么我们可以用指针数组可以用来存储地址这特性,用来模拟实现二维数组。