C语言进阶第二篇【指针进阶】

 ✅作者简介:大家好我是@每天都要敲代码,一位材料转码农的选手,希望一起努力,一起进步!
📃个人主页:@每天都要敲代码的个人主页
💬在我们学习的过程中,肯定需要刷题,巩固所学知识点;给大家推荐一款模拟面试、刷题神器,从基础到大厂面试题👉点击跳转刷题网站

前言:Hello!我是@每天都要敲代码。今天就让我们一起学习一下指针的进阶,指针可算是C语言中的重中之重,我们一定要学好指针这一课;这里我也会尽可能详细的说明每个知识点。

目录

引言:

1.字符指针 char*

1.1 常见的两种使用方式

1.2 经典面试题

2. 指针数组    int* arr[10]

2.1 一维数组中的应用:

2.2 二维数组中的应用:

3. 数组指针  int (*p)[10]

3.1 数组指针的定义

3.2 &数组名 VS 数组名

第一种情况:

第二种情况:

第三种情况:

3.3 数组指针的使用

3.3.1 在一维数组的使用

3.3.2 在二维数组的使用

4. 数组参数、指针参数

4.1 一维数组传参

4.1.1 常见普通数组传参

         4.1.2 指针数组传参

4.2 二维数组传参

4.3 一级指针传参

4.4 二级指针传参

5. 习题补充篇

5.1 无符号数和有符号数比较大小

5.2 求Sn=a+aa+aaa+aaaa+aaaaa的前五项之和

5.3 求出0-100000之间的所有“水仙花数”并输出

5.4 指针练手 

5.5 打印菱形

5.6 喝汽水问题

5.7  调整数组使奇数全部都位于偶数前面

5.8 发生截断的例题

5.9 杨辉三角

5.10 谁是killer(抽象问题转换成代码)

5.11 我是第几名(抽象问题转换成代码)

引言:

我们在初级阶段的《指针》章节已经接触过了,我们知道了指针的概念:
1. 指针就是个变量,用来存放地址,地址唯一标识一块内存空间。
2. 指针的大小是固定的4/8个字节(32位平台/64位平台)。
3. 指针是有类型,指针的类型决定了指针的+-整数的步长,指针解引用操作的时候的权限。
4. 指针的运算。

这个章节,我们继续探讨指针的高级主题。

1.字符指针 char*

1.1 常见的两种使用方式

我们来一起解析一下常量字符串:代码 const char* ps = "hello bit." 

我们这样看可能会觉得把字符串 hello bit. 放到字符指针 ps 里了;实际上并不是,我们通过调试的过程发现:ps的地址是和h的地址是一样的!

 所以不要被误导以为是把字符串 hello bit. 放到字符指针 ps 里了;本质是把字符串 hello bit. 首字符(h)的地址放到了ps中。

1.2 经典面试题

这里最终输出的是: 

为什么是这个结果呢?

1、这里str3和str4指向的是一个同一个常量字符串C/C++会把常量字符串存储到单独的一个内存区域,当几个指针指向同一个字符串的时候,他们实际会指向同一块内存

2、但是用相同的常量字符串初始化不同的数组的时候就会开辟出不同的内存块

3、所以str1和str2不同,str3和str4不同。

通过调试进行验证:

2. 指针数组    int* arr[10]

有了前面的学习,我们都已知晓:

整型数组 int arr[5];存放整型的数组,数组里的每个元素都是int类型 。

字符数组 char arr[5];存放字符的数组,数组里的每个元素都是char类型 。

指针数组 int* arr[10];存放指针的数组,数组里的每个元素都是int*类型。

指针数组是一个存放指针(地址)的数组,本质上还是数组。例如:int* arr[3];数组有3个元素,每个元素是int*(整型指针)类型。既然本质上还是数组,那如何在数组中应用呢?

2.1 一维数组中的应用:

实际上在移位数组的应用并没有什么应用场景,这里只做了解就可以;下面直接上代码

2.2 二维数组中的应用:

在一维数组中,我们是把一个元素取地址作为指针数组里的元素;现在我们不妨把一个数组(数组名就是首元素地址)作为指针数组里面的元素。

 怎么理解呢?我们不妨先画图理解一下:

下面看具体代码: 

 其实三种打印方式本质是相同的:我们都知道*(arr+i)就等价与ar[i];所以arr[i][j]===>

*( arr[i] + j) ===> *( *(arr+i) + j)

3. 数组指针  int (*p)[10]

3.1 数组指针的定义

我们已经熟悉:
整形指针: int * p; 指向整形数据的指针。

浮点型指针: char * p; 指向浮点型数据的指针。

数组指针:int (*p) [10];本质是一种指针;指向数组的指针,int(*)[10]就是它的类型。

要注意:[ ]的优先级要高于*号的,所以必须加上()来保证p先和*结合

我们不妨在举一个例子来练手,如果是double* d[5],写成数组指针的形式是怎样的呢?照葫芦画瓢: double* (*pd)[5] = &d

3.2 &数组名 VS 数组名

