指针进阶(二)

指针进阶

    • 5.函数指针
    • 6. 函数指针数组
    • 7. 指向函数指针数组的指针
    • 8. 回调函数
      • 案例:使用回调函数,模拟实现qsort(采用冒泡的方式)。
      • 案例:测试qsort排序结构体数据

5.函数指针

补:
&函数名就是函数的地址
函数名也是函数的地址

代码演示:
#include<stdio.h>
int Add(int x, int y)
{return x + y;
}
int main()
{//&函数名就是函数的地址//函数名也是函数的地址printf("%p\n", &Add);printf("%p\n", Add);
}

运行结果:
在这里插入图片描述

#include<stdio.h>
int Add(int x, int y)
{return x + y;
}
int main()
{//函数指针变量:int (*pf1)(int, int) = Add;//pf1就是函数指针变量//形式1:int (* pf2)(int, int) = &Add;int ret = (* pf2)(2, 3);//形式2:int (* pf2)(int, int) = Add;int ret = pf2(2, 3);//形式3int (* pf2)(int, int) = Add;int ret = Add(2, 3);printf("%d\n", ret);return 0;
}

来看以下两个代码:

//代码1
(* (void (*)( )) 0 )( );
//代码2
void (* signal (int , void(*)(int)) )(int);

分析:
//代码一:
将0强制类型转化为(void (*)( )),解引用函数指针类型,出入参数为空
//代码二:
是一次函数声明,声明的是signal函数
第一个是int类型
第二个是函数指针类型,该类型是void( * )(int)。该函数指针指向的函数参数是int,返回类型是void
signal函数的返回类型也是函数指针类型,该类型是void( * )(int),该函数指针指向的函数,参数是int,返回类型是void

代码2太复杂,如何简化:

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

6. 函数指针数组

数组是一个存放相同类型数据的存储空间,那我们已经学习了指针数组

//比如:
int *arr[10];
//数组的每个元素是int*

在这里插入图片描述

那要把函数的地址存到一个数组中,那这个数组就叫函数指针数组,那函数指针的数组如何定义呢?

int (*parr1[10])();
int *parr2[10]();
int (*)() parr3[10];

答案是:parr1
分析:
parr1 先和 [ ] 结合,说明 parr1是数组,数组的内容是什么呢?
是 int (*)( ) 类型的函数指针。

看如下代码进一步理解函数指针数组:

int Add(int x, int y)
{return x + y;
}int Sub(int x, int y)
{return x - y;
}
int main()
{int (*pf1)(int, int) = &Add;int (*pf2)(int, int) = &Sub;//数组中存放类型相同的多个元素int (*pfArr[4])(int, int) = { &Add, &Sub };//pfArr 是函数指针数组 - 存放函数指针的数组return 0;
}

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

例子:计算器

void menu()
{printf("****************************\n");printf("***  1. add      2. sub  ***\n");printf("***  3. mul      4. div  ***\n");printf("***  0. exit             ***\n");printf("****************************\n");
}
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()
{int input = 0;int x = 0;int y = 0;int ret = 0;do{menu();printf("请输入选择>:");scanf("%d", &input);switch (input){case 1:printf("请输入两个操作数》");scanf("%d%d", &x, &y);ret = Add(x, y);printf("%d\n", ret);break;case 2:printf("请输入两个操作数》");scanf("%d%d", &x, &y);ret = Sub(x, y);printf("%d\n", ret);break;case 3:printf("请输入两个操作数》");scanf("%d%d", &x, &y);ret = Mul(x, y);printf("%d\n", ret);break;case 4:printf("请输入两个操作数》");scanf("%d%d", &x, &y);ret = Div(x, y);printf("%d\n", ret);break;case 0:printf("退出游戏");default:printf("输入错误");}} while (input);return 0;
}

用函数指针实现

