ios从入门到放弃之C基础巩固-----多级指针、指针数组、函数指针

继续接着上一次的https://www.cnblogs.com/webor2006/p/15183259.htmlC基础往下学习,这次是C中的最后一块内容了----指针,争取用一二篇收尾了,然后赶紧进入OC的学习,不然感觉学了这么久,还是没有沾到IOS开发的边~~当然关于C的指针的内容也不少,最重要也最难,所以过好这一关也是很有必要的。

指针变量-基本概念:

关于指针变量是啥这里就不过多说明了,只要学过C的基本都有接触过,其实就是用一个变量来存放指针,这种变量称为指针变量。因此,一个指针变量的值就是 某个内存单元的地址或称为某内存单元的指针,而该指针变量的表示如下:

这里简单用程序来热个身,感受一下,我们知道平常定义普通变量是可以这样定义:

同样的,对于指针定义也类似,如下:

 对应的内存大概是这样的:

注意指针永远保存的是地址,而通常我们要修改变量的值是这样来修改的:

而有了指针变量之后,也可以通过指针的方式来修改它所指向变量的值,如下:

那么,有一个规律就你发现了:*p = num,以前你用num去访问的,以后全可以用*p来代替,这个规律对于指针的正确使用很重要。

指针-注意点:

1、指针只能保存地址:

这个一定要牢记,因为不经意间可能你就会这样写代码:

2、同一个变量可以有多个指针指向它:

这里木有疑问对吧,接下来再来修改:

为啥用p2这个指向来修改会影响p指针所指向的内容呢?因为其实p、p1、p2这三个指针都是指向的同一块地址,如下:

也就是num这个变量被多个指针所指向了。

3、指针的指向可以修改:

这代码木有啥疑问对吧,等于p指针目前指向的是变量a的地址,好,接下来继续修改:

是不是此时又可以将指针p的指向改成另一个变量b的地址了,再修改就是修改b变量的内容啦。

4、不要访问野指针:

关于这个注意事项只要了解指针的都知道:

对于这种情况,通常建议是给个NULL:

虽说也报错了,但是在程序阅读上会好一些,NULL其实就是0。

5、指针类型是什么类型,就只能指向什么类型的数据:

这程序木毛病对吧,接下来修改指针p的指向类型:

虽说改了类型,但是呢结果还是对的,接下来再来修改就不一定对喽,如下:

 

所以为了保证结果的正确性,记住指针类型是啥,其保存的类型就应该是它。

多级指针:

二级指针:

如果一个指针变量存放的又是另一个指针变量的地址,则称这个指针变量为指向指针的指针变量。也称为“二级指针”,如果理解了一级指针,其实对于二级指针也比较容易理解,比如定义一个一级指针:

对于p指针它不是也是一个变量么?那咱们就又可以定义一个指向它的指针,则就是指针的指针了嘛,如下:

其中可能看到"**charValuePP"这样的写法有点不那么好理解,这里拆解一下:

“**charValuePP = *(*charValuePP) = *(charValueP) = charValue”,而对于多级指针在使用时记住一个简单的原则既可:就是通过几颗星来存储的,就通过几颗星来访问,回到咱们的程序来理解:

多级指针:

知道了二级指针的用法,对于更多级的指针其实套路一样,比如:

指针为什么要分类型:

我们知道所有的指针变量都是内存地址,也就是8个字节的对吧,如下面:

其中%lu输出的是long unsigned数据类型无符号长整数或无符号长浮点数,那么有一个疑问就产生了,既然其长度都是8个字节,我们在定义指针时还指定具体类型干嘛呢?

下面来说明一下原因,其实有过对指针的了解对于这样的一个疑问基本都知道为啥,这里用例子来说明一下原因:

这里一切都很自然,接下来做一个骚操作:

那你认为其输出是多少呢?看下面,会惊掉你的下巴:

这里要想分析出其输出的原因,则需要从内存存储这块来进行,下面来分析一下:

首先存intValue,需要将1230转换成二进制:0000 0000 0000 0000 0000 0100 1100 1110,而根据之前https://www.cnblogs.com/webor2006/p/14115791.html所学习的变量存储原理:

此时它在内存的存储为(内存寻址是从高到低):

接下来存charValue,占一个字节:

而此时charValueP指针的指向是这样的:

它的指针类型是一个int,那这个内存结构也看不出来跟这个输出的值314929有啥关系呀,这里将314929转换成二进制就晓得啥关系了:

而取值咱们应该是取这四个字节,因为指针类型是int类型(其实这里就已经道出了为啥指针需要指明类型了),所以需要取4个字节,如下:

而由于内存寻址是从高到低,所以此时得到的二进制应该是“00000000 00000100 11001110 00110001”,发现新大陆木有,其实它就是:

通过这个小例子,是否能明白为啥在定义指针时需要指明数据类型了吧,其实也就是在取值时,系统会自动根据指针的类型来确定应该取多少字节中的值。

指针和数组:

关于指针与数组之间的关系,这里稍稍提一下,也是普遍都熟知的东东,数组名就是首元素地址,如下:

所以,可以把数组名直接赋值给一个指针变量,如下:

此时就可以使用指针的方式来访问数组元素了,比如:

而有了指针指向数组之后,其遍历方式又可以用:

指针和字符串:

由于字符串的定义也是一个字符数组对吧:

那么,同样可以用指针来进行字符串的保存,如下:

两者区别?

那通过数组来保存字符串和通过指针来保存字符串,它们之间有啥区别呢?其实是有区别的,如下:

但是!!!对于指针保存的字符串是不能修改的,如下:

这是因为数组保存的字符串存储在内存的栈中,而通过指针保存的字符串存储在常量区中。

注意事项:

