指针,作为C语言中的一个重要组成部分,让不少还未入门C语言的小白”闻风丧胆“。而今天,我将将我目前所学到的有关C语言的指针的内容和理解呈现给大家,帮助更好地学习与理解指针
目录
1.何为指针
指针,是C语言中的地址的另一个称呼。而要了解指针就不得不先从内存与地址讲起。我们知道计算上CPU(中央处理器)在处理数据的时候,需要的数据是在内存中读取的。那内存中是怎么读取数据的呢?首先,内存里面会划分为一个一个小的内存单元,每个内存单元的大小为1个字节。(字节为计算机中的单位),而每个字节内又存放了8个比特位。每个比特位可以存放0或1。而且数千上万种0和1的排列组合最终构造出我们所需要的数据。但计算机虽然神奇,可终究是人创造的,计算机读在内存里数以万计的内存单元中找到我们需要的内存单元并取数据时,不可能凭空能找到数据,它仍然需要我们为它提供一个具体的,明确的内存单元编号。而这个编号我们就称为地址。地址的作用犹如一根一般精确地指向我们需要的内存单元,于是我们又把地址叫做指针。至此,我们可以理解为:
所以以后无论是说内存单元的编号或者地址或者指针,你都要知道它们本质上都是指向同一个东西,只是称呼不同罢了。
2.指针变量和地址
2.1取地址操作符(&)
首先我们要明确一点,在C语言中创造变量的本质就是向内存申请一块空间来存放我们想要的数据。而不同的数据类型内存所给的空间大小也不同,例如,整型int的大小是4个字节,char类型的大小是1个字节,等等。
如上图所示,计算机向内存申请了一块大小为4个字节的空间,每一个字节都有自己的地址。那我们要如何获得并使用a的地址呢?(&)——这个操作符正是我们所需要的,其名为取地址操作符。
正如上图一样,我们通过取地址操作符(&)成功找到a的地址,但前面我们不是说过int a的大小不是有四个字节吗?可为什么这里只有一个呢?事实上,&a取出的是a所占的4个字节中地址较小的地址。计算机自己会通过这较小的地址找到接下去紧挨着的三个字节的地址。
2.2指针变量和解引用操作符(*)
2.2.1指针变量
像上面图中,我们已经通过&取出了a的地址0x007FF8CC。当然,我们取出a的地址后肯定是希望它为我们所用,那我们肯定得把它存放起来吧,那我们要把地址存放到哪里呢?其实C语言已经帮我们规定好了,存放到指针变量中。
指针变量本质上也是一种变量,而这种变量是专门用来存放地址的,存放在指针变量中的值也会被理解为了地址。
首先我们先创建一个指针变量int * pa,并打印出pa存放的数据
我们发现打印出的地址是一样的,这也说明我们pa中存放的就是a的地址。
我们可以简单将指针变量、指针、数据之间的关系通过上图中的食物、碗和碟子的关系来类比。指针存放我们的数据,而指针变量又存放我们的指针。
2.2.2如何拆解指针类型
在上面中我们创建了的pa的类型是int*,要如何理解呢?
首先这个*是在向计算机说明pa是指针变量,然后int则是在说明pa指向的是整型(int)类型的对象。同样的,加入现在我们要存放一个char类型的变量ch的地址,我们就需要char*类型的指针变量了。
2.2.3解引用操作符(*)
如果说我们将指针存放到指针变量中比作将东西放入盒子中,那我们要取出指针就犹如打开盒子,我们需要一把钥匙,而这把钥匙就是解引用操作符(*)。
老样子,我们上代码,通过代码来理解。
我们不难发现,a的值与*pa的值相同且a的值还被修改,其实*pa的意思就是通过pa中存放的地址,找到指向的空间,*pa其实就是a变量了:所以*pa = 24,也就相当于a = 24了。
2.3 指针变量的大小
就像指针需要开辟空间来存放数据一样,指针变量也需要空间来存放我们的指针,那指针变量的空间有多大呢?也就是指针变量又多少字节呢?其实,指针变量的大小与我们CPU的地址总线有关,也就是和我们机器是在几位的平台下有关,这里我将不详细展开,有兴趣的小伙伴可自行了解。
总之,我们只需要记住结论即可:
结论:
• 32位平台下,指针变量大小是4个字节(32个bit位)
• 64位平台下,指针变量大小是8个字节(64个bit位)
• 注意——指针变量的大小和类型是无关的,只要指针类型的变量,在相同的平台下,大小都是相同的。
所以在相同的平台下:无论时int*,还是char*等等,其大小均相等。
3.指针变量类型的意义
3.1 指针的解引用
首先我先给出结论:
指针的类型决定了,对指针解引用的时候有多大权限(一次能操作多少个字节)。比如:char*的指针解引用就只能访问一个字节,而int*的指针解引用就能访问四个字节。
怎么来理解呢?
3.2 指针加减整数
char*类型的指针变量+1跳过了一个字节,int*类型的指针变量+1跳过了四个字节。这就是指针变量的类型差异带来的变化。
结论:指针的类型决定了指针向前或向后走一步的距离有多大。
3.3 void指针
void指针是一种没有具体类型的指针,它的作用是用来接收任意类型的地址。特别注意的是,void类型的指针不能直接进行指针的+-整数和解引用的运算。
4. const修饰指针
4.1const修饰变量
总所周知,变量是可以修改的,但有时候我们并不想让别人修改或者防止未来的自己修改了某些重要的变量或者参数。这时候就需要我们给这些变量上锁,而这个锁就是const。
但,但是,正所谓道高一尺魔高一丈。const是限制了a变量的修改,但这中间仍然存在一个bug,这个我们的指针有关。有些聪明的小伙伴可能就已经先想到了。没错,正是从地址入手,通过取出a的地址,再修改a地址上的值。
看,就像这样,a的值即使是被const限制我们仍然可以修改。但,但这明显已经违背了我们的初衷,属于是打破了语法规则。难道真是就无懈可击了吗?显然不是,既然你需要通过获取我a变量的地址来修改,那我就干脆让你连地址都无法获得,都无法修改,我们接着讲。
4.2 const修饰指针变量
让我们来观察下面几组代码
我们发现,两段相似的代码,却在不同的地方发生报错,且均是报错警告我们不可被修改。让我们继续来分析:
当const在int*左边时,pa指向的内容物不可被修改,即*pa = 23不被允许;
当const在int*右边时,pc不可指向其他地址,即pc = &d不被允许。
我们直接上结论:
当const修饰指针变量的时候:
• const如果放在*的左边,修饰的是指针指向的内容,保证指针指向的内容不能通过指针来改变。 但是指针变量本身的内容可变。
• const如果放在*的右边,修饰的是指针变量本身,保证了指针变量的内容不能修改,但是指针指向的内容,可以通过指针改变。
这么说可能会比较抽象,让我们痛过一个简单的例子来加深理解:
综上所述:如果如果都不允许改变,我们可以祭出最强杀招!int const * const pa,从两头锁死。
5.指针运算
5.1指针+-整数
我们知道数组在内存中是连续存放的,只要找到首元素地址,计算机就会顺下找到其他所有元素的地址。
我们上例子:
从中我们可以看出:pa是数组首地址,pa + 1就是数组的第二个元素,即指针+-整数就是跳过整数个指针大小的意思。丛中我们还能发现,arr[i] = *(arr + i)。详细的我们在后面的内容中还会讲到。
5.2 指针 - 指针
指针 - 指针很好理解,它得到的是两个指针之间的元素个数(绝对值)。
前提条件:两个指针指向同一块空间!!!
6.野指针
概念:野指针就是指针指向的位置是不可知的(随机的、不正确的、没有明确限制的)。
下面几种情况均是野指针。
因此,为了规避野指针的出现,我们应该注意指针是否有初始化,是否会越界等等。
指针的内容远远不仅如此,限于篇幅有限,我将会继续更新指针相关的知识。才疏学浅,如有错误或者不当之处,希望大家不吝提出。
完