初识C语言指针(4)

目录

1. 字符指针变量

2. 数组指针变量 

3. ⼆维数组传参的本质

4. 函数指针变量

5. typedef 关键字 

6. 函数指针数组 

结语


1. 字符指针变量

字符指针变量就是存储字符或字符串首字符地址的变量,字符指针变量有2种使用方式。

最常用的使用方式:

​
​
int main()
{char ch[] = "hello bit.";char* pc = &ch;return 0;
}​​

还有一种使用方式:

int main()
{const char* pstr = "hello bit.";printf("%s\n", pstr);return 0;
}

第一种使用方式大家都知道,是将一个字符串中第一个字符(字符数组中第一个元素)的地址存放在字符指针变量中。

但是第二种 字符指针 pstr 变量中存放的是这一整个常量字符串的地址吗?还是把字符串 hello bit .放到字符指针 pstr ⾥了?其实都不是,实际上也是把字符串 hello bit. ⾸字符(h)的地址放到了pstr中。

既然这两种方式存放的都是字符串第一个字符的地址,那么它们有什么差别呢?

 总结:

这两种方法的相同点:它们存放的地址都是字符串首字符的地址,并且字符串都是一块连续的空间

这两种方法的不同点:第一种方式存放的是字符数组首元素的地址(字符串首字符的地址),数组的元素是可以修改的。第二种方式存放的是常量字符串首字符的地址,而常量字符串是常量,常量是不能被修改的

下面我们来看一道来自《剑指offer》中⼀道和字符串相关的笔试题:

int main()
{char str1[] = "hello bit.";char str2[] = "hello bit.";const char* str3 = "hello bit.";const char* str4 = "hello bit.";if (str1 == str2)printf("str1 and str2 are same\n");elseprintf("str1 and str2 are not same\n");if (str3 == str4)printf("str3 and str4 are same\n");elseprintf("str3 and str4 are not same\n");return 0;
}

运行结果:

可以发现 str1 和 str2是不相等的,虽然str1 和 str2中存放的内容是一样的,但是它们两个是不同的数组,既然是两个不同的数组,存放的地址自然就不一样。

而str3 和str4是相同的这是为什么呢?这是因为str3 和 str4中存放的都是字符串常量首字符的地址,而字符串常量是不能修改的,并且有const进行修饰,所以当字符串常量被创建时,字符串常量首字符的地址先赋给了str3,当创建str4变量时,此时计算机发现已经有了一个一模一样字符串常量,就不会重新创建新的字符串常量,则是将原来创建好的字符串常量首字符的地址也赋给了str4,所以此时str3 和 str4指向的是同一个字符串常量。

2. 数组指针变量 

整形指针变量存放的是整形变量的地址,字符指针变量存放的是字符变量的地址,那么数组指针变量自然就是用来存放数组的地址,能够指向数组的指针变量。

注意:这里存放的是整个数组的地址,而不是数组首元素的地

数组指针变量解析: 

int main()
{int arr[10] = { 0 };int (*p)[10] = &arr;return 0;
}int(*p)[10] = &arr;
|    |   |
|    |   |
|    |   p指向的数组(arr)的元素个数
|    | 
|	 p是数组指针的变量名
|
p指向的数组(arr)的元素类型

 那么此时数组指针变量p的类型是什么呢?如:int a=10中,a变量的类型是int 类型,char ch=' a ’中ch的类型是char 类型,我们发现好像去掉变量名就是该变量的类型,所以数组指针变量p的类型就是 int (*)[10]

3. ⼆维数组传参的本质

在过去我们有⼀个⼆维数组的需要传参给⼀个函数的时候,我们是这样写的:

void test(int a[3][5], int r, int c)
{int i = 0;int j = 0;for (i = 0; i < r; i++){for (j = 0; j < c; j++){printf("%d ", a[i][j]);}printf("\n");}
}
int main()
{int arr[3][5] = { {1,2,3,4,5}, {2,3,4,5,6},{3,4,5,6,7} };test(arr, 3, 5);return 0;
}

这⾥实参是⼆维数组,形参也写成⼆维数组的形式

但是,根据数组名是数组⾸元素的地址这个规则,实际上我们传的是二维数组首元素的地址,而⼆维数组其实可以看做是每个元素是⼀维数组的数组,也就是⼆维 数组的每个元素是⼀个⼀维数组。那么⼆维数组的⾸元素就是第⼀⾏,是个⼀维数组。

