Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >【C语言】(指针系列3)数组指针+函数指针+typedef+函数数组指针+转移表

【C语言】(指针系列3)数组指针+函数指针+typedef+函数数组指针+转移表

作者头像
用户11367452
发布于 2024-11-21 00:05:55
发布于 2024-11-21 00:05:55
11100
代码可运行
举报
文章被收录于专栏:学习学习
运行总次数:0
代码可运行

前言:前言:开始之前先感谢一位大佬,清风~徐~来-CSDN博客,由于是时间久远,博主指针的系列忘的差不多了,所以有顺序部分借鉴了该播主的,同时也加入了博主自己的理解,有些地方如果解释的不到位,请翻看这位大佬的,感谢大家!!!!!!

一,指针数组

思考:我们上一系列已经知道什么是数组指针了,那么指针数组又是什么哪

答案是:指针数组是一个数组是数组,储存的是地址(首元素),数组指针是数组还是指针哪?答案是--------指针变量

我们已经熟悉:

  • 整形指针变量: int * pi; 存放的是整形变量的地址,能够指向整形数据的指针。
  • 浮点型指针变量: float * pf; 存放浮点型变量的地址,能够指向浮点型数据的指针。
  • 组指针:存放的是数组的地址,能够指向数组的指针变量

二、指针数组和数组指针的区别

数组指针和指针数组的写法

int*parr[10]//指针数组

解释数组名先和[]结合,说明这是一个数组,数组中有10个元素,元素的类型是(int*)型,所以p2是一个数组,数组元素指针,叫做指针数组。

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

解释p2先和(*)结合说明p2是一个指针变量,然后指针指向的是一个大小为10个整形的数组,,所以p2是一个指针,叫做数组指针。

[]的优先级要高于*号,所以必须要叫()来提高优先级,确保p2与*结合


数组指针的初始化

数组指针是用来存放数组地址的,那怎么才能获得数组地址?这里就要用到&的符号,如果要存放数组地址,就得存放数组指针变量中

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
int main()
{
	int arr[10] = { 0 };
	int(*p)[10] = &arr;
	return 0;
}

我们调试可以看到

我们调试也能看到 &arr 和 p 的类型是完全⼀致的。

三、二维数组传参的本质

我们将之前,先来回忆一下一维数组传参,一维数组传参为了避免开辟额外的空间,所以只需要传数组的首地址即可

我们再来看我们曾经写过的一段二维数组传参的一段代码:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
	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;
	}

这里实参是⼆维数组,形参也写成⼆维数组的形式,那还有什么其他的写法吗?

重点

  • 二维数组在内存中是连续存储的
  • 二维数组可以理解为一维数组的数组,二维数组的每一行都可以看成一个一维数组
  • 二维数组名也是元素的首地址,这里的首地址是指第一行首元素,传过去的是第一行这个一维数组的地址,也就是arr[0]的地址。
  • 第一行的一维数组的类型就是int[5],所以第一行的地址的类型就是int(*) [5]

二维数组传参,形参的部分可以写成数组形式,也可以写成指针形式,如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include <stdio.h>
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));//等价于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:数组首元素的地址,也就是一维数组arr[0]的地址。
  • p+i:跳过 i 个 int[5] 这样的数组(p的类型是数组指针),指向arr[i],p+i 就是一维数组 arr[i] 的地址
  • *(p+i):访问一维数组arr[i],等价于一维数组arr[i],而 arr[i] 是数组名,又是数组首元素的地址,也就是 arr[i][0] 的地址
  • *(p + i) + j:由于 *(p+i)是 arr[i][0] 的地址,所以 +j 跳过 j 个整形(指向整形),也就是 arr[i][j] 的地址
  • *( *(p + i) + j):由于 *(p + i) + jarr[i][j] 的地址,进行解引用操作,就是找到 arr[i][j]
  • 最终:*( *(p + i) + j) 等价于 arr[i][j]。

二、函数指针

什么是函数指针变量呢?

根据前面学习整型指针,数组指针的时候,类比一下,我们不难得出结论

函数指针变量应该是用来存放函数地址的,未来通过地址能够调用函数的。那么函数是否有地址呢?我们做个测试:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include <stdio.h>
void test()
{
	printf("hehe\n");
}
int main()
{
	printf("test:  %p\n", test);
	printf("&test: %p\n", &test);
	return 0;
}