在上面我们已经知道通过数组和指针保存字符串所在内存位置的不同了,这里还有另外一个特点:存储在栈中的变量有一个特点就是当作用域结束时系统会自动释放该变量;而存储在常量区中的值的特点是不会被释放,而且多个相同的字符串值对应的地址相同。回到程序上来理解:

另外关于重复声明的字符串,两者的表现也不一样,看程序:

这个细节不一定人人都清楚,需要了解一下。

指针保存字符串的使用场景:

那思考一下,对于指针保存字符串一般使用在什么场景呢?其实就是代码优化上面,先看一下传统的保存方式:

很明显这种方式每循环一次,就会申请释放一次内存,很明显性能不是太好,而此时改为用指针指向是最优的:

注意点:

对于字符串的输入来说,写法如下:

此时运行如下:

那如果换成指针的方式呢?试一下:

指针数组:

这个比较简单,敲一遍就行:

指针变量作用:

 指针其实也是一种数据类型对吧,而对于一个数据类型通常具有以下三个特点:

1、可以用来定义变量;

2、可以用来作为形参的类型;

3、作为函数的返回值;

对于第一、二点,这个没啥可说的,比较简单,重点是说第三点,这里是有一些坑的,如下:

 

你觉得输出是啥?运行一下:

呃,为啥用数组存储的字符串返回之后,咱们调用木有正常打印出来呢?还记得它存储的位置不----栈,出了作用域则会自动被释放,也就是:

 

所以,关于用数组和指针存储字符串之间的区别一定要明确哟~~

指向函数的指针:

接下来学习一个非常重要的指针,函数指针, 学好它在未来ios的学习中也会起到非常大的帮助使用,所以,这里从头到尾复习一遍。

初识函数指针:

为什么指针可以指向一个函数? 

函数作为一段程序,在内存中也要占据部分存储空间,它也有一个起始地址,即函数的入口地址。函数有自己的地址,那就好办了,我们的指针变量就是用来存储地址的。因此,可以利用一个指针指向一个函数。其中,函数名就代表着函数的地址。

定义使用:

1、无参无返回值:

 

这是传统函数调用的方式对吧,那如果改用指针来调用呢?其实是有套路的,由于这是第一个函数指针的学习,这里就啰嗦地来定义一下函数指针,掌握这个规律之后面对更复杂的函数都可以轻松应对。

  • 1、把要指向函数头拷贝过来

  • 2、把函数名称使用小括号括起来

  • 3、在函数名称前面加上一个*

  • 4、修改函数名称

    这是由于我们没有声明方法的参数,对于无参的函数,在定义函数指针时,需要用它来明确的说明一下:

是不是函数指针的定义比一般的指针定义要稍稍麻烦一些?是的,但是掌握了套路之后其实也很容易,接下来咱们就可以把这个指针指向咱们的函数进行调用了,如下:

这里注意,千万不要写成这样了:

而调用是这样的:

而对于数组指针来说:

 

同样的,对于函数指针来说testP是等于test的,所以,调用可以进一步简化:

2、无参有返回值:

 

定义是同样的套路:

调用也简单:

3、有参无返回值:

同样的套路来定义函数指针:

不过,这里的写法可以精简,对于形参名其实不用定义了,如下:

接下来调用一下:

4、有参有返回值:

函数指针定义:

套路都一模一样,接下来调用一下:

注意:

  • 由于这类指针变量存储的是一个函数的入口地址,所以对它们作加减运算(比如p++)是无意义的。

  • 函数调用中"(指针变量名)"的两边的括号不可少,其中的不应该理解为求值运算,在此处它 只是一种表示符号。

应用场景:

关于函数指针的应用场景,其实主要是体现在程序的扩展性上,这里简单演示一下,也是很经典的示例,比如不用函数指针的话要对两数进行运算就得定义多个,比如:

但是,有了函数指针的话,其实定义一个通用方法就可以了,如下:

像java8的Lambda表达式也能达到类似的功效,就是将函数提升到了一等公民了。

案例练习:

需求:

由于函数指针相对而言要稍稍麻烦一些,所以这里为了巩固,用一个小练习巩固一下,其需求为:

要求用户输入一段英文,将用户输入的英文单词所有的首字母变成大写。

比如:hello world ---> Hello World 

实现:

1、接受字符串的输入:

注意了!!!这里程序是有问题的,其实IDE也给出提示了:

所以,这里改为字符数组来接收了:

那。。咋办,此时需要换一个系统函数了,如下:

嗯,成功按我们的意愿输出了,但是!!!给了一个关于gets()函数的警告了:

 

关于这块可以参考https://blog.csdn.net/chienchia/article/details/41697797,这里就不过多说明了,反正面对咱们这个小案例是木问题的。

2、实现单个字母的大小写转换:

接下来调研一下如何将一个字母转成大写,在之前https://www.cnblogs.com/webor2006/p/14115791.html咱们其实实现了将一个大写字母变成小写字母,所以相反的,由小写变大写思路也一样,都是利用ascii码表,这里就不过多啰嗦了,具体实现一下:

 

其实可以使用指针来更改形参的值,如下:

3、实现字符串首字母的大小写转换:

一切前置条件都已经准备好了之后,接下来就可以进行整个逻辑的实现了,如下:

这里也很好解决:

最后,将字符串首字母转大写的逻辑封装成一个函数:

不过为了扩展,这个函数应该提供让用户来决定转大小写的权利,所以函数指针又派上用场了,如下:

总结:

至此,就把指针相关的东东基本过了一遍,为下一步OC的学习打下了非常坚实的基础。C语言这块还剩结构体,宏等一些很基础又非常重要的知识点木有复习完,那放下篇进行收尾喽。

posted on 2021-10-27 09:19  cexo  阅读(106)  评论(0编辑  收藏  举报

导航