【C语言】指针的进阶篇,深入理解指针和数组,函数之间的关系

欢迎来CILMY23的博客喔,本期系列为【C语言】指针的进阶篇,深入理解指针和数组,函数之间的关系,图文讲解其他指针类型以及指针和数组,函数之间的关系,带大家更深刻理解指针,以及数组+指针,指针和函数的用法,感谢观看,支持的可以给个赞哇。

前言

在上一篇博客中,我们了解了strlen的模拟实现,以及冒泡排序,并且为了熟悉指针数组,我们还学习了用指针数组来模拟实现二维数组,本期博客将用其他指针类型来开篇,并学习指针和数组,函数之间的关系。 

目录

一、字符指针变量

 二、数组指针变量

三、二维数组传参

四、函数指针变量

五、typedef关键字

六、函数指针数组

七、转移表


一、字符指针变量

 什么是字符指针变量?

看下列代码:

#include<stdio.h>

int main()
{
	char ch = 'c';
	char* pc = &ch;

	return 0;
}

上列代码中,pc就是字符指针变量,那如果后面的不是&ch,而是一个字符串又是如何存放在字符指针变量中的呢?

#include<stdio.h>

int main()
{
	char* pc = "abcde";
	printf("%c ", *pc);

	return 0;
}

我们通过调试发现,最后的输出结果为a。所以字符串在存入指针变量的时候,是将首字符a的地址存入的。 

 接下来看以下代码:

#include<stdio.h>

int main()
{
	char str1[] = "hello SILMY23";
	char str2[] = "hello SILMY23";

	const char* str3 = "hello SILMY23";
	const char* str4 = "hello SILMY23";

	if (str1 == str2)
		printf("same\n");
	else
		printf("%p\n%p\n",str1,str2);

	if (str3 == str4)
		printf("same\n");
	else
		printf("%p\n%p\n", str3, str4);
	return 0;
}

我们假设有两个字符数组,和两个用const修饰的字符指针变量,存放字符串hello SILMY23,我们看看在内存上它们是如何存放的?

运行结果如下:

 

我们发现在内存上,用const修饰的,它们是公用同一块空间的,也就是空间图如下所示:

 

用const修饰的常量字符串是不能被修改的,既然不能修改那么同样的内容就没有必要再开辟另外一个空间进行存放,所以两个str3和str4所指向的空间是相同的。 

 二、数组指针变量

我们在上一篇学习到,指针数组是用来存放指针的数组,那这一块数组指针,我们仍然采用类比的方式,字符指针,指向字符,是用来存放字符地址, 整型指针,指向整型,是用来存放整型地址,所以数组指针是指向数组,用来存放数组地址。例如:

int arr[10];
int (*p)[10] = &arr;

&arr拿到数组的地址。 p就是数组指针。

数组指针和指针数组

int* p[10];//指针数组
int (*p)[10];//数组指针

 字符数组指针:

char cha[8];
char (*pc)[8] = &cha;

所以数组指针类型:

int (*)[10]

char (*)[8] 

那数组指针如何初始化呢?就是用数组地址来初始化。

 那数组指针和指针数组的用法得分开

#include<stdio.h>

int main()
{
	//指针数组
	int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 };
	int* p[] = { arr1 };
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		printf("%d ", p[0][i]);
	}
	printf("\n");
	//数组指针
	int(*p1)[10] = &arr1;
	for (i = 0; i < 10; i++)
	{
		printf("%d ", (*p)[i]);
	}

	return 0;
}

结果如下:

 

上述代码的分布图如下: 

总结:

指针数组是数组内的元素是一个个指针而数组指针是指向数组的指针

指针数组是数组,而数组指针就是个指针。

三、二维数组传参

我们在上一篇博客中,用指针数组模拟了二维数组,那二维数组传参的本质又是什么呢?

我们先来看二维数组的传参使用

#include<stdio.h>

void print(int arr[2][5], int r, int c)
{
	int i = 0;
	for (i = 0; i < r; i++)
	{
		int j = 0;
		for (j = 0; j < c; j++)
		{
			printf("%d ", arr[i][j]);
		}
		printf("\n");
	}
}