第一种情况:

    arr是数组名代表的是首元素的地址,&arr代表的是整个数组的地址;我们惊奇的发现它们的地址居然是一样的。为什么呢?&arr我们取出的是数组的地址,那数组的地址不也有起始地址?所以这两个位置是同一个位置!值一样,但是类型意义不同。比如:char c = 'a',ASCII码值是97;int i = 97,它们的值都是97,但是意义完全不同!

 第二种情况:

第2种情况实际是和前面一样的,只不过这次我们把arr和&arr交给了指针来处理;我们最终打印的结果也是相同的 。

第三种情况:

从前面两种情况,我们是看不出arr和&arr有什么本质的区别的,它们的地址相同;那么我们把指针+1又有什么样的结果呢?

我们可以得到:p1指针是整型指针,p1+1跳过4个字节;p2是数组指针,p2+1应该跳过一整个数组40(是28这是16进制,十进制就是40);这就体现出它两的区别了!

复习:

数组名是数组首元素的地址,但是有两个例外:

1.sizeof(数组名) ----数组名表示整个数组,计算的是整个数组的大小,单位是字节

2. &数组名----数组名表示整个数组,取出的是整个数组的地址

3.3 数组指针的使用

3.3.1 在一维数组的使用

很别扭,我们一般很少这样写代码:

3.3.2 在二维数组的使用

二维数组的数组名表示首元素的地址;二维数组首元素是:第一行

数组指针相当于我们在打印二维数组时,又多了一种方法!

区分:

int arr[5];整型数组

int* arr[5] ;整型指针的数组===》指针数组

int (*parr)[10];数组指针,该指针能够指向一个数组,数组10个元素,每个元素的类型是int

拓展:int (*parr[10])[5];数组指针的数组,该数组能够存放10个数组指针,每个数组指针能够指向一个数组,数组5个元素,每个元素是int类型

4. 数组参数、指针参数

我们在设计函数时,参数既可以写成数组,也可以写成指针!!!

4.1 一维数组传参

4.1.1 常见普通数组传参

   

     前面我们都已经学习过了,我们接收参数的时候,既可以写成数组的形式,也可以写成指针的形式;本质上都是把数组首元素的地址传过去!

4.1.2 指针数组传参

arr2是一个指针数组:存放int*的数组;

1、前两种,本来是指针数组的形式,我们就以指针数组的形式接收,当然没问题;

2、那如何利用指针接收呢?这个数组有20个元素,每个元素对应的是int*;数组名表示首元素的地址,就是int*的地址一级指针的地址我们用二级指针接收当然也没问题。

4.2 二维数组传参

对于二维数组传参,我们还是要分为数组形式和指针形式接收:
1、数组形式接收:二维数组传参,我们以二维数组的形式接收完全没问题,需要注意的是:我们只能省略行,不能省略列!所以1,2都是可以的,3不行 。

2、指针形式接收:二维数组传参,传的是首元素的地址,也就是一维数组的地址,我们用一级指针来接收,肯定是不行的,所以4no;5是指针数组,根本就不是指针的形式,no;首元素的地址代表第一行的地址,我们用指向一维数组的指针来接收是刚好可以的,所以6,数组指针是ok的;传的是首元素的地址,也就是一维数组的地址,我们用二维数组接收也是不对的,我们要弄清楚我们传过去的是什么,在考虑用什么接收!所以对于二维数组传参,我们如果用指针的形式接收只能用数组指针的形式。

4.3 一级指针传参

我们还是根据传过来本质是什么形式,我们就以什么样的形式 来接收!传过来的是一级指针,所以我们用一级指针来接收肯定是没问题的。

思考:反过来想,如果我们以一级指针的形式接收参数,那么传参的时候是传什么样的呢?

例如void print(char* p)来接收,我们传参?例如:char ch = 'w',我们传ch的地址&ch是可以的;char* p = &ch,这样传一级指针p也是可以的!

4.4 二级指针传参

二级指针传参,我们就用二级指针接收;我们要找到或者修改原来的数据,也要解应用两次。

思考:反过来想,如果我们以二级指针的形式接收参数,那么传参的时候是传什么样的呢?

例如:上面的例题,我们已知:void test(int** ptr)来接收,我们传参?

1、首先我们就传二级指针,二级指针接收,肯定是没问题的;

2、或者传一级指针的地址,二级指针接收;那么还有什么形式呢?

3、传一级指针的数组,如果我们写成指针数组的形式:int* arr[10],就传arr可以吗?前面是指针数组,里面存放的都是int*指针,arr又代表首元素的地址,最终本质也就是一级指针的地址,不也是可以的!

5. 习题补充篇

今天的指针学习就暂时到这里,最主要的就是要理解指针数组数组指针的区别和用法!下面让我们补充一些课外题目,发散一下思维吧。

5.1 无符号数和有符号数比较大小

在做下面这道例题之前,我们要先知道两个知识点

1.sizeof这个操作符,算出的结果返回的类型是无符号整型unsigned int