函数的地址确实是有的,同时也验证了函数的地址也是可以用&调取出来的。同时我们观察发现,函数名和取出来的地址是一样的

函数指针变量的使用

如果我们要将函数的地址存放起来,就得创建函数指针变量咯,函数指针变量的写法其实和数组指针非常类似。如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include<stdio.h>
int Add(int x, int y)
{
	return x + y;
}
int main()
{
	//int a = 10;
	//int* pa = &a;//整型指针变量

	//int arr[5] = {0};
	//int (*parr)[5] = &arr;//parr 是数组指针变量
	//arr:数组首元素的地址   &arr:数组的地址
	
	//&函数名和函数名都是函数的地址,没有区别
	//printf("%p\n", &Add);
	//printf("%p\n", Add);

    //int(*pf3)(int, int) = Add;
	//int(*pf3)(int x, int y) = &Add;//x和y写上或者省略都是可以的 
	
	//int (*pf)(int,int) = &Add;//pf 函数指针变量,()不能省略
	int (*pf)(int, int) = Add;//pf 函数指针变量
	int ret1 = (*pf)(4, 5);
	int ret2 = pf(4, 5);//pf等价于Add
	printf("%d\n", ret1);
	printf("%d\n", ret2);

	int ret = Add(4, 5);
	printf("%d\n", ret);

	//int (*pf)(int x, int y) = &Add;//pf 函数指针变量
	//int (*)(int,int) 函数指针类型
	return 0;
}
  1. int (*pf)(int, int) = Add,*pf外的 () 不能省略。
  2. pf == (*pf) == Add == &Add。

两端有趣的代码:

1.( *( void ( * )( ) )0 ) ( );
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
2.void( *signle(int, void( * )(int) ) ) (int)
在这里插入图片描述
在这里插入图片描述

typedef重命名函数

typedef是用来重命名的,可以将复杂的名字简单化,规范化

比如我们命名了一个结构体叫做jinfsjajngijiasogjoiasjda(随便打的),我们每次调用都要写很长一段复杂的东西,但是有了typedef这个东西,我们可以将它重命名为js对!就这两个字符,就可以表达这个结构体

typedef unsigned int uint; //将unsigned int 重命名为uint

如果是指针类型,能否重命名呢?其实也是可以的,比如,将 int* 重命名为 ptr_t ,这样写:

typedef int* ptr_t;

但是对于数组指针和函数指针稍微有点区别: 比如我们有数组指针类型 int(*)[5] ,需要重命名为 parr_t ,那可以这样写:

typedef int(*parr_t)[5];

函数指针类型的重命名也是⼀样的,比如,将 void(*)(int) 类型重命名为 pf_t ,就可以这样写:

typedef void(*pfun_t)(int);//新的类型名必须在*的右边

那么要简化代码2,可以这样写:

typedef void(*pfun_t)(int); pfun_t signal(int, pfun_t);

重点

给出typedef命名一个数组的例子:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include<stdio.h>
int main()
{
typedef int IntArray[5]//定义一个包含5个int类型元素的数组类型IntArray
IntArray arr={1,2,3,4,5};// 使用 IntArray 声明一个数组

for(int i=0;i<5;i++)
{
printf("%d",arr[i]);

}
return 0;
}

四、函数指针数组

定义:

int(*parr[3])();

解释:parr先和[]结合表明这是一个数组,数组的内容是什么,是int(*)()类型的函数指针


函数指针数组的实际应用:转移表

计算器的一般实现路径代码:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#define _CRT_SECURE_NO_WARNINGS 1
#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 x = 0;
	int y = 0;
	int input = 0;
	int ret = 0;
	do
	{
		menu();
		printf("请输入:");
		scanf("%d", &input);
		switch (input)
		{
		case 0:
			break;
		case 1:
			printf("请输入两个数:");
			scanf("%d %d", &x, &y);
			ret = Add(x, y);
			printf("%d+%d=%d\n", x, y, ret);
			break;
		case 2:
			printf("请输入两个数:");
			scanf("%d %d", &x, &y);
			ret = Sub(x, y);
			printf("%d-%d=%d\n", x, y, ret);
			break;
		case 3:
			printf("请输入两个数:");
			scanf("%d %d", &x, &y);
			ret = Mul(x, y);
			printf("%d*%d=%d\n", x, y, ret);
			break;
		case 4:
			printf("请输入两个数:");
			scanf("%d %d", &x, &y);
			ret = Div(x, y);
			printf("%d/%d=%d\n", x, y, ret);
			break;
		default:
			printf("输入错误,请重新输入\n");
			break;
		}
	} while (input);
	return 0;
}