int main()
{
	int arr1[2][5] = { 1,2,3,4,5,6,7,8,9,10 };
	print(arr1, 2, 5);

	return 0;
}

二维数组的传参本质其实还是传了数组首元素的地址,传的是数组第一行第一列的地址,所以我们函数写形参还是可以写指针形式接收。

二维数组的每一行可以看作一个一维数组,这个一维数组可以看作是二维数组的一个元素,所以二维数组也可以认为是一维数组的数组,所以二维数组的首元素地址就是第一行的地址,也就是一个一维数组的地址。所以:

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

代码形参可以改造成这种,形参部分写成指向第一行的数组指针。而arr[i][j]==*(*(arr+i)+j)

#include<stdio.h>

void print(int (*arr)[5], int r, int c)
{
	int i = 0;
	for (i = 0; i < r; i++)
	{
		int j = 0;
		for (j = 0; j < c; j++)
		{
			printf("%d ", *(*(arr + i) + j));
		}
		printf("\n");
	}
}

int main()
{
	int arr1[2][5] = { 1,2,3,4,5,6,7,8,9,10 };
	print(arr1, 2, 5);

	return 0;
}

四、函数指针变量

我们已经接触过了很多指针变量,整型指针,数组指针,字符指针,那现在要新认识一个函数指,那顾名思义,函数指针,就是用来存放函数的地址,那函数名是否就是地址呢?

我们看之前写下的代码:

#include<stdio.h>

void print(int (*arr)[5], int r, int c)
{
	int i = 0;
	for (i = 0; i < r; i++)
	{
		int j = 0;
		for (j = 0; j < c; j++)
		{
			printf("%d ", *(*(arr + i) + j));
		}
		printf("\n");
	}
}

int main()
{
	int arr1[2][5] = { 1,2,3,4,5,6,7,8,9,10 };
	printf("%p ", print);
	printf("%p ", &print);
	return 0;
}

结果如下: 

 

我们发现,无论是函数名还是&函数名,都是函数的地址,而在数组当中,数组名和&数组名是不一样的。 

那现在要将print存入指针变量,在写法上还是类似数组指针的

那如何通过p调用print函数呢?

	(*p)(arr1, 2, 5);

首先是对p解引用获得函数地址,其次是传参。这样就可以通过函数指针调用函数了。

 但其实不写也是可以的。但写上(*)更容易理解。 

五、typedef关键字

typedef关键字是用来进行类型重命名的,可以将复杂的类型简单化:

//重命名数据类型
typedef unsigned int uint;
typedef double dl;
//重命名指针类型
typedef char* pcr;
typedef int* pint;
//重命名数组指针类型
typedef int(*parr_t)[5];
//重命名函数指针类型
typedef void(*pfun_t)(int);
//新的类型名必须在*的右边

六、函数指针数组

函数指针数组?说白了就是一个数组,这里面存放的都是函数指针的地址

在第四个知识点我们写了print函数,现在我们想把它放进一个函数指针数组里

void (*parr[1])(int, int, int) = { print };

函数指针类型是需要一样的,唯一不一样的是变量名我们给了一个数组来实现函数指针数组。 

七、转移表

函数指针数组的用途:转移表

普通四则计算器的实现:

#include <stdio.h>

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 menu()
{
	printf("***************************\n");
	printf("*****  1. add  2.sub  *****\n");
	printf("*****  3. mul  4.div  *****\n");
	printf("*****  0. exit        *****\n");
	printf("***************************\n");
}

int main()
{
	int input = 0;
	int x = 0;
	int y = 0;
	int ret = 0;
	int(*pfArr[5])(int, int) = { 0,Add,Sub,Mul,Div};
	do
	{
		menu();
		printf("请选择:>");
		scanf("%d", &input);
	
		if (input >= 1 && input <= 4)
		{
			printf("请输入2个操作数:>");
			scanf("%d %d", &x, &y);
			ret = pfArr[input](x, y);
			printf("%d\n", ret);
		}
		else if(input == 0)
		{
			printf("退出计算器\n");
			break;
		}
		else
		{
			printf("选择错误,请重新选择");
		}
	} while (input);

	return 0;
}

如果利用函数指针数组来简化代码,代码就会变得相对简短一些,增添一些功能,也只会从pfArr中添加。我们把这种用函数指针数组拿来做中间板的情况,就叫转移表。

