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

news2025/1/9 15:27:32

欢迎来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中添加。我们把这种用函数指针数组拿来做中间板的情况,就叫转移表。

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

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

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

相关文章

LeetCode Python - 16.最接近的三数之和

目录 题目答案运行结果 题目 给你一个长度为 n 的整数数组 nums 和 一个目标值 target。请你从 nums 中选出三个整数&#xff0c;使它们的和与 target 最接近。 返回这三个数的和。 假定每组输入只存在恰好一个解。 示例 1&#xff1a; 输入&#xff1a;nums [-1,2,1,-4],…

Vulnhub靶场 DC-6

目录 一、环境搭建 二、主机发现 三、漏洞复现 1、wpscan工具 2、后台识别 dirsearch 3、爆破密码 4、rce漏洞利用 activity monitor 5、rce写shell 6、新线索 账户 7、提权 8、拿取flag 四、总结 一、环境搭建 Vulnhub靶机下载&#xff1a; 官网地址&#xff1a…

凭证获取:Linux凭证获取

目录 shadow文件详解 1.Shadow文件的组成与作用 2.破解Shadow文件内容 利用strace记录密码 shadow文件 1.Shadow文件的组成与作用 在Linux中&#xff0c;/etc/shadow文件又被称为“影子文件”&#xff0c;主要用于存储Linux文件系统中的用户凭据信息。该文件只能由root权…

【日常聊聊】新年新征程:迎接学习的挑战

&#x1f34e;个人博客&#xff1a;个人主页 &#x1f3c6;个人专栏&#xff1a;日常聊聊 ⛳️ 功不唐捐&#xff0c;玉汝于成 目录 前言 正文 结语 我的其他博客 前言 随着新的一年的到来&#xff0c;程序员们站在了全新的起点。这是一个充满机遇和挑战的时刻&#xff0…

【C++初阶:类和对象(下篇)】初始化列表 | static成员 | 友元

目录 一、构造函数构造函数体赋值&#x1f43e;初始化列表&#x1f43e;&#x1f4a6; explicit关键字 二、static成员&#x1f43e;概念**&#x1f4a6; 关于静态的特性** 三、友元&#x1f4a6; **友元函数**&#x1f4a6; **友元类** **四、内部类** 一、构造函数 构造函数…

Spring 事务原理总结四

作为一名认知有限的中国人&#xff0c;我对年的喜爱&#xff0c;胜过其他一切&#xff0c;因为它给了我拒绝一切的合理理由。每到这个时候&#xff0c;我都会用各种理由来为自己的不作为开脱&#xff0c;今年亦是如此。看着频频发出警报的假期余额&#xff0c;我内心的焦躁变得…

分布式文件系统 SpringBoot+FastDFS+Vue.js【一】

分布式文件系统 SpringBootFastDFSVue.js【一】 一、分布式文件系统1.1.文件系统1.2.什么是分布式文件系统1.3.分布式文件系统的出现1.3.主流的分布式文件系统1.4.分布式文件服务提供商1.4.1.阿里OSS1.4.2.七牛云存储1.4.3.百度云存储 二、fastDFS2.1.fastDSF介绍2.2.为什么要使…

详解CC++内存管理(new和delete)

文章目录 写在前面1. C&C内存分布2. C语言中动态内存管理方式&#xff1a;malloc/calloc/realloc/free3. C内存管理方式&#xff08;语法&#xff09;3.1 new/delete操作内置类型3.2 new和delete操作自定义类型 4. new和delete的实现原理4.1 operator new与operator delete…

【MySQL】学习外键约束处理员工数据

&#x1f308;个人主页: Aileen_0v0 &#x1f525;热门专栏: 华为鸿蒙系统学习|计算机网络|数据结构与算法 ​&#x1f4ab;个人格言:“没有罗马,那就自己创造罗马~” #mermaid-svg-g4glZPIY0IKhiTfe {font-family:"trebuchet ms",verdana,arial,sans-serif;font-siz…

STM32—DHT11温湿度传感器

