当前位置:网站首页>【C进阶】数组传参与函数指针
【C进阶】数组传参与函数指针
2022-07-30 03:57:00 【ecember】
技术宅,拯救世界!
对读者的话:相信奇迹的人本身和奇迹一样伟大
生命中最快乐的事情是拼搏,而非成功,生命中最痛苦的是懒散,而非失败。今天我们继续来学习指针的进阶,这里主要给大家讲数组传参与函数指针的相关内容(以下内容均在VS2022中编译)。
| 感谢大家的点赞 和关注 ,如果有需要可以看我主页专栏哟 |
文章目录
1. 数组传参 && 指针传参
在写代码的时候难免要把【数组】或者【指针】传给函数,那函数的参数该如何设计呢?
1.1 一维数组传参
int main()
{
int arr1[10] = {
0 };
test1(arr1);//数组传参
int* arr2[20] = {
0 };
test2(arr2);//指针数组传参
return 0;
}
上述两个数组该怎么传参呢?
void test1(int arr[])//yes
{
}
void test1(int arr[10])//yes
{
}
void test1(int *arr)//yes
{
}
void test2(int *arr[20])//yes
{
}
void test2(int **arr)//yes
{
}
1.2 二维数组传参
void test(int arr[3][5])
{
}
void test(int arr[][5])//行可以省略,列不可以省略
{
}
void test(int(*arr)[5])//数组名是数组首元素地址
{
}
int main()
{
int arr[3][5] = {
0 };
test(arr);
return 0;
}
注意这里不能用二级指针来存放,二级指针是用来存放一级指针的,而数组指针是用来存放数组地址的,这里传参传过去即相当于第一行元素的地址,即可以看成数组地址。
1.3 一级指针传参
void test(int* ptr, int sz)
{
int i = 0;
for (i = 0; i < sz; i++)
{
printf("%d ", *ptr);
ptr++;
}
}
int main()
{
int arr[10] = {
1,2,3,4,5,6,7,8,9,10 };
int* p = arr;
int sz = sizeof(arr) / sizeof(arr[0]);
test(p, sz);//p是一级指针
return 0;
}
那么反着来,一级指针可以接收哪些参数呢?
void test(int* ptr)
{
//反着推-实参
}
int main()
{
int a = 10;
int* p = &a;
int arr[10] = {
0 };
test(arr);
test(p);
test(&a);//p是一级指针
return 0;
}
1.4 二级指针传参
那么我们的二级指针能接收哪些参数呢?
void test(char** ppc)
{
//反着推
}
int main()
{
char ch = 'a';
char* pc = &ch;
char** ppc = &pc;
char* arr[4] = {
0 };
test(&pc);
test(ppc);
test(arr);
}
始终记住二级指针拿来存放一级指针的地址。
小练习
int main()
{
int arr[3][5];
test1(arr);
test2(&arr);
return 0;
}
我们该怎么设计两个函数的参数呢?
void test1(int(*p)[5])
{
}
void test2(int(*p)[3][5])
{
}
注意第二个函数接受的是整个二维数组的地址,即用数组指针来接收。
2. 函数指针
嘿嘿相信这个有不少友友没听过吧,难不成函数也有地址?我们不妨拿编译器来验证验证。
2.1 函数指针基本介绍
#include <stdio.h>
void test()
{
printf("hehe\n");
}
int main()
{
printf("%p\n", test);
printf("%p\n", &test);
return 0; }

结果值可能不同,但两个值一定是一样的。我们知道了函数也有地址,并且和数组名差不多,函数名即可代表地址。
那么我们怎么用指针来存放函数的地址呢?
int add(int x, int y)
{
return x + y;
}
int test(char* str)
{
}
int main()
{
int (*pf)(int, int) = add;
int (*pt)(char*) = test;//函数指针变量
int ret1= (*pf)(2, 3);//这里的*其实是摆设,方便理解
int ret2 = add(2, 3);
int ret3 = pf(2, 3);
printf("%d %d %d\n", ret1, ret2, ret3);
return 0;
}