感谢各位同伴的支持,本期指针进阶篇就讲解到这啦,如果你觉得写的不错的话,可以给个赞,若有不足,欢迎各位在评论区讨论。  

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

谁拿了最多奖学金——NOIP 2005 提高组

输入样例&#xff1a; 4 YaoLin 87 82 Y N 0 ChenRuiyi 88 78 N Y 1 LiXin 92 88 N N 0 ZhangQin 83 87 Y N 1 输出样例&#xff1a; ChenRuiyi 9000 28700 这道题用结构体做对吧 #include <bits/stdc.h> using namespace std; class student{public:string name;int FG…

Springboot+vue的大学生智能消费记账系统的设计与实现(有报告)。Javaee项目,springboot vue前后端分离项目

演示视频&#xff1a; Springbootvue的大学生智能消费记账系统的设计与实现&#xff08;有报告&#xff09;。Javaee项目&#xff0c;springboot vue前后端分离项目 项目介绍&#xff1a; 本文设计了一个基于Springbootvue的大学生智能消费记账系统的设计与实现&#xff0c;采…

Midjourney绘图欣赏系列(三)

Midjourney介绍 Midjourney 是生成式人工智能的一个很好的例子&#xff0c;它根据文本提示创建图像。它与 Dall-E 和 Stable Diffusion 一起成为最流行的 AI 艺术创作工具之一。与竞争对手不同&#xff0c;Midjourney 是自筹资金且闭源的&#xff0c;因此确切了解其幕后内容尚不…

platformio 提示 fatal error: TimeLib.h: No such file or directory 的解决方案

在platformio编译arduino项目的时候&#xff0c;如果提示fatal error: TimeLib.h: No such file or directory&#xff0c;解决方法有2&#xff1a; 方法1&#xff1a; 在项目的platformio.ini文件中&#xff0c;添加 lib_deps # Using library Id44方法2&#xff1a; 通过…

C++ 模板进阶