文章目录 一.温湿度原理1.1 时序图 二.代码 一.温湿度原理 1.1 时序图 (1).下图一是DHT11总的时序图。 (2).图二对应图一的左边黑色部分&#xff0c;图三对应图一的绿色部分&#xff0c;图四的左部分图对应图一的红色部分&#xff0c;图四的右部分对应图一的黄色部分。 (3)…

计算机组成原理(1)----主存储器

目录 1.基本半导体元件及原理 2.寻址 1.基本半导体元件及原理 一个主存储器可以分为存储器&#xff0c;MAR&#xff08;地址寄存器&#xff09;和MDR&#xff08;数据寄存器&#xff09;&#xff0c;这三个部件由在时序控制逻辑的控制下工作 其中存储体用来存放二进制数据0和…

[BIZ-缓存] - 3.金融交易系统缓存架构设计

1. 前置文章 [BIZ] - 1.金融交易系统特点https://blog.csdn.net/besthezhaowen/article/details/136118133 [缓存] - 1.缓存共性问题https://blog.csdn.net/besthezhaowen/article/details/136111466 [缓存] - 2.分布式缓存重磅中间件 Redis-CSDN博客文章浏览阅读1.4k次&…

docker (一)-简介

1.什么是docker Docker 是一个开源的应用容器引擎&#xff0c;由于docker影响巨大&#xff0c;今天也用"Docker" 指代容器化技术。 2.docker的优势 一键部署&#xff0c;开箱即用 容器使用基于image镜像的部署模式&#xff0c;image中包含了运行应用程序所需的一…

【Java程序员面试专栏 分布式中间件】ElasticSearch 核心面试指引

关于ElasticSearch 部分的核心知识进行一网打尽,包括ElasticSearch 的基本概念,基本架构,工作流程,存储机制等,通过一篇文章串联面试重点,并且帮助加强日常基础知识的理解,全局思维导图如下所示 基础概念 从数据分类入手,考察全文索引的基本概念 现实世界中数据有哪…

【教程】Kotlin语言学习笔记(二)——数据类型(持续更新)

写在前面&#xff1a; 如果文章对你有帮助&#xff0c;记得点赞关注加收藏一波&#xff0c;利于以后需要的时候复习&#xff0c;多谢支持&#xff01; 【Kotlin语言学习】系列文章 第一章 《认识Kotlin》 第二章 《数据类型》 文章目录 【Kotlin语言学习】系列文章一、基本数据…

机器学习和统计学的区别?

1、本质区别&#xff1a; 目标&#xff1a;机器学习的核心目标是建立一个可以自动学习和改进的模型&#xff0c;以预测未知数据。它更关注结果的准确性和模型的泛化能力&#xff0c;通常不关心模型是否可以解释。而统计学的目标是探究变量之间的关系&#xff0c;理解数据的内在…

Python数据科学:Scikit-Learn机器学习

4.1Scikit-Learn机器学习 Scikit-Learn使用的数据表示&#xff1a;二维网格数据表 实例1&#xff1a;通过Seaborn导入数据 def skLearn():scikit Learn基本介绍:return:import seaborn as sns#导入Iris数据集#注&#xff1a;一般网络访问不了iris sns.load_dataset(iris)ir…

Spring Boot 笔记 020 redis集成

1.1 安装redis Windows 下 Redis 安装与配置 教程_redis windows-CSDN博客 2.1 引入redis坐标 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency> 2.2 配置…

144.【Activiti 7】

Activiti 7 工作流 (一)、工作流程1.概念2.工作流系统3.适用行业4.具体应用5.实现方式 (二)、Activiti7概述1.介绍(1).BPM 介绍(2).BPM软件(3). BPMN 2.使用步骤(1).部署activiti(2).流程定义(3).流程定义部署(4).启动一个流程实例(5).用户查询待办任务(Task)(6).用户办理任务(…

第13讲创建图文投票

创建图文投票实现 图文投票和文字投票基本一样&#xff0c;就是在投票选项里面&#xff0c;多了一个选项图片&#xff1b;、 <view class"option_item" v-for"(item,index) in options" :key"item.id"><view class"option_input&…