指针的部分总结(上)


title: 指针的部分总结(上)

指针是C语言的关键所在,可以说掌握了指针就打通了学习C语言的经脉。作为C语言的特点也是难点,一部分同学在学习指针的时候会感觉云里雾里,希望我的博客会对学习困难的同学有一定帮助。大家一起加油(ง •_•)ง!


一、指针是什么:

指针其实也是一种变量,与int、char一样,代表的是不同的数据类型。只是int类型变量以整数作为其数值,char类型变量以字符作为其数值,而指针变量的数值是地址。所以指针也可以进行一系列变量可以进行的操作,比如对指针赋值,指针的加减,两个指针大小的比较(同类型)等等。


二、为什么要学习指针 :

这里有一个很多人问的问题:我们到底为什么需要指针,直接用变量名对数值进行操作不就可以了吗?

用变量名当然可以,但是只是用变量名会出现很多bug,因为人脑的思维方式和电脑的运作方式不一样。我们知道int代表整数啊,char代表字符啊,但是计算机不知道,计算机只知道地址和指令(这个也是当本菜鸡学习汇编指令之后才明白的)。

当我们查看编译之后的代码时,它长这样:

这里并没有我们习以为常的各种变量名,只有一系列抽象的数字串。所以在处理一些复杂项目的时候,全篇使用变量名可能会出现一些让人摸不着头脑的bug,而这些错误的源头位置也较难发现。

这样说还是抽象了点,接下来举个栗子:

如图是李明的部分代码,他在主函数中定义了整型变量a之后,在函数fun1中对a进行了修改,但在输出结果中,a的值没有改变。像这样,多个函数要对同一个数据进行读写操作的时候就需要用到指针。

需要理解的是,在函数中对数据的改动只是对数据副本的更改,原本不动。如果想要对原值进行修改,就需要传入数据的地址,这样程序才能找到地址将其修改


三、对某个特定指针的理解:

1. 个人认为,学好指针的关键在于对计算机内存的理解。

计算机的内存是用于储存数据的空间,空间的分布是建立在字节(byte)和位(bit)的概念之上的。一个位能表示0/1两个状态,而一个字节是一个8位的块,大多数计算机将字节作为最小的可寻址内存单位。不同的数据类型的占用内存的大小不同,int类型的数据占用4字节的内存而char类型的数据占用1字节的内存。

当我们声明一个变量的时候,实际上是向内存申请了一块空间来存放数据,而一个指针存储的数据就是该指针所指向变量的起始地址。

2.当我们看到一个指针的时候,需要搞定四道关卡:指针的类型、指针所指数据的类型、指针的值、指针本身所占的内存。

首先说用到最多的:指针的类型和指针所指数据的类型。

从语法的角度看,将声明语句中指针的名字去掉剩下的就是指针的类型,而将指针名字以及其左边的指针声明符*去掉之后剩下的就是指针指向数据的类型。

接下来还是举一些例子:

(1)int *ptr

指针类型:int*

指针指向数据类型:int

(2) char *ptr

指针类型:char*

指针指向数据类型:char

(3) int **ptr

指针类型:int**

指针指向数据类型:int*

(4) int(*ptr)[3]

指针类型:int*[3]

指针指向数据类型:int[3]

在这个例子中,从名称ptr出发,ptr先与*结合(加括号的原因是[ ]优先级高于 *,所以分析的时候可以去掉括号),说明ptr是一个指针,指针与[]结合说明指针指向的内容是数组,然后与int结合说明数组元素是整型,所以p是指向整型数组的指针。

与指向数组的指针相对应的是指针数组:int*ptr[3]

因为ptr首先与[ ]结合,所以ptr是一个数组,然后与*结合,说明这个数组中的元素是指针,然后与int结合,说明指针指向的是内容是整型。所以这是一个指向整型数据的指针组成的数组。

(5) int *(*ptr)[4]

指针类型:int**[4]

指针指向数据类型:int*[4]

通过上一个例子的解释,例(5)就很明了是指向数组的指针,而这个数组是由指向整型数据的指针组成的。

接下来要讨论的是指针的值,我们说指针中存储的是地址,所以指针的值就是地址的大小。即为指针所指向的数据的起始地址。

指针本身所占的内存大小也就是说指针这种变量类型的字长,在64位机器(大部分机器)中是八字节。


四、指针的使用:

1、首先必须强调的一点是绝对不能对未初始化的指针取值!!!

形如

int *ptr; *ptr=5;

这样的未初始化的指针我们称之为“野指针”,其危害性极大。第二句表示将数值5存储在指针ptr所指向的地址中,但是因为指针未初始化,所以其值是随机的,我们不知道5会被存到什么位置。也许会存到某个已经有程序数据的地址,这样做的后果是原有的数据会被覆盖,可能导致程序崩溃。

指针需要初始化的原因在于当创建指针的时候,系统之分配了用来存储指针本身的内存空间。并没有给它分配存储数据的内存空间。因此在使用指针之前,必须给它赋予一个已经分配的内存地址用来存储数据。


2、运算符:&和*(作为单目运算符)

&即为取址运算符,对一个变量a进行取址运算&a得到的是a的地址,即&a成为了指针变量。

*是间接运算符,用来拿到一个指针所指向地址的内容。


3、数组与指针及数组的算术运算:

数组标记其实是对指针的变相使用。对于数组:data[int size],数组名data实际上就是该数组首元素的地址,即数组名就是指向数组起始元素的指针:data = = &data[0]。

在C语言中,对指针加1的结果是对该指针增加一个存储空间(即sizeof((指针指向的数据类型))。所以对一个数组data[size]来说,*(data+1)=data[1]。但是对于非数组指针来说,情况就会变得不太一样。

举个栗子:

思考一下会输出什么?

我是一道分隔符

答案是:J 未知数

我们来分析一下ptr这个指针,它的类型是char ** ,所指向的类型是char * ,所以当ptr执行递增操作时,指针会增加一个sizeof(char*)即指针原来指向的地址+8.数组是一系列连续排列的地址,但是指向数组的指针的地址可并不是连续排列的,而指针的地址+8,谁都不知道会指向哪里。


4、指针与多维数组

假设有声明:int zippo[4][2]

那么有这么几个概念:zippo,zippo[0],zippo[0][0]

zippo是数组首元素地址,所以zippo=&zippo[0]。

而zippo[0]是包含两个整数的数组名,所以zippo[0]=&zippo[0][0]。

因为整数和两个整数组成的数组开始的地址相同,所以zippo=zippo[0],两者数值一致。

那么来判断一下zippo+1==zippo[0]+1这句代码的正确性

答案是否定的。zippo指向的是一个由两个整数组成的数组,zippo[0]指向的是一个整数,所以zippo+1是原值+8(2*4),而zippo[0]+1则是原值+4

当用指针符号表示时,

zippo==zippo[0]

*zippo==zippo[0]==&zippo[0][0]

**zippo==*zippo[0]==zippo[0][0]

zippo[2][1]==*(*(zippo+2)+1)

对于最后一行等式,如果不理解可以看这里:

zippo第一个一维数组的首元素地址
zippo+2第三个一维数组的首元素地址
*(zippo+2)第三个一维数组的首元素地址,和zippo+2的区别在于类型转变了
*(zippo+2)+1第三个一维数组的第二个元素的地址
*(*(zippo+2)+1)第三个一维数组的第二个元素的值
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值