用函数指针实现的:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#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 x = 0;
	int y = 0;
	int input = 0;
	int ret = 0;
	do
	{
		menu();
		printf("请输入:");
		scanf("%d", &input);
		int(*arr[5])(int, int) = { 0,Add,Sub,Mul,Div };//转移表
		if (input == 0)
		{
			break;
		}
		else if (input >= 1 && input <= 4)
		{
			printf("请输入两个数:");
			scanf("%d %d", &x, &y);
			ret = arr[input](x, y);
			printf("ret=%d\n", ret);
		}
		else
		{
			printf("输入错误,请重新输入\n");
		}
	} while (input);
	return 0;
}

不仅仅可以实现加减乘除,还能实现按位与,或,异或,左移,右移等操作,只需在数组中追加函数地址即可,当然前提是将函数敲出来,这种就叫作转移表。

结尾祝福语:

指针系列三到这里就结束了,我们对指针的熟悉又近了一步,指针说是新手的噩梦,其实当你完全了解之后,指针也只不过是取经之路的一难而已,当我们取完经之后,我们会发现,关关难过关关过,斗罢艰险再出发!!!!!!!

最后:风会带来故事的种子,时间是指发芽,但也请不要忘记,旅途中你所看到的风景,人最不能忘的就是初心!!!!!

我们指针系列四再会!!!!!!!!!!!!!!!!!!!!!!!!

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2024-11-20,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
【C语言】带你手把手拿捏指针(3)(含转移表)
   在指针的类型中我们知道有⼀种指针类型为字符指针 char* ,⼀般使⽤的方式如下:
