指针的千层套路

概览

先是对指针有入门的理解(然后入土(bushi))

然后观赏一下指针联合数组的组合技

最后和函数指针亲密接触(通过函数指针来理解qsort库函数)

指针的入门理解

一、结合电脑来理解指针

1.内存和地址

电脑把内存中的数据读取至CPU处理,

数据的宿舍就是内存

就像宿舍有房间,内存也有内存单元,一个单元是一个字节的大小

就像宿舍有编号,内存单元也有自己的号码,这,就是地址

在c语言中,又将地址称作指针

总结一下内存单元的编号就是地址,叫做指针

2、编址的理解

电脑里硬件是咋相互传信号,一起工作的?

用“线”,就是物理意义上的线,地址总线,数据总线,控制总线等等

硬件会用“电线”,来编出超级多的地址,简称,编址(这是一种语言,就好比远古人类结绳记事,或者是摩斯电码)

 那,到底咋编址的呢?

线中传递两种信息,0和1,用电脉冲的有无来表示

所有线表示的信息,就是一个地址

举个例子

32位的机器有32根地址线,每根线都可以传递两种信息,它们通力合作来表示一个地址

不妨算一个简单的数学问题,所有线中信号排列组合,一共多少种?

二的三十二次方种,也就代表了二的三十二次方种的地址

这些地址的背后,就是内存里数据的房间号

我们用地址,可以找到数据们

二、指针变量

1、指针变量的创建和使用

创建:

变量创建的本质就是向内存申请空间

举个例子:

int *     pn  =  n

int *是指针变量的类型,指针变量有很多类型,char *,int * 等

pn是指针变量的名字

n是我所取地址的对象,换句话来说,就是我搞到手了n的地址,不是其他阿猫阿狗的

&取地址操作符,告诉电脑,老子要取地址了

使用:

对于指针变量的使用,就是根据这个地址,返回去找到内存中的数据

  *  解引用符就是实现这个操作的工具

  *pn 就是根据pn这个地址,找到n

2、指针变量的大小

 指针变量的大小和类型无关!!!只和平台有关

32位的机器下(即x86平台)表示有32根线,32个比特位,换算一下,是四个字节

64位的机器,则是八个字节

地址的大小由平台决定

3、指针变量的类型

那大小一样,类型的区分有啥用呢??

指针类型用来决定,在进行计算等操作时,电脑需要访问几个字节

解引用操作:

char* pn=&n

*pn=0               这里在内存里只改变一个字节变成0(毕竟作为char的n就只有这么大)

int *pn=&n

*pn=0              这里改变四个字节

指针加减整数:

char* pn=&n

pn+1                  地址跳过一个字节,找到下一个元素

int *pn=&n

pn+1                   地址跳过四个字节,找到下一个元素

tips:这里介绍一个无具体类型的指针 void*

它不可以进行解引用和加减整数的操作

4、野指针

指针指向的位置未知

野指针是很危险的东西

所以我们使用指针初始化来紧急避险

举个例子:

char* pn= NULL(空指针)

赋一个空指针就可以

指针和数组

一、一维数组

1、数组名的理解

数组名是一个地址,一个指针,是数组首元素的地址

但是,有两个例外:

1、sizeof(arr)这里计算整个数组的大小,单位是字节

2、&arr这里代表取出了整个数组的地址

       所以   &arr+1会跳过整个数组

2、使用指针访问数组元素

法1:我们不妨先搞到数组各元素的地址:

int * p=arr

p+i 就是下标为i的元素的地址

*(p+i )就找到了元素

法2:利用arr数组名也可以

arr[i]和*(arr+i)其实完全等价,也是对元素的访问

3、一维数组传参本质

一维数组传参本质其实是传递数组首元素的地址

举个例子:

创建一个自定义函数: void test(int  arr[  ] )

int  arr[] 表面上是数组形参,实际上是希望接收一个地址

我们在主函数中写实参写     arr(代表首元素地址)即可

番外:

1、二级指针

这里是一个套娃:

int * *  ppn=&pn

一级指针pn存放n的地址,二级指针ppn存放了一级指针pn的地址