void menu()
{printf("****************************\n");printf("***  1. add      2. sub  ***\n");printf("***  3. mul      4. div  ***\n");printf("***  0. exit             ***\n");printf("****************************\n");
}
//+ - * / && || & | >> <<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()
{int input = 0;int x = 0;int y = 0;int ret = 0;do{menu();printf("请选择:>");scanf("%d", &input);//函数指针数组 - 转移表int (*pfArr[])(int, int) = {NULL, Add, Sub, Mul, Div};//                          0     1     2   3    4if (0 == input){printf("退出计算器\n");}else if (input >= 1 && input <= 4){printf("请输入2个操作数:");scanf("%d %d", &x, &y);ret = pfArr[input](x, y);printf("ret = %d\n", ret);}else{printf("选择错误,重新选择!\n");}} while (input);return 0;
}

7. 指向函数指针数组的指针

指向函数指针数组的指针是一个 指针
指针指向一个 数组 ,数组的元素都是 函数指针 ;
如何定义?
在这里插入图片描述

8. 回调函数

回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。

//代码演示:
void menu()
{printf("****************************\n");printf("***  1. add      2. sub  ***\n");printf("***  3. mul      4. div  ***\n");printf("***  0. exit             ***\n");printf("****************************\n");
}
//+ - * / && || & | >> <<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 calc(int (*pf)(int,int))
{int x = 0;int y = 0;int ret = 0;printf("请输入2个操作数:");scanf("%d %d", &x, &y);ret = pf(x, y);printf("ret = %d\n", ret);
}int main()
{int input = 0;do{menu();printf("请选择:>");scanf("%d", &input);switch (input){case 1:calc(Add);break;case 2:calc(Sub);break;case 3:calc(Mul);break;case 4:calc(Div);break;case 0:printf("退出计算器\n");break;default:printf("选择错误, 重新选择\n");break;}} while (input);return 0;
}

看图片理解下回调函数:
在这里插入图片描述

案例:使用回调函数,模拟实现qsort(采用冒泡的方式)。

一般冒泡排序:

//代码演示:
void print_arr(int arr[], int sz)
{int i = 0;for (i = 0; i < sz; i++){printf("%d ", arr[i]);}printf("\n");
}void bubble_sort(int arr[], int sz)
{//趟数int i = 0;for (i = 0; i < sz - 1; i++){//每一趟冒泡排序的过程int j = 0;for (j = 0; j < sz - 1 - i; j++){if (arr[j] > arr[j + 1]){int tmp = arr[j];arr[j] = arr[j + 1];arr[j + 1] = tmp;}}}
}int main()
{//数据int arr[] = { 9,8,7,6,5,4,3,2,1,0 };int sz = sizeof(arr) / sizeof(arr[0]);print_arr(arr, sz);bubble_sort(arr, sz);//冒泡排序print_arr(arr, sz);return 0;
}

运行结果:
在这里插入图片描述

这种冒泡排序缺陷:
在这里插入图片描述

qsort(采用冒泡的方式):
在这里插入图片描述

了解以下qsort()函数:

在这里插入图片描述

//代码案例:
#include <stdlib.h>
#include <string.h>
void print_arr(int arr[], int sz)
{int i = 0;for (i = 0; i < sz; i++){printf("%d ", arr[i]);}printf("\n");
}
int cmp_int(const void* e1, const void* e2)
{return *(int*)e1 - *(int*)e2;
}
//测试qsort排序整型数据
void test1()
{int arr[] = { 9,8,7,6,5,4,3,2,1,0 };int sz = sizeof(arr) / sizeof(arr[0]);print_arr(arr, sz);qsort(arr, sz, sizeof(arr[0]), cmp_int);print_arr(arr, sz);
}
int main()
{test1();return 0;
}

运行结果:
在这里插入图片描述

补:void*
1.void* 类型的指针 - 不能进行解引用操作符,也不能进行±整数的操作
2.void* 类型的指针是用来存放任意类型数据的地址
3.void* 无具体类型的指针

代码演示:
int main()
{char c = 'w';char* pc = &c;int a = 100;//int* p = &c;//不可以存放char*类型void* pv = &c;//存放char*pv = &a;//存放int*return 0;
}

案例:测试qsort排序结构体数据

结构体数据怎么比较呢?

  1. 按照年龄比较
#include<stdio.h>
#include<stdio.h>
struct Stu
{char name[20];int age;
};
int cmp_stu_by_age(const void* e1, const void* e2)
{return ((struct Stu*)e1)->age - ((struct Stu*)e2)->age;
}
void test1()
{struct Stu arr[] = { {"zhanhsan", 20}, {"lisi", 30}, {"wangwu", 12} };int sz = sizeof(arr) / sizeof(arr[0]);qsort(arr, sz, sizeof(arr[0]), cmp_stu_by_age);
}
int main()
{test1();return 0;
}
  1. 按照名字比较
 struct Stu
{char name[20];int age;
};
int cmp_stu_by_name(const void* e1, const void* e2)
{return strcmp(((struct Stu*)e1)->name, ((struct Stu*)e2)->name);
}
void test2()
{struct Stu arr[] = { {"zhanhsan", 20}, {"lisi", 30}, {"wangwu", 12} };int sz = sizeof(arr) / sizeof(arr[0]);qsort(arr, sz, sizeof(arr[0]), cmp_stu_by_name);
}
int main()
{test1();return 0;
}

💘不知不觉,指针进阶(二)以告一段落。通读全文的你肯定收获满满,不久的将来会继续更新指针进阶的内容,让我们继续为C语言学习共同奋进!!!

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

sql:SQL优化知识点记录(十二)

&#xff08;1&#xff09;读锁案例讲解 加读锁和写锁 查看是否上锁&#xff1a;In_use&#xff1a;变成了1 读写锁对我们数据产生哪些影响&#xff1a; 读锁&#xff1a;是共享锁&#xff0c;其他线程可以查看&#xff1a; 加了读锁&#xff1a;session1不能修改自己&#xf…

rhcsa学习3 文件

文件系统权限 有三种权限类别可应用&#xff1a;读取、写入和执行 查看文件和目录的权限及所有权 ls -l 显示有关权限和所有权的详细信息 长列表的第一个字符表示文件类型 - 表示常规文件、d 目录、l 软链接、其它字符代表硬件设备&#xff08;b和c&#xff09;或其他具有特殊用…

视频融合平台EasyCVR综合管理平台加密机授权报错invalid character是什么原因

视频融合平台EasyCVR综合管理平台具备视频融合汇聚能力&#xff0c;作为安防视频监控综合管理平台&#xff0c;它支持多协议接入、多格式视频流分发&#xff0c;可支持的主流标准协议有国标GB28181、RTSP/Onvif、RTMP等&#xff0c;以及支持厂家私有协议与SDK接入&#xff0c;包…

[C++]杨辉三角

目录 题目 解题思路 代码实现 获取数字 打印函数 主函数 全部代码 运行结果 题目 给定一个非负整数numRows &#xff0c;生成「杨辉三角」的前numRows行。 在「杨辉三角」中&#xff0c;每个数是它左上方和右上方的数的和。 解题思路 第k列的第i个数字的值第k-1列的(…

【漏洞复现】深信服科技EDR平台存在任意用户登录漏洞

漏洞描述 深信服终端检测响应平台EDR,通过云网端联动协同、威胁情报共享、多层级响应机制,帮助用户快速处置终端安全问题,构建轻量级、智能化、响应快的下一代终端安全系统。 该EDR系统存在任意用户登录漏洞&#xff0c;攻击者通过漏洞可以登录系统后台并获取服务器的敏感信息…

一次预制体丢失[XX prefab at index n is missing]的排查经历 及 【用代码查找场景中的预制体】

一、症状描述 &#xff08;1&#xff09;从【主场景】跳转【某场景】时&#xff0c;报错——预制体丢失[prefab is missing] &#xff08;2&#xff09;在编辑器单独启动该场景&#xff0c;没有报错 二、症状溯源 场景里面有预制体实例&#xff0c;但是我把预制体的资源删除…

vue2 vuex

一、Vuex 概述 Vuex 是一个 Vue 的 状态管理工具&#xff0c;状态就是数据。 大白话&#xff1a;Vuex 是一个插件&#xff0c;可以帮我们管理 Vue 通用的数据 (多组件共享的数据)。 使用场景 某个状态 在 很多个组件 来使用 (个人信息) 多个组件 共同维护 一份数据 (购物车) …

17-Sharding-jdbc

一 Sharding-JDBC介绍 1 背景 随着通信技术的革新&#xff0c;全新领域的应用层出不穷&#xff0c;数据存量随着应用的探索不断增加&#xff0c;数据的存储和计算模式无时无刻不面临着创 新。面向交易、大数据、关联分析、物联网等场景越来越细分&#xff0c;单 一数据库再也无…

MongoDB常用的比较符号和一些功能符号

比较符号 results collection.find({age: {$gt: 20}})功能符号 results collection.find({name: {$regex: ^M.*}})

伪微分反馈控制(Pesudo-Drivative Feedback Control——PDF)

运动控制-单轴伺服控制带宽分析&#xff08;二&#xff09; - 知乎 (zhihu.com) 伪微分反馈控制_百度百科 (baidu.com) 伺服电机控制器的参数整定_老马过河hhh的博客-CSDN博客 伪微分PIIP控制_yukee10的博客-CSDN博客

Discourse 能支持多少数量的主题

支持主题的数量和 ID 使用的数据类型有关。 根据我们从 Discourse 上 dump 出来的 SQL&#xff0c;我们看到 Discourse 的官方使用 Integer 作为 ID 的数据类型。 随后&#xff0c;我们查看了 pgsql 的官方文档&#xff0c;integer 是 4 字节的&#xff0c;能够存储的最大值为…

Pythonの类

Python是一种面向对象编程语言&#xff0c;因此类在Python中是很重要的概念。类是一种定义数据和行为的模板&#xff0c;可以创建对象并针对特定的问题对其进行操作。 在Python中&#xff0c;类的定义以关键字"class"开头&#xff0c;后跟类的名称。类可以包含方法和…