2.2 函数指针小练习
我们先来阅读两段有趣的代码。
//代码1
(*(void (*)())0)();
//代码2
void (*signal(int , void(*)(int)))(int);
我们该怎么理解上述代码呢?莫慌莫慌,我来为大家一 一解读。
(*(void (*)())0)();
1. void (* ) () 是函数指针类型
2. ( void (* ) () ) 加括号表示强制类型转换
3.( void (*) () ) 0 表示对0进行强制类型转换
而0地址就是函数地址,那么就是:
1.首先把0强制转换为一个函数指针类型,这就意味着0地址处放一个返回类型是void,无参的一个函数。
2.调用0地址处的这个函数。
void (*signal(int , void(*)(int)))(int);
1. void(* )(int)signal(int, void(* )(int))
2. signal是一个函数的声明
3. signed函数的参数,第一个是int类型的,第二个是void(*)(int)的函数指针类型
4. signed 函数的返回类型也是:void( * )(int)
改进:(方便理解)
typedef void(*pf_t)(int);//将函数指针类型重命名pf_t
pf_t signal(int, pf_t);
2.3 函数指针数组
数组是一个存放相同类型数据的存储空间,那我们已经学习了指针数组,
比如:
int *arr[10];
//数组的每个元素是int*
那要把函数的地址存到一个数组中,那这个数组就叫函数指针数组,那函数指针的数组如何定义呢?
int (*parr1[10])();//?
int *parr2[10]();//?
int (*)() parr3[10];//?
答案为:parr1 先和 [ ] 结合,说明 parr1是数组,数组的内容是什么呢?
是 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;
}
int main()
{
//指针数组
//字符指针数组
char* arr[5];
//整型指针数组
int* arr2[4];
//函数指针
int (*pf1)(int, int) = Add;
int (*pf2)(int, int) = Sub;
int (*pf3)(int, int) = Mul;
int (*pf4)(int, int) = Div;
//函数指针数组
int (*pf[4])(int, int) = {
Add, Sub, Mul, Div };
int i = 0;
for (i = 0; i < 4; i++)
{
int ret = pf[i](8, 2);
printf("%d\n", ret);
}
return 0;
}
后面我还会教大家写一个简易计算器。
2.4 指向函数指针数组的指针
我们知道指向函数指针数组的指针是一个 指针,指针指向一个 数组 ,数组的元素就是函数指针。这样一分析,我们便知道如何定义了。
void test(const char* str) {
printf("%s\n", str);
}
int main()
{
//函数指针pfun
void (*pfun)(const char*) = test;
//函数指针的数组pfunArr
void (*pfunArr[5])(const char* str);
pfunArr[0] = test;
//指向函数指针数组pfunArr的指针ppfunArr
void (*(*ppfunArr)[5])(const char*) = &pfunArr;
return 0;
}
3. 回调函数
这里我们简单介绍一下回调函数。深入将在后续以例子的形式发布。
void test()
{
printf("hehe\n");
}
void print(void(*p)())
{
p();
}
int main()
{
print(test);
return 0;
}

回调函数指通过函数指针的方式在一个函数内通过该指针的方式调用其它函数。
4. 结语
到这,我们的 《数组传参与函数指针》 已经接近尾声了,后续我还会持续更新C语言相关内容,学习永无止境,就会永远只会留给有准备的人。希望我的博客对大家有所帮助,如果喜欢的可以 点赞+收藏哦,也随时欢迎大家在评论区及时指出我的错误。
边栏推荐
- Nacos cluster partition
- [Node accesses MongoDB database]
- 第51篇-知乎请求头参数分析【2022-07-28】
- WeChat second-hand transaction small program graduation design finished works (8) graduation design thesis template
- Nacos service registration and discovery
- flutter 记录学习不一样的动画(一)
- ospf 导图
- curl命令获取外网ip
- Flutter records and learns different animations (1)
- Resampling a uniformly sampled signal
猜你喜欢
![[ 云原生之谜 ] 云原生背景 && 定义 && 相关技术详解?](/img/eb/0cd6891fcc00d2c01ba8bd7f8d0822.png)
[ 云原生之谜 ] 云原生背景 && 定义 && 相关技术详解?

The difference between BGP room and ordinary room in Beijing

OpenFeign realize load balance

Starlight does not ask passers-by!The young lady on the Wuhan campus successfully switched to software testing in three months and received a salary of 9k+13!

为什么突然间麒麟 9000 5G 版本,又有库存了?

Nacos服务注册与发现

ospf 导图

OpenFeign实现降级

Wechat second-hand transaction small program graduation design finished product (1) Development overview

Small application project works WeChat integral mall small program of graduation design (4) the opening report of finished product
随机推荐
spicy(二)unit hooks
进程优先级 nice
Mini Program Graduation Works WeChat Second-hand Trading Mini Program Graduation Design Finished Works (4) Opening Report
Mini Program Graduation Works WeChat Points Mall Mini Program Graduation Design Finished Products (6) Question Opening and Defense PPT
New interface - API interface for "Taote" keyword search
Pytorch框架学习记录7——卷积层
Mini Program Graduation Works WeChat Second-hand Trading Mini Program Graduation Design Finished Works (7) Interim Inspection Report
Sentinel Traffic Guard
发给你的好友,让 TA 请你吃炸鸡!
Process priority nice
一直空、一直爽,继续抄顶告捷!
Nacos service registration and discovery
spicy (1) basic definition
Redis server启动后会做哪些操作?
JIT vs AOT
Smart answer function, CRMEB knowledge payment system must have!
Monitor page deployment
FreeRTOS Personal Notes - Memory Management
小程序毕设作品之微信积分商城小程序毕业设计成品(5)任务书
CMake installation and testing