所以形参在接收的时候我们应该用数组指针变量接收:

void test(int(*p)[5], int r, int c)
{int i = 0;int j = 0;for (i = 0; i < r; i++){for (j = 0; j < c; j++){//printf("%d ", *(*(p + i) + j));第一种打印方式//printf("%d ", (*(p + i))[j]);第二种打印方式printf("%d ", p[i][j]);//第三种打印方式}printf("\n");}
}
int main()
{int arr[3][5] = { {1,2,3,4,5}, {2,3,4,5,6},{3,4,5,6,7} };test(arr, 3, 5);return 0;
}

        第一种打印方式该如何理解呢?首先p此时是二维数组首元素的地址(第一个一维数组的地址)那么*p就是第一个一维数组首元素地址,*(p+0)就是第1个一维数组首元素地址,那么*(p+0)+0就是第1个一维数组的首元素地址,*(*(p+0)+0)就是第1个一维数组的首元素。所以*(*(p+i)+j)就是第 i +1个一维数组中的 j+1个元素。而第2种和第3种方式在编译器运行时都会转换成第一种打印方式,在前面的博客中有讲过。

https://blog.csdn.net/2402_86304740/article/details/141467527icon-default.png?t=N7T8https://blog.csdn.net/2402_86304740/article/details/141467527总结:⼆维数组传参,形参的部分可以写成数组,也可以写成指针形式。

4. 函数指针变量

其实函数也是有地址的,函数指针变量就是⽤来存放函数地址的,并且能够通过地址调⽤函数。

函数指针变量解析:

int Add(int x, int y)
{return x + y;
}int main()
{int(*pf3)(int x, int y) = &Add;//x和y写上或者省略都是可以的return 0;
}
int (*pf3) (int x, int y)
|	   |	------------
|      |		  |
|      |		  pf3指向函数(Add)的参数类型和个数的交代
|      |
|	  (pf3)函数指针变量名
|
pf3指向函数的返回类型int (*) (int x, int y) //pf3函数指针变量的类型

那么我们该如何通过指针调用函数呢?其实&函数名和函数名拿到的都是函数的地址,所以&函数名等价于函数名,此时将&Add赋给了ps3,所以通过指针ps3也可以调用Add函数。

5. typedef 关键字 

typedef 是⽤来类型重命名的,可以将复杂的类型,简单化。

例如:

6. 函数指针数组 

数组是⼀个存放相同类型数据的存储空间,我们已经学习了指针数组,那要把多个函数的地址存到⼀个数组中,那这个数组就叫函数指针数组。

那函数指针的数组如何定义呢?

上面我们学过函数指针变量的定义,那么函数指针数组的定义就是在函数指针的变量名旁边加上数组的大小即可 ,类型为函数指针类型即 int (*) (int ,int),那么该怎么调用数组中的函数呢?

我们也是通过数组下标对数组中元素进行调用。

接下来我们通过函数指针数组完成一道练习:制作一个计算器

int add(int x, int y)
{return x + y;
}
int sub(int x, int y)
{return x - y;
}
int mul(int x, int y)
{return x * y;
}
int div(int x, int y)
{return x / y;
}
void print()
{printf("*****************************\n");printf("****《1. add 》《2.sub 》****\n");printf("****《3. mul 》《4.div 》****\n");printf("********《 0. exit 》********\n");printf("*****************************\n");
}
void sent(int(*p)(int, int))
{int x = 0;int y = 0;printf("请输入两个操作数:");scanf("%d %d", &x, &y);int ret = p(x, y);printf("%d\n", ret);
}
int main()
{int input = 0;int (*p[5])(int, int) = { 0,add,sub,mul,div };do{print();printf("请选择:");scanf("%d", &input);if (input >= 1 && input <= 4){sent(p[input]);}else if (input == 0){printf("退出成功\n");}else{printf("选择失败\n");}} while (input);return 0;
}

首先我们将完成加减乘除的4个函数的地址用函数指针数组存储起来,然后打印菜单,通过输入 input 的值进行选择需要进行哪种运算,如果选择了1,(p [ 1 ]存放的是add函数的地址)那么就进行加法运算,然后将 (p[ 1 ]) 加法函数(add)的地址传给 sent 函数,sent 函数的形参部分用函数指针接收实参(p[ 1 ])传过来的的地址,随后输入计算的数字,通过接收到的函数的地址,调用函数,完成数值的计算,最后将计算的值返回到主函数,打印出来。如果不需要计算了,输入0即可退出程序。这道题非常巧妙的体现了函数指针数组的便捷性。

结语

以上就是本章的所有内容,本章的内容可能需要一定的时间进行消化,所以大家可以反复阅读,一定会有所收获。你们的点赞和关注就是小新创作的动力来源,在此感谢大家的观看,谢谢大家!!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/408824.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Datawhale X 李宏毅苹果书 AI夏令营(深度学习入门)task3

实践方法论 在应用机器学习算法时&#xff0c;实践方法论能够帮助我们更好地训练模型。如果在 Kaggle 上的结果不太好&#xff0c;虽然 Kaggle 上呈现的是测试数据的结果&#xff0c;但要先检查训练数据的损失。看看模型在训练数据上面&#xff0c;有没有学起来&#xff0c;再…

智能手机摄影综评:品牌联名与自建影像品牌的战略分析

随着智能手机摄影技术的飞速发展&#xff0c;各大厂商不仅与知名摄影品牌展开合作&#xff0c;还通过自建影像品牌来提升产品的摄影能力和品牌形象。本文将重点分析小米、华为、荣耀、OPPO、Vivo和苹果在摄影品牌联名与自建影像品牌方面的战略&#xff0c;探讨这些策略如何影响…

【案例55】WebSphere非root用户启动方案

问题背景 很多项目为了安全因素考虑&#xff0c;想让在Linux服务器中启动的程序都用非root用户启动。 解决方案 创建用户和组 现在我们用 root 用户登录&#xff0c;并创建用户和组。 ##创建用户 [rootnc-test ~]# useradd wasadmin##修改密码 [rootnc-test~]# passwd was…

Python优化算法16——鲸鱼优化算法(WOA)

科研里面优化算法都用的多&#xff0c;尤其是各种动物园里面的智能仿生优化算法&#xff0c;但是目前都是MATLAB的代码多&#xff0c;python几乎没有什么包&#xff0c;这次把优化算法系列的代码都从底层手写开始。 需要看以前的优化算法文章可以参考&#xff1a;Python优化算…

【学习笔记】技术分析-华为智驾控制器MDC Pro 610分析

华为的智能驾驶控制器一直在迭代&#xff0c;和网络上广泛披露的早期MDC 610相比&#xff0c;华为 MDC Pro 610 智能驾驶控制器&#xff0c;现在的样品设计采用了海思的双系统级芯片 (SoC) 提高了处理能力&#xff0c;三星的存储模块为无缝数据处理提供了充足的内存&#xff0c…

一分钟制作电子版的招生简章

​在当今信息化社会&#xff0c;快速、高效地传播信息显得尤为重要。招生简章作为学校、机构招生的重要宣传材料&#xff0c;其电子版制作更是需要简洁明了、吸引眼球。一分钟你就能制作出一份精美的电子版招生简章。让我们一起来看看&#xff0c;如何实现这一目标。 1.要制作电…

Linux 可视化管理工具:Webmin

&#x1f600;前言 在 Linux 系统的运维管理中&#xff0c;命令行界面&#xff08;CLI&#xff09;是主要的操作方式。然而&#xff0c;对于许多系统管理员或开发者来说&#xff0c;使用 CLI 进行管理和维护任务并不总是最直观或最方便的方式。为了简化操作并提高效率&#xff…

今天你City了吗?维乐Angel Revo带你穿梭都市自由随风~

当7月的热浪在都市中翻滚&#xff0c;你是否渴望逃离钢筋水泥的束缚&#xff0c;寻找一片属于自己的绿意盎然&#xff1f;今天你City了吗&#xff1f;快带上VELO Angel Revo一起抓住夏日的尾巴&#xff0c;用一场骑行与这座城市的风景共舞&#xff01;      轻巧出行&#…

面向对象编程:深入PHP的封装、继承和多态性!

文章目录 面向对象OOP的核心概念定义类、创建对象构造函数和析构函数访问修饰符继承方法重写接口和抽象类静态方法和属性魔术方法 错误处理错误处理概述错误级别异常处理自定义异常设置错误处理忽略错误错误日志断言 总结 面向对象编程&#xff08;OOP&#xff09;是一种编程范…

海绵城市雨水监测系统简介

海绵城市雨水监测系统主要有&#xff1a;数据采集、无线数据传输、后台云服务、终端平台显示等部分组成。系统通过前端数据采集水质&#xff08;ss\cod\浊度、PH等&#xff09;、雨水雨量、流量、水位、土壤湿度、气象等数据。通过无线数据传输通讯&#xff08;4G、5G、以太网、…

洞见数据价值,激活组织活力,让决策更精准的智慧交通开源了。

智慧交通视觉监控平台是一款功能强大且简单易用的实时算法视频监控系统。它的愿景是最底层打通各大芯片厂商相互间的壁垒&#xff0c;省去繁琐重复的适配流程&#xff0c;实现芯片、算法、应用的全流程组合&#xff0c;从而大大减少企业级应用约95%的开发成本。用户只需在界面上…

人工智能在专业领域的斗争

介绍 ChatGPT 等大型语言模型 (LLM) 在用自然语言讨论一般话题的能力方面令人印象深刻。然而&#xff0c;他们在医学、金融和法律等专业领域却举步维艰。这是由于缺乏真正的理解&#xff0c;并且注重模仿而不是智力。 大语言模型正处于炒作的顶峰。由于能够用自然语言回答和讨…

python库(20):Jsonschema库描述JSON数据的规范

1 Jsonschema简介 在当今信息时代&#xff0c;数据规范与交换变得越来越重要&#xff0c;JSON&#xff08;JavaScript Object Notation&#xff09;作为一种轻量级的数据交换格式&#xff0c;被广泛应用于网络通信与前后端数据交互。 JSON Schema是一种用于描述JSON数据的规范…

力扣2025.分割数组的最多方案数

力扣2025.分割数组的最多方案数 哈希表 前缀和 用两个哈希表分别存元素(之后会遍历)左侧和右侧的前缀和 typedef long long LL;class Solution {public:int waysToPartition(vector<int>& nums, int k) {int n nums.size(),ans 0;vector<LL> sum(n);unor…

【Linux系列】SH 与 BASH 的区别:深入解析与使用案例

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

实现MySQL的主从复制基础

目录 1 MySQL实现主从复制的原理 1.1 实现主从复制的规则 1.2 如何实现主从复制 2 MySQL 实现主从复制实践 2.1 实验环境 2.2 my.cnf 配置添加 2.2.1 配置MSTER 端配置文件 2.2.2 配置SLAVE 端配置文件 2.2.3 三台MySQL服务器重启服务 2.3 创建用于复制的用户 2.4 保证三台主机…

51单片机——模块化编程

1、模块化编程介绍 传统方式编程&#xff1a;所有的函数均放在main.c里&#xff0c;若使用的模块比较多&#xff0c;则一个文件内会有很多的代码&#xff0c;不利于代码的组织和管理&#xff0c;而且很影响编程者的思路。 模块化编程&#xff1a;把各个模块的代码放在不同的.…

运维学习————nginx2-配置详解及负载均衡

目录 一、配置文件详解 1.1、结构 1.2、重要配置解释 1.3、详细配置 全局配置 Events HTTP 服务器配置 server虚拟主机配置 location URL匹配配置 1.4、完整配置 二、负载均衡 2.1、概念 2.2、集群规划及实现 2.3、具体实现 2.3.1、克隆 2.3.2、修改tomcat1配…

MySQL分区表

1.分区算法 MySQL提供4种分区算法&#xff1a;取余&#xff1a;Key&#xff0c;hash 条件&#xff1a;List&#xff0c;range 。 参与分区的参数字段需要为主键的一部分。 &#xff08;1&#xff09;KEY – 取余 &#xff0c;按照某个字段进行取余 分成5个区&#xff0c;就…

力扣: 设计链表

文章目录 需求代码结尾 需求 你可以选择使用单链表或者双链表&#xff0c;设计并实现自己的链表。 单链表中的节点应该具备两个属性&#xff1a;val 和 next 。val 是当前节点的值&#xff0c;next 是指向下一个节点的指针/引用。 如果是双向链表&#xff0c;则还需要属性 p…