初识C语言指针(4)

news2024/11/15 18:42:32

目录

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");
	else
		printf("str1 and str2 are not same\n");

	if (str3 == str4)
		printf("str3 and str4 are same\n");
	else
		printf("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.coloradmin.cn/o/2071608.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

pikachu-ssrf_redis

目录 SSRF 1、SSRF漏洞介绍&#xff1a; 2、SSRF漏洞原理&#xff1a; 3、SSRF漏洞利用手段&#xff1a; 4、SSRF漏洞绕过方法&#xff1a; SSRF(curl)用法 1、通过网址访问链接 2、利用file协议查看本地文件 3、dict协议扫描内网主机开放端口 4.gopher&#xff1a;威…

神经网络算法 - 一文搞懂Gradient Descent(梯度下降)

本文将从梯度下降的本质、梯度下降的原理、梯度下降的算法 三个方面&#xff0c;带您一文搞懂梯度下降 Gradient Descent | GD。 梯度下降 机器学习“三板斧”&#xff1a;选择模型家族&#xff0c;定义损失函数量化预测误差&#xff0c;通过优化算法找到最小化损失的最优模型参…

docker GBase 8sV8.8使用的常见错误

因项目需要GBase 8sV8.8数据库环境&#xff0c;所以在搭建使用过程中有一些坑和错误&#xff0c;所以记录和分享 docker搭建 docker.com获取镜像 docker pull liaosnet/gbase8s:v8.8_3503x1_x64创建容器 docker run -d -p 19088:9088 \-e SERVERNAMEgbase01 \-e USERPASSGB…

CCleaner:系统优化与隐私保护的双重守护

大家好&#xff0c;今天电脑天空要介绍一款非常实用的系统优化工具——CCleaner。如果你的电脑运行速度越来越慢&#xff0c;或者担心隐私泄露问题&#xff0c;那么CCleaner可能是你的解决方案。 系统优化&#xff0c;一键搞定 CCleaner能够智能识别并清理电脑中的临时文件、…

基于Spring Boot的文字识别系统

前端使用htmlcssjs&#xff0c;后端使用Spring Boot&#xff0c;数据库使用mysql&#xff0c;识别算法有两个&#xff0c;一个是使用百度OCR接口&#xff0c;一个是自己写一个python&#xff0c;用flask包装。 其中百度OCR接口可以去免费申请&#xff0c;然后把appid、apikey、…

k8s 部署Ruoyi-Vue-Plus之vue打包镜像

在这里插入图片描述 在这篇文章中&#xff0c;解释如何通过容器化&#xff08;Docker&#xff09;来打包和部署前端项目&#xff0c;替代之前手动维护版本的方式 1.nginx配置 在 ruoyi-ui 项目的根目录下创建一个 nginx.conf 文件, 我没有使用monitor-admin和xxljob-admin模块…

【吊打面试官系列】为什么Mysql的索引结果用B+树

Mysql如何使用索引查询数据的 下图是一个B树我们的目标节点是 13 我们先根据 13 锁定非叶子节点 2&#xff0c;计算机进行一次IO操作把叶子节点取出来&#xff0c;在叶子节点2中找到叶子节点5 里面有 13 计算机也要进行一次IO将数据取到内存里然后读数据 一共进行了 3次磁盘I…

MATLAB 快速计算点到二维直线的距离并可视化(79)

计算点到二维直线距离,主要是还提供了具体的可视化方法 MATLAB 快速计算点到二维直线的距离并可视化(79) 一、算法介绍二、算法实现1.代码2.效果一、算法介绍 问题:给定一条直线的方程 (Ax + By + C = 0) 和一个点 ((x_0, y_0)),点到直线的距离 (d) 方法:运行此脚本后,…

区块链的搭建与运维4

区块链的搭建与运维4 任务一:区块链产品需求分析与方案设计 本任务需要依据项目背景完成需求分析与方案设计,具体要求如下: 按照新能源系统的需求规定,用户可以通过本系统实现能源管理与交易、新能源资产管理与交易、用户管理等功能。本系统软件部分可划分为浏览器页面、服务…

Tina-SDK开发

开发环境搭建 获取TinaSDK源码 Tina-SDKV2.0源码网盘链接&#xff1a;https://pan.baidu.com/s/13uKlqDXImmMl9cgKc41tZg?pwdqcw7 上传到ubuntu&#xff0c;创建文件夹用来保存源码&#xff1a; ubuntuubuntu1804:~$ mkdir Tina_SDK ubuntuubuntu1804:~$ cd Tina_SDK/ ubu…

【单片机】PIC16F1719 单片机,UART,串口发送

/** 文件: main.c* 目标: PIC16F1719* 编译器: XC8 v1.41* 开发环境: MPLABX v4.10** 创建日期: 2023年8月4日 下午2:58** PIC16F1719* +-----------------+* VPP -> 1 : RE3/MCLR/VPP : 40 <> PGD/RB7* <>…

【二分-BM19 寻找峰值】

题目 BM19 寻找峰值 描述 给定一个长度为n的数组nums&#xff0c;请你找到峰值并返回其索引。数组可能包含多个峰值&#xff0c;在这种情况下&#xff0c;返回任何一个所在位置即可。 分析 对一个左闭右闭的区间二分&#xff0c;通过改变l 或r 的值将原始查找区间缩小一半…

【HarmonyOS NEXT星河版开发实战】页面跳转

个人主页→VON 收录专栏→鸿蒙综合案例开发​​​​​ 代码及其图片资源会发布于gitee上面&#xff08;已发布&#xff09; gitee地址https://gitee.com/wang-xin-jie234 目录 前言 界面功能介绍 界面构建过程 知识点概述 页面跳转 页面传参 全套源代码 Index页面 Sec…

API 的多版本管理,如何在 Apifox 中操作?

开放 API 是技术团队向外部提供服务和数据的关键手段。随着业务的发展和技术的更新&#xff0c;API 也需要不断进行版本迭代。这种迭代通常是为了满足市场需求&#xff0c;优化现有功能&#xff0c;增加新特性&#xff0c;或者修复漏洞。 在多个版本共存的情况下&#xff0c;团…

pikachu靶场----ssrf实现

目录 ssrf简介 SSRF&#xff08;curl&#xff09; 1.后端代码分析 2.http协议连接本地文件 3.file协议读取C盘中的文件。 4.dict协议扫描内网其他主机的端口开放情况 5.使用burp扫描内网其他主机的端口开放情况 ssrf简介 SSRF(Server-Side Request Forgery:服务器端请求…

AI引领边缘计算变革,打造嵌入式产业新未来

在科技的洪流中&#xff0c;AI&#xff08;人工智能&#xff09;如同一位强大的领航者&#xff0c;正引领着边缘计算发生深刻的变革&#xff0c;为嵌入式产业开辟出一片充满无限可能的新未来。 曾经&#xff0c;我们难以想象智能设备能够在无需依赖云端强大计算能力的情况下&am…

.NET_WebForm_layui控件使用及与webform联合使用

使用layui控件填充布局 操作流程&#xff1a; 创建项目。引入css以及js样式。使用栅格样式进行布局。官网查找控件元素&#xff08;此处以图标和按钮为例&#xff09;。将对应的元素从官网复制下来。在布局中填充。很简单的操作&#xff0c;其他控件也同理&#xff0c;不再赘…

GlusterFS-分布式文件系统:概念、案例

目录 GlusterFS 简介 概念 文件系统 通信方式 Cluster特点 扩展性和高性能 高可用性 全局统一命名空间 弹性卷管理 基于标准协议 GlusterFS术语 &#xff08;1&#xff09;Brick &#xff08;2&#xff09;Volume &#xff08;3&#xff09;FUSE &#xff08;4…

因http连接问题产生的生产事故

早上7点接到报警&#xff08;有机器oom报警&#xff0c;且负载很高&#xff0c;运维看阿里云监控上的tcp链接&#xff09;&#xff0c;研发和运维小伙伴一起分析&#xff0c;因为怕影响线上客户&#xff0c;直接选择了服务回退&#xff08;昨天&#xff09;。 服务回退后&…

2024/08(二) 近期关于AI的阅读和理解[笔记]

## Multi Agent/Flow 最近团队在实验 flowise 的 Agentflows 设计&#xff0c;顺带看了现在市面多加解决方案&#xff0c;这两天偶尔看到蚂蚁CodeFuse团队开发的Mulit Agent框架开发思想&#xff0c; 所以将他们一起总结归纳下。 Agent Base&#xff1a;构建了四种基本的Agent…