c指针
初识c指针
1.什么是指针
2.指针和指针类型
3.野指针
4.指针运算
5.指针和数组
6.二级指针
7.指针数组
进阶了解c指针
1.字符指针
2.数组指针
3.指针数组
4.数组传参和指针传参
5.函数指针
6.函数指针数组
7.指向函数指针数组的指针
8.回调函数
9.指针和数组试题
开始之前,我们先来看一看这些东西
- int p; //这是一个整型变量
- int *pa; //首先从P 处开始,先与*结合,所以说明P 是一个指针,然后再与int 结合,说明指针所指向的内容的类型为int 型.所以Pa是一个返回整型数据的指针
- int p[]; //首先从P 处开始,先与[]结合,说明P 是一个数组,然后与int 结合,说明数组里的元素是整型的,所以P 是一个由整型数据组成的数组
- int *p[]; //首先从P 处开始,先与[]结合,因为其优先级比*高,所以P 是一个数组,然后再与*结合,说明数组里的元素是指针类型,然后再与int 结合,说明指针所指向的内容的类型是整型的,所以P 是一个由返回整型数据的指针所组成的数组
- int (*p)[]; //首先从P 处开始,先与*结合,说明P 是一个指针然后再与[]结合(与"()"这步可以忽略,只是为了改变优先级),说明指针所指向的内容是一个数组,然后再与int 结合,说明数组里的元素是整型的.所以P 是一个指向由整型数据组成的数组的指针
进阶
- int **p; //说明一下,这是个二级指针喔。首先从P 开始,先与*结合,说P 是一个指针,然后再与*结合,说明指针所指向的元素是指针,然后再与int 结合,说明该指针所指向的元素是整型数据.由于二级指针以及更高级的指针极少用在复杂的类型中,所以后面更复杂的类型我们就不考虑多级指针了,最多只考虑一级指针.
- int p(int); //从P 处起,先与()结合,说明P 是一个函数,然后进入()里分析,说明该函数有一个整型变量的参数,然后再与外面的int 结合,说明函数的返回值是一个整型数据
- Int (*p)(int); //从P 处开始,先与指针结合,说明P 是一个指针,然后与()结合,说明指针指向的是一个函数,然后再与()里的int 结合,说明函数有一个int 型的参数,再与最外层的int 结合,说明函数的返回类型是整型,所以P 是一个指向有一个整型参数且返回类型为整型的函数的指针
- int *(*p(int))[3]; //可以先跳过,不看这个类型,过于复杂从P 开始,先与()结合,说明P 是一个函数,然后进入()里面,与int 结合,说明函数有一个整型变量参数,然后再与外面的*结合,说明函数返回的是一个指针,,然后到最外面一层,先与[]结合,说明返回的指针指向的是一个数组,然后再与*结合,说明数组里的元素是指针,然后再与int 结合,说明指针指向的内容是整型数据.所以P 是一个参数为一个整数据且返回一个指向由整型指针变量组成的数组的指针变量的函数.
看了看上面的东西,是不是感觉指针并没有那么难,减少对指针的恐惧,好啦,现在开始我们的主题。
第一部分
1.什么是指针?
在计算机科学中,指针是编程语言中的一个对象,利用地址,它的值直接指向存在电脑存储器中另一个地方的值,由于通过地址能找到所需的变量单元,可以说,地址指向该变量单元。因此,将地址形象化称为:指针。意思为它能找到以它为地址的内存单元
我们可以这样理解:内存是一块大的空间,这么大的空间我们该怎么去管理?把内存划分为一个个内存单元,取一个字节为基础的内存单元,为了很好管理,我们把这些内存进行编号,这个编号在内存中也叫地址,把地址也形象称为指针。就这样内存编号,内存地址,指针实际上都是一回事,是不是概念更加清楚了。
实际动手一下,比如int a = 10;//占4个字节空间,&a拿到的是a的4个字节中第一个字节的地址,int*pa = &a;*说明pa是指针变量,然后int说明pa指向的对象是整型类型*pa = 20;通过pa就能很好的找到a,这时候20就能改掉,这是上面第2点讲到的。地址存起来需要一个变量,既存放指针,所有把存放地址的变量称为指针变量。
总结一下:指针就是变量,用来存放地址。指针是多大的呢?指针的大小在32位平台是4个字节,在64位平台是8个字节,不知道大家知不知道这个点,这里稍微提一下。
2.指针和指针类型
大小都是4个字节啊,那跟类型有什么关系,类型好像没什么用,类型没什么用,我们造一个通用类型不就行了?用一个代表所有不是很简单??此时我们心中一万个疑问涌上,实际上并没有存在,这反过来恰恰说明了一个点,这些类型是有用的,不会平白无故的出现。意义是什么?
这里很有必要说一说指针类型的意义,我看过别人写过指针的博客,但是好像都没有说到这里
指针类型的意义
1.指针类型决定了解引用的权限有多大 。int解引用能访问4个字节 char解引用能访问1个字节
2.指针类型决定了,指针走一步,能走多远(步长),这这里给个代码大家好好看一下
总结:指针的类型决定了指针向前或者向后走一步有多大以及权限的大小,这就是指针类型的两个意义,不是胡乱的出现的。这一点可以提高大家的理解。
3.野指针
野指针就是指针指向的位置是不可知的(随机的,不正确的,没有明确限制的)
原因
1.指针未初始化
2.指针越界访问
3.指针指向的空间释放
返回a后生命周期结束,销毁还给操作系统,*p访问空间的时候出问题了。
如何规避野指针?
指针初始化
吐槽一下,课本上很多东西都没初始化,这些需要我们注意,初始化是良好的编程风格
小心指针越界
指针指向空间释放既使置NULL
使用前检查有效性
4.指针运算
指针+-整数
指针-指针
指针的关系运算
指针的关系运算是比较大小
这段代码用指针+整数以及指针的关系运算。还有一个指针-指针
实际上,指针-指针得到的两个指针之间的元素个数。指针和指针相减的前提是:两个指针指向同一块空间
这里提一个标准规定:
允许指向数组元素的指针指向数组最后一个元素后面的那个内存位置的指针比较,但是不允许与指向第一个元素之前的那个内存位置的指针进行比较。
5.指针和数组
数组名是什么?
数组名是首元素的地址,这是我们反复强调的,这里可以理解一下,问题不大
从这个代码我想告诉大家一个点,首元素地址+i产生的就是下标为i的地址 。
在来看一个有趣的
//arr[2]----*(arr+2)----*(2+arr)---2[arr]
到这里大家有没有对数组有了更深层次的理解呢
6.二级指针
有了这样的理解之后,我们在来看看二级指针
什么是二级指针呢?
二级指针
来看一小段代码
ppa首先是指针既*ppa,ppa指向的对象是pa,pa的类型整体是int*,所以就是int* *ppa,书写上有区别,但是实际并没有区别,ppa这就是二级指针变量。希望大家能够理解。 实际上就是套娃....
就是说*ppa == pa | *pa == a | **ppa == a,两层关系,这就是二级指针
看到这里,我们很容易联想到三级指针,继续套娃
当然,你也可以搞出四级指针,五级指针,语法是支持的,但是很少用到,没必要深究
看到这里,你是不是对二级指针有了更深的理解。
最后,我们在来学习一个知识点
7.指针数组
指针数组是什么东西呢?
指针数组实际上----数组
怎么说呢?
先来了解一下,整型数组——存放整型的数组就是整型数组
字符数组——存放的是字符
以此类推
指针数组——存放指针的数组
前面刚开始是第4点有提到,是不是就串起来了呢,当然,我们可以看看这个代码
parr[5]是一个数组,存放整型指针的数组,大家应该理解了把
好啦,到这里就介绍完初始c指针的基础点,此时你已经有了对付c指针的初步知识,有了初步了解。接下去,进入我们第二部分,进阶版本。
首先恭喜你成功看到了这里,实际上,第一部分比较简单,学的很浅接下来,介绍第二部分进阶了解c指针,带大家进一步去了解。希望大家能跟上节奏。这个部分我们来说说指针的高级主题
1.字符指针
在指针的类型中我们知道有一种指针类型为字符指针char*;这是很普遍的,很多人不理解为什么要放在这里,不急,接下去看。
之前我们都是这样子用字符指针的
但是我想说的是,字符指针不仅仅可以指向一个字符,还可以指向一个字符串
其实,这种写法是把字符串首字符h地址放在ps上,可以看我们的运行结果
在来看一看这个代码,是不是头脑更加清晰了,慢慢感受把,是不是对字符指针有了更深的认知。
有必要提醒一下,常量字符串是不能去改的,注意const的修饰,使语法更加标准一些
指针数组
上面也是已经提过,在来说一说,下面这种写法比较奇怪
也可以写成这样子
这就是指针数组的用处
数组指针
数组指针的定义?
看到这里是不是懵了,指针数组??数组指针??
数组指针是指针??还是数组??心里头一万只草泥马走过
答案是:指针
数组指针是一种指针,指向数组的指针,下面给大家好好推敲推敲,不要着急,慢慢来
parr存放的就是数组的地址,这就是数组指针
p1是整型指针,p2是数组指针,刚开始都是起始地址,结果相同,看看运行结果可知,都加1结果肯定不同,一个是整型指针,另一个是数组指针,结果肯定不同啊,可以看看运行结果
注意:&数组名取出的是整个数组的地址,不是首元素的地址!!
到了这里,有必要总结一下
数组名是首元素地址
但是有2个例外
1.sizeof(数组名)-数组名表示整个数组,计算的是整个数组大小,单位是字节
2.&数组名--数组名表示整个数组,取出的是整个数组的地址
希望大家牢记起来,不要老是一度认为数组名就是首元素地址,到时候错了都不知道怎么错的。
学了数组指针和指针数组我们来看看下面代码的意思
int arr[5];——整型数组
int*parr1[10]——整型指针数组
int(*parr2)[10]——数组指针,该指针能够指向一个数组,数组10个元素,每个元素类型都是int
int(*parr3[10])[5]——parr3是一个存储数组指针的数组,该数组能够存放10个数组指针,每个数组指针能够指向一个数组,数组5个元素,每个元素是int类型
数组传参和指针传参
写代码难免把数组和指针传给函数,函数该怎么设计呢
一维数组传参
二维数组传参
一级指针传参
二级指针传参
这些都要我们去注意了解,怎么去接收?这里就不展开讨论了。这里只是适当的给大家拓展一下
遇到的时候多加留意,不懂的也可以看看书籍相关知识,不过这个比较少用到,这里跟大家说说有这回事。
到了这里给大家梳理一下,以免大家开始乱了,实际上,理清思路有助于我们更好的记忆:
指针
一级指针,二级指针,数组指针
数组
一维数组,二维数组,指针数组
接下去,进入我们的函数指针
函数指针
指向函数的指针就叫函数指针。这些概念其实本质上是一样的
这便是函数指针,从上面代码我们还可以看到 函数名==&函数名,这是完全等价的,但是
数组名!=&数组名
pf就是函数指针变量,跟数组指针的表示方法非常类似
这里有必要加固一下表示的练习,如图所示
怎么调用呢,我们来实际看看
我们在来看一个东西,实际上Add == pf
这个结果也是8。
函数指针数组
怎么去理解函数指针数组呢,直接摆上我们的代码,里面有注释了,大家耐心观看,便会懂了,实际并没有那么玄妙
pfArr就是所谓的函数指针数组,当然,Add与Sub可以用pf1与pf2代替,到这里,你对它已经有了认识,认识到了概念,但是怎么用呢?有什么用,这里,非要讲用处的话,我可以举例子给大家看看,函数指针数组的运用,这里以简单的计算器加减乘除为例,为大家简单运用一下函数指针数组
这里大大简化了程序,你可以试试用switch语句去改写,你就知道这个变得多简洁了。
接下来,再给大家说一个东西
指向函数指针数组的指针
刚刚说的是函数指针的数组--数组
取出函数指针数组的地址
&函数指针数组
int(*p)(int,int);//函数指针
int(*p2[4])(int,int);//函数指针数组
p3 = &p2;//取出的是函数指针数组的地址
//p3就是一个指向函数指针的数组的指针
那么问题来了,p3怎么去定义呢
int(*(*p3)[4])(int,int) = &p2;
大家了解一下就ok了,不要重点研究,实际上指针就是在套娃,一直在套娃,这东西已经比较深入了。学不会也没关系。
回调函数
回调函数这块我本来不想说的,一方面是这篇博客写的时间已经用了很久,另一方面感觉没必要,没啥精力写了,但是既然到了这里,我还是介绍一下把,回调函数就是一个通过函数指针调用的函数。
通过函数指针调用,这里只是简单运用一下。
冒泡排序c语言实现_平凡的人1的博客-CSDN博客这里大家可以看看我之前写过的博客
我这里主要是想说一个函数qsort()//快速排序,实际上,关于用到函数自己却不懂的时候
我给大家推荐一款东西MSDN,查找相关函数怎么用,有什么参数,或者去一个网站,cplusplus.com,大家可以去看看
指针和数组试题
好啦,手工结束