2.无符号数和有符号数比较大小,要先把有符号数转换成无符号数,在比较大小

按照我们一般的理解:i = -1,sizeof(i) = 4;最终结果应该是-1 < 4,打印出来<;但最终打印出来的结果确是>;很难让人接受。

解析:

1、这里我们先要理清局部变量全局变量未初始化的区别对于全局变量没有初始化,默认值为0;对于局部变量未初始化,默认是一个随机值!

2、sizeof(i)结果是4,此时的4实际上是无符号数;i--结果是-1,此时的-1是有符号数

3、无符号数和有符号数比较大小,要先把有符号数转换成无符号数,在比较大小。所以要先把有符号数-1转换成无符号数:

所以最终打印出来的结果是:>

5.2 求Sn=a+aa+aaa+aaaa+aaaaa的前五项之和

其中a是一个数字,我们不妨就拿一组具体的数来分析:2+22+222+2222+22222

 有了规律,我们就写出一个通用的代码!在不考虑溢出的情况下:求任何一个数n的前m项都是可以的:

5.3 求出0-100000之间的所有“水仙花数”并输出

"水仙花数"是指一个n位数,其各位数字的n(n是位数)次方之和刚好等于该数本身;我们之前接触的大都是三位数的水仙花数,例如:153 = 1^{3}+5^{3}+3^{3};今天我们写一下0-100000之间的!

5.4 指针练手 

这道关于指针的题目其实很简单,我们只要把指针理解了就很容易得出答案:

5.5 打印菱形

例如:

 我们首先要分两部分进行打印,上面一部分下面一部分:

 我们根据图形很容易就能写出来,需要注意的是:假如要打印的大小n=4,我们上半部分打印4行,下半部分只需要打印3行就可以了!

5.6 喝汽水问题

1瓶汽水1元,2个空瓶可以换一瓶汽水,给20元,问最终可以换多少瓶汽水?这也是一道找规律题;我们还是画图进行分析:

 有了上面的逻辑分析,我们理清一下思路,就能写出代码了!

5.7  调整数组使奇数全部都位于偶数前面

比如:1 2 3 4 5 6 7 8 9 10打印出来的结果就是前面全是奇数,后面全是偶数;当然顺序不重要!思路:我们的思路就是左右两头开始;左边找偶数,右边找奇数,然后交换一下位置就可以

两种特殊情况也要考虑到:

 具体代码:

 

5.8 发生截断的例题

 

 这道题的结果是什么?初看感觉都是300;但是实际上却不是,我们还是先分析一下:

我们已经分析出来c的值是44,那么a+b的值为啥没有越界,发生截断呢? 

原因是:a+b没有放到c里面,计算完结果会直接放到内存里,不会发生截断;

              而c=a+b,是把a+b的结果放到了char类型的c里,会发生截断,再放入内存

5.9 杨辉三角

打印杨辉三角和打印菱形算是一类题目,我们不要上来就想着去打印,要先分析这个图形是怎么后构成的。比如杨辉三角:

1、无非就是第一列(j==0)和对角线(i==j) 全为1;

2、剩下的从第三行第二列开始(i=2,j=1)当前数是上一行两数的和;

分析清楚了,代码就很好写出来了

 

5.10 谁是killer(抽象问题转换成代码)

题目:判断谁是凶手,其中3个人说真话,一个人说假话
A说:不是我
B说:是C
C说:是D
D说:C在胡说

我们就根据他们说的话作为判断条件;就假设他们每一个人说的话都正确:

如果A说真话,说明杀手不是A

如果B说真话,说明杀手是C

如果C说真话,说明杀手是D

如果D数真话,说明杀手不是D

最终我们在带入每个人说的话进行验证,看是否这个人说的话,与另外两个人说的也是对应的;换而言之,只与一个人的话起冲突;那么这个人说谁是小偷,谁就是小偷!

最终我们得到的是姓名是一个字符,所以定义为字符型

5.11 我是第几名(抽象问题转换成代码)

题目:5位运动员参加了10米台跳比赛,有人让他们预测比赛结果:每个人说的话只有一半真一半假
A选手说:B第二,我第三
B选手说:我第二,E第四
C选手说:我第一,D第二
D选手说:C最后,我第三
E选手说:我第四,A第一

最终我们得到的是名字是整型,所以定义为整型

我们不妨就嵌套5个for循环,根据他们说的话一半真一半假作为筛选条件;最后在根据其他限制条件再次筛选掉那些名词重复的:

 最终得到我们想要的结果:

总结:以上就是今天要学习的的内容,我们主要讲了字符指针、指针数组和数组指针的内容。这些也需要我们去重点掌握!指针的内容很多,也很重要,需要我们经常去练习才能掌握;下一篇我们将继续学习指针部分:函数指针、函数指针数组等精彩内容

结束语

今天的分享就到这里,想要提升编程思维的,快快去注册牛客网开始刷题吧!各种大厂面试真题在等你哦!

 💬刷题神器,从基础到大厂面试题👉点击跳转刷题网站

评论 49
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

@每天都要敲代码

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值