C 模板进阶 一.非类型模板参数1.概念2.实例3.注意事项 二.模板的特化1.引出2.函数模板的特化1.语法和使用2.建议 3.类模板的特化1.全特化2.偏特化1.部分特化2.对参数进行进一步的限制 4.匹配顺序 三.模板的分离编译1.什么是分离编译2.模板的分离编译3.解决方法1.显式实例化(不推…

C++中的拷贝构造函数

一、拷贝构造函数的概念 拷贝构造函数用于创建一个与已有对象相同的对象&#xff0c;本质上也是构造函数的重载 拷贝构造函数只有一个类型为 const 类类型引用的形参&#xff0c;当我们要创建一个与已存在对象相同的对象时&#xff0c;由编译器自动调用拷贝构造函数。 clas…

MySQL运行错误:‘mysql‘不是内部或外部命令,也不是可运行程序或批处理文

主要原因是&#xff1a;没有将mysql安装目录下的bin目录&#xff0c;添加到系统变量中 编辑系统环境变量 双击Path即可 下一步 记得每一步点击确定就好啦。 下面验证一下是否成功呢&#xff1f; 输入命令符(V是大写的哦~&#xff09; mysql -V 以上就是成功啦&#xff01…

Flink理论—容错之状态

Flink理论—容错之状态 在 Flink 的框架中&#xff0c;进行有状态的计算是 Flink 最重要的特性之一。所谓的状态&#xff0c;其实指的是 Flink 程序的中间计算结果。Flink 支持了不同类型的状态&#xff0c;并且针对状态的持久化还提供了专门的机制和状态管理器。 Flink 使用…

一周学会Django5 Python Web开发-项目配置settings.py文件-模版配置

锋哥原创的Python Web开发 Django5视频教程&#xff1a; 2024版 Django5 Python web开发 视频教程(无废话版) 玩命更新中~_哔哩哔哩_bilibili2024版 Django5 Python web开发 视频教程(无废话版) 玩命更新中~共计17条视频&#xff0c;包括&#xff1a;2024版 Django5 Python we…

OpenAI突然发布首款文生视频模型——Sora;谷歌发布Gemini 1.5,迈向多模态大模型新时代

&#x1f989; AI新闻 &#x1f680; OpenAI突然发布首款文生视频模型——Sora 摘要&#xff1a;OpenAI发布了首个AI视频模型Sora&#xff0c;可以根据文字指令生成神级效果的长视频&#xff0c;引发了广泛关注和震惊。 Sora模型通过深入理解语言和图像&#xff0c;能够创造出…

阿里云香港服务器多少钱一年?288元

阿里云香港服务器2核1G、30M带宽、40GB ESSD系统盘优惠价格24元/月&#xff0c;288元一年&#xff0c;每月流量1024GB&#xff0c;多配置可选&#xff0c;官方优惠活动入口 https://t.aliyun.com/U/bLynLC 阿里云服务器网aliyunfuwuqi.com分享阿里云香港服务器优惠活动、详细配…

前端工程化面试题 | 10.精选前端工程化高频面试题

&#x1f90d; 前端开发工程师、技术日更博主、已过CET6 &#x1f368; 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 &#x1f560; 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 &#x1f35a; 蓝桥云课签约作者、上架课程《Vue.js 和 E…

【算法设计与分析】搜索旋转排序数组

&#x1f4dd;个人主页&#xff1a;五敷有你 &#x1f525;系列专栏&#xff1a;算法分析与设计 ⛺️稳中求进&#xff0c;晒太阳 题目 整数数组 nums 按升序排列&#xff0c;数组中的值 互不相同 。 在传递给函数之前&#xff0c;nums 在预先未知的某个下标 k&#xff…

Covalent Network(CQT)与卡尔加里大学建立合作,共同推动区块链技术创新

Covalent Network&#xff08;CQT&#xff09;作为领先的 Web3 数据索引器和提供者&#xff0c;宣布已经与卡尔加里大学达成了具备开创性意义的合作&#xff0c;此次合作标志着推动区块链数据研究和可访问性的重要里程碑。卡尔加里大学是首个以验证者的身份加入 Covalent Netwo…

程序全家桶 | 机器学习之心【Python机器学习/深度学习程序全家桶】

理论背景 机器学习&#xff08;Machine Learning&#xff09;是一种人工智能&#xff08;Artificial Intelligence&#xff09;领域的技术和方法&#xff0c;通过使用数据和统计模型&#xff0c;使计算机系统能够自动学习和改进&#xff0c;而无需明确地进行编程。机器学习使计…

RUST入门:如何用vscode调试rust程序

RUST已经流行一阵子了&#xff0c;但是比较系统的IDE介绍还是比较少&#xff0c;这里我简单介绍 一下如何用vscode实现单步调试rust程序&#xff0c;就像我们平时调试c程序一样。 学习资料网站 首先&#xff0c;介绍几个学习rust的好网站&#xff0c; Rust程序设计语言Rust语…

html从零开始8:css3新特性、动画、媒体查询、雪碧图、字体图标【搬代码】

css3新特性 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"IEedge"><meta name"viewport" content"widthdevice-width, …

分布式事务详解

概述 随着互联网的发展,软件系统由原来的单体应用转变为分布式应用。分布式系统把一个单体应用拆分为可独立部署的多个服务,因此需要服务与服务之间远程协作才能完成事务操作。这种分布式系统下不同服务之间通过远程协作完成的事务称之为分布式事务,例如用户注册送积分事务…

一、部署Oracle

部署Oracle 一、Docker部署1.Oracle11g1.1 测试环境1.1.1 拉取镜像1.1.2 启动容器1.1.3 配置容器环境变量1.1.4 修改sys、system用户密码1.1.5 创建表空间1.1.6 创建用户并授权1.1.5 使用DBeaver测试连接 二、安装包部署 一、Docker部署 1.Oracle11g 1.1 测试环境 当前只能用…

MATLAB知识点:perms函数(★★★☆☆)用来返回向量v中各元素所有可能的排列情况

讲解视频&#xff1a;可以在bilibili搜索《MATLAB教程新手入门篇——数学建模清风主讲》。​ MATLAB教程新手入门篇&#xff08;数学建模清风主讲&#xff0c;适合零基础同学观看&#xff09;_哔哩哔哩_bilibili 节选自第3章&#xff1a;课后习题讲解中拓展的函数 在讲解第三…