int *代表了取地址的对象是一个指针pn

*  代表了新创建的变量ppn是指针类型

以此类推 还有三级 四级指针等等

2、指针数组

注意:指针数组是数组,数组里元素的类型是指针

int * arr[  7 ] ={&a,  &b,  &c }

 int * 代表数组中存放的类型是整型指针

printf("%d",arr[0])   这样只能打印地址,

要是想打印a,就要写*arr[0]

3、字符指针变量

存放字符地址的变量就是字符指针变量

char *   =  "abcdef"

abcdef是常量字符串,不可以被修改

p代表常量字符串中首元素a的地址

可以把字符串看成数组,里面的元素是字符

二、二维数组

1、数组指针变量

是指针,来存放数组的地址

int arr[10]={0}

int (  *  pa   ) [  10  ]=&arr     用于存放arr的地址

紫色的部分(去掉了数组指针的名字)就是数组指针的的类型

tips:去掉变量名,就得到变量类型

再举个例子:char (*pa)[5]=&arr

char (*)[5]就是类型

2、二维数组传参的本质

二维数组传参本质其实是传递数组首元素的地址

举个例子:

形参:用数组名代表地址 int arr[  ] [  5  ]    这个5不可以省略
或者也可以写成 地址的形式:int ( *p )[  5   ]  这是一个数组指针变量

这里注意 二维数组的每一行都是一个元素,而这一行,就是一个一维数组

所以 二维数组首元素的地址,是一个数组指针变量

函数指针变量

一、函数指针变量基础

1、函数指针变量的创建

啊?函数也有地址吗?是的!

函数指针变量就是存放函数地址的,便于通过地址,找到函数来引用

举个例子:

假设一个叫add的函数,他的地址有两种表示方法:

add和&add

假设add函数的定义:int add(int x,int y)

那么函数指针变量:int (*pa )( int,int )=add     这里只要写形参的类型

再来一次去掉变量名,就得到变量类型,这里的类型是:int (*)( int,int )

对这个函数进行使用:

 int ret=(* pa ) ( 2  ,  3 )两个实参

2、函数指针数组

函数指针数组,是数组,这个数组里面的元素是函数指针

在此之前,我们回忆一下我们是如何创建整型数组的:

int  arr 

int 是数组中元素的类型,arr是数组名 

ok  知道了数组名和数组元素类型是最重要的,我们来小试牛刀:

int (   *  pfArr [ 4 ] )(int,int )

啊?!为啥完全不一样???

不要紧张 我们一部分一部分看

int (   *  pfArr [ 4 ] )(int,int)

黄色是数组名

红色是元素类型,是函数指针的类型,而这个函数会有两个int类型的形参,4代表这个数组可以放四个函数的地址(即四个元素)

二、函数指针的实战

1、回调函数

即通过函数指针调用的函数

就是在一个函数中调用另一个函数的地址来使用该函数

举个例子:回调实现一个计算器

加减乘除四个函数                      calc函数调用黄色部分                              主函数中调用calc函数

calc函数就像是中间商,通过地址,来使唤加减乘除这四个函数

这样的回调,大大减少了重复书写代码的需要,方便万岁

2、对qsort函数的理解和使用

qsort库函数的大优势是可以实现任意类型的排序,装逼来说就是泛型编程

我们来看一下这个库函数的定义:

void  qsort  (    void*base,      size_t num,   size_t size int (*compare)(const void * e1,const void *e2)  )

void表示函数返回值的类型是void

void*base 存放数组的第一个元素的地址

size_t num数组里元素的个数

 size_t size 数组里一个元素的长度,以字节为单位(这是达成泛型编程非常重要的工具)

我们以字节这个较小的基础单位来进行比较排序,就可以应对各种类型

举个例子:这里用冒泡排序实现了qsort函数中排序的模仿:

int (*compare)(const void * e1,const void *e2)一个函数指针,存放一个函数的地址,用于比较两个元素,这个函数有两个类型是const void *的形参,用于字节之间的交换

tips:qsort函数默认排列成升序

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值