指针是C语言中的一个难点,但如果真正理解了指针,其实没有很难,本篇文章介绍了指针的基础知识,后面还会继续更行指针的内容。感谢支持。
目录
1. 内存和地址
1.1 内存
1.2 究竟该如何理解编址
2. 指针变量和地址
2.1 取地址操作符(&)
2.2 指针变量和解引⽤操作符(*)
2.2.1 指针变量
2.2.2 如何拆解指针类型
2.2.3 解引⽤操作符
2.3 指针变量的⼤⼩
3. 指针变量类型的意义
3.1 指针的解引⽤
3.2 指针+-整数
3.3 void* 指针
4. const修饰指针
4.1 const修饰变量
4.2 const修饰指针变量
5. 指针运算
5.1 指针+- 整数
5.2 指针-指针
5.3 指针的关系运算
1. 内存和地址
1.1 内存
CPU( 中央处理器) 在处理数据的时候 ,需要的数据是在内存中读取的 ,处理后的数 据也会放回内存中 ,那我们买电脑的时候 , 电脑上内存是8GB/16GB/32GB等 。内存划分为一个个的内存单元 ,每个内存单元的大小取1个字节,方便高效管理。
计算机中常见的单位:
其实每个内存单元都有自己的唯一确定的编号,通过这个编号,CPU就可以快速,找到对应的内存空间,这个编号也就是地址,在C语言中也有一个名字叫作:地址。
我们可以理解为:内存单元的编号==地址==指针
1.2 究竟该如何理解编址
CPU访问内存中的某个字节空间 ,必须知道这个 字节空间在内存的什么位置 ,而因为内存中字节很多 ,所以需要给内存进行编址(就如同宿舍很多 ,需要给宿舍编号一样)。
计算机中的编址 ,并不是把每个字节的地址记录 下来 ,而是通过硬件设计完成的。
首先 ,必须理解 ,计算机内是有很多硬件单元,而硬件单元是要互相协同工作的。所谓的协同 ,至少相互之间要能够进行数据传递。
但是硬件与硬件之间是互相独立的 ,那么如何通信呢?答案很简单 ,用"线"连起来。
而CPU和内存之间也是有大量的数据交互的 ,所以 ,两者必须也用线连起来。
不过 ,我们今天关心一组线 ,叫做地址总线。
我们可以简单理解,32位机器有32根地址总线,每根线只有两态 ,表示0或1【电脉冲有无】 ,那么一根线 ,就能表示2种含义 ,2根线就能表示4种含 义 ,依次类推。32根地址线 ,就能表示2^32种含 义 ,每一种含义都代表一个地址。
地址信息被下达给内存 ,在内存上 ,就可以找到该地址对应的数据 ,将数据在通过数据总线传入 CPU内寄存器。
2. 指针变量和地址
2.1 取地址操作符(&)
理解了内存和地址的关系,我们再回到C语⾔,在C语⾔中创建变量其实就是向内存申请空间,
我们知道C语言中一个整形占4个字节,创建时会向内存申请4字节的空间,当取整形变量的地址时会取出4个字节地址的第一个地址,因为知道是int类型,当需要这个变量时就能顺藤摸瓜,将这4个字节全部访问。
2.2 指针变量和解引⽤操作符(*)
2.2.1 指针变量
在这里p就是指针变量,int*是指针变量类型,p变量里存放的是变量a的地址。
2.2.2 如何拆解指针类型
其实int* p理解为:int是p指针变量所指向的变量的类型。
*理解为:告诉我们p是指针变量。
p理解为:指针变量的名字。
如果是char类型的数据:
2.2.3 解引⽤操作符
我们得到了一个变量的地址,将他存在了一个指针变量中,我们这样做有什么用呢?
我们可以通过地址找到这个地址所指向的变量。来操作,改变它。我们必须要用到解引用操作符:*。
*pa=0;相当于通过pa中存放的地址找到了这个a变量,并把a赋值为0;
我们因为可以看到打印a与打印*pa的结果是一样的。
2.3 指针变量的⼤⼩
前⾯的内容我们了解到,32位机器假设有32根地址总线,每根地址线出来的电信号转换成数字信号后是1或者0,那我们把32根地址线产⽣的2进制序列当做⼀个地址,那么⼀个地址就是32个bit位,需要4个字节才能存储。
如果指针变量是⽤来存放地址的,那么指针变的⼤⼩就得是4个字节的空间才可以。
同理64位机器,假设有64根地址线,⼀个地址就是64个⼆进制位组成的⼆进制序列,存储起来就需要8个字节的空间,指针变的⼤⼩就是8个字节。
我们可以分别看一下在X86(32位)和X64(64位)下打印地址大小的结果:
32位下的结果:
64位下的结果:
可以看到在同一环境下不同类型的地址所占内存的大小是相同的,也就是说地址无贵贱,所占内存大小都遵循下面的规律:
•
32位平台下地址是32个bit位,指针变量⼤⼩是4个字节
•
64位平台下地址是64个bit位,指针变量⼤⼩是8个字节
•
注意指针变量的⼤⼩和类型是⽆关的,只要指针类型的变量,在相同的平台下,⼤⼩都是相同的。
3. 指针变量类型的意义
3.1 指针的解引⽤
代码一:
代码二:
代码一是将int*类型强制类型转变成了char*类型,解引用时只访问了一个字节将44变成了00
而代码二int*类型的pi解引用访问了四个字节,将n全变为0;
结论:指针的类型决定了,对指针解引⽤的时候有多⼤的权限(⼀次能操作⼏个字节)。
⽐如:
char*
的指针解引⽤就只能访问⼀个字节,⽽
int*
的指针的解引⽤就能访问四个字节。
3.2 指针+-整数
我们可以看出,
char*
类型的指针变量+1跳过1个字节,
int*
类型的指针变量+1跳过了4个字节。
这就是指针变量的类型差异带来的变化。
结论:指针的类型决定了指针向前或者向后⾛⼀步有多⼤(距离)。
3.3 void* 指针
在指针类型中有⼀种特殊的类型是
void*
类型的,可以理解为⽆具体类型的指针(或者叫泛型指
针),这种类型的指针可以⽤来接受任意类型地址。但是也有局限性,
void*
类型的指针不能直接进⾏指针的+-整数和解引⽤的运算。
举例:
将⼀个int类型的变量的地址赋值给⼀个char*类型的指针变量。编译器给出了⼀个警
告,是因为类型不兼容。⽽使⽤void*类型就不会有这样的问题。
但是void*并不是万能的:
这⾥我们可以看到,
void*
类型的指针可以接收不同类型的地址,但是⽆法直接进⾏指针运算。
那么
void*
类型的指针到底有什么⽤呢?这里有一篇博客可以参考,使用在了函数传参上面
4. const修饰指针
4.1 const修饰变量
变量是可以修改的,如果把变量的地址交给⼀个指针变量,通过指针变量的也可以修改这个变量。
但是如果我们希望⼀个变量加上⼀些限制,不能被修改,怎么做呢?这就是const的作⽤
上面代码中m被const修饰了不能再被修改了,
但是如果我们绕过m直接通过地址去修改m也是可以的。
我们可以看到这⾥⼀个确实修改了,但是我们还是要思考⼀下,为什么m要被const修饰呢?就是为了不能被修改,如果p拿到m的地址就能修改m,这样就打破了const的限制,这和我们的想法是不一样的,所以应该让p拿到m的地址也不能修改m,那接下来怎么做呢?
4.2 const修饰指针变量
总结:
const修饰指针变量的时候
•
const如果放在*的左边,修饰的是指针指向的内容,保证指针指向的内容不能通过指针来改变。
但是指针变量本⾝的内容可变。
•
const如果放在*的右边,修饰的是指针变量本⾝,保证了指针变量的内容不能修改,但是指针指
向的内容,可以通过指针改变。
5. 指针运算
指针的基本运算有三种,分别是:
•
指针+- 整数
•
指针-指针
•
指针的关系运算
5.1 指针+- 整数
因为数组在内存中是连续存放的,只要知道第⼀个元素的地址,顺藤摸⽠就能找到后⾯的所有元素。
我们通过运用地址访问了数组中所有的元素,并打印出来。
5.2 指针-指针
5.3 指针的关系运算
C语言中指针的知识多而复杂,可能有的地方写的不明白,欢迎在评论区讨论。
感谢观看,感谢指正错误。