TANGLONG
2024/10/15
1060
【C语言】带你手把手拿捏指针(3)(含转移表)
第四节——从深层剖析指针(让你不再害怕指针)
这里是把⼀个字符串(hello bit.)放到指针变量里了吗? 其实本质是把字符串hello bit 首字符的地址放到了pstr中(pstr指向字符串的首字符的地址)。
egoist祈
2025/01/23
1110
第四节——从深层剖析指针(让你不再害怕指针)
指针详解(3)
在使用上,由于pstr存放的是字符串第一个字符的地址所以 对pstr解引用就可以打印第一个字符,打印整个操作符只需提供首元素的地址,使用%s就可以打印出来。
技匠晓晨
2024/11/26
830
指针详解(3)
C语言——I /深入理解指针(三)
代码 const char* p = "hello bit."; 字符串 hello bit 放到字符指针 p ⾥了,但是本质是把字符串 hello bit. ⾸字符的地址放到了p中(类似于数组)。
用户11015888
2024/03/11
1350
C语言——I /深入理解指针(三)
什么是 字符指针? 数组指针? 函数指针? 函数指针数组?
代码 const char* pstr = “hello world.”; 特别容易让我们以为是把字符串 hello world. 放到字符指针 pstr ⾥了,但是本质是把字符串 hello world. 首字符的地址放到了ps!!tr中。
用户11317877
2024/10/16
1130
什么是 字符指针? 数组指针? 函数指针? 函数指针数组?
小代老师带你深入学习指针!!!深入理解指针(3)
既然放入指针里面的常量字符串无法被修改,那么为了程序的稳健性,我们在程序里面加入const
用户11319080
2024/10/17
710
小代老师带你深入学习指针!!!深入理解指针(3)
指针详解(二级指针、指针数组和数组指针、字符指针、二维数组传参、函数指针变量)(二)
走在努力路上的自己
2024/01/26
6270
指针详解(二级指针、指针数组和数组指针、字符指针、二维数组传参、函数指针变量)(二)
C语言 —— 此去经年梦浪荡魂音 - 深入理解指针(卷三)
迷迭所归处
2025/03/20
700
C语言 —— 此去经年梦浪荡魂音 - 深入理解指针(卷三)
【C语言基础】:深入理解指针(终篇)
指针系列回顾 【C语言基础】:深入理解指针(一) 【C语言基础】:深入理解指针(二) 【C语言基础】:深入理解指针(三)
爱喝兽奶的熊孩子
2024/04/10
1060
【C语言基础】:深入理解指针(终篇)
指针(3)---不同指针变量
在这行代码中,貌似将hello bit.这个字符串放进了字符指针变量pstr中,但事实真的如此吗?
Skrrapper
2024/06/18
820
指针(3)---不同指针变量
拿下大怪兽——指针
parr[i]是访问parr数组的元素,parr[i]找到的数组元素指向了整型一维数组,parr[i][j]就是整型一维数组中的元素。
用户11328191
2024/10/23
870
拿下大怪兽——指针
【C语言】指针总结3
注:代码 const char pstr = “hello.”; 特别容易让人以为是把字符串 hello 放 到字符指针 pstr 里了,但是本质是把字符串 hello. 首字符的地址放到了pstr中。*
用户11290673
2024/09/25
840
【C语言】指针总结3
C语言学习系列-->看淡指针(3)
本质是把字符串 hello ⾸字符的地址放到了pstr中。 把⼀个常量字符串的⾸字符 h 的地址存放到指针变量 pstr 中。
南桥
2024/01/26
2550
C语言学习系列-->看淡指针(3)
【C语言】指针进阶:字符指针&&数组指针&&函数指针
✨作者:@平凡的人1 ✨专栏:《C语言从0到1》 ✨一句话:凡是过往,皆为序章 ✨说明: 过去无可挽回, 未来可以改变 ---- 🌹感谢您的点赞与关注,同时欢迎各位有空来访我的🍁平凡舍 ---- 文章目录 @[toc] 🚀前言 🚀字符指针 🚀指针数组 🚀数组指针 🍁&数组名 与 数组名 🍁数组指针的使用 🚀数组传参、指针参数 🍁一维数组传参 🍁二维数组传参 🍁一级指针传参 🍁二级指针传参 🚀函数指针 🚀结语 🚀前言 回想之前,我们学了指针的一些基础👉 指针与结构体 我们知道了指针的概念
平凡的人1
2022/11/15
3.1K0
【C语言】指针进阶:字符指针&&数组指针&&函数指针
C语言----深入理解指针(3)
char* p = "abcdef";//这里的赋值是讲字符串中首字符的地址存在p中
凯子坚持C
2024/09/23
990
轻松拿捏C语言——【保姆级·指针讲解】期末C语言<指针>急救包,全是干货,诚意满满!
有一栋楼,里有200个房间,假如我们要去某个房间找某个人,然后他说他在C304,我们就能通过门牌号C304快速找到他所在房间。
用户11162265
2024/06/14
1270
轻松拿捏C语言——【保姆级·指针讲解】期末C语言<指针>急救包,全是干货,诚意满满!
C语言之指针详解(3)
上述的代码const char* pstr = "hello bit.";特别容易让大家以为是把字符串hello bit.放到字符指针pstr里了,但是本质是把字符串hello bit.首字符的地址放到了pstr中。所以说,上面的代码是把常量字符串的首字符h的地址放到指针变量pstr中。
Crossoads
2024/10/21
770
C语言之指针详解(3)
C语言:深入理解指针(3)
    通过深入理解指针(1)和深入理解指针(2),我们对指针有了一个初步的了解,学会了一级指针、二级指针、指针数组……而深入理解指针(3),主要是为了学习不同数据类型的指针变量。
小陈在拼命
2024/02/17
1310
C语言:深入理解指针(3)
【C语言】指针进阶
官方解释: C/C++会把常量字符串存储到单独的⼀个内存区域,当几个指针指向同一个字符串的时候,他们实际会指向同一块内存。但是用相同的常量字符串去初始化不同的数组的时候就会开辟出不同的内存块。 分析: 我们可以看到字符串是一样的,为hello world,str1和str2不同,str3和str4相同,我们可以看到str3和str4的char*有const修饰,str1和str2没有,因此我们得出的结论就是:有const修饰的字符串str3和str4,计算机会将其认为是一种字符常量,相同的常量会被计算机存放到同一个地址里,所以二者相同;str1和str2是变量,被存放再不同的地址里边了,所以两者不同(这也优化了底层的运行,其实计算机每一种规则都是为了简化过程,减少资源的浪费)
s-little-monster
2024/06/06
890
【C语言】指针进阶
C语言指针进阶
(公众号:愚生浅末) 在指针的类型中我们知道有一种指针类型为字符指针 char* ;
愷龍
2023/10/16
2610
C语言指针进阶
推荐阅读
相关推荐
【C语言】带你手把手拿捏指针(3)(含转移表)
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验