【C语言指针】指针和函数

文章目录

  • 一、前言
  • 二、指针函数
    • 2.1 概念
    • 2.2 定义
    • 2.3 具体例子
  • 三、函数指针
    • 3.1 概念
    • 3.2 定义
    • 3.3 具体例子
    • 3.4 回调函数
      • 3.4.1 概念
      • 3.4.2 例子1
      • 3.4.3 例子2
  • 四、函数指针数组
    • 4.1 概念
    • 4.2 定义
    • 4.3 具体例子
  • 五、函数指针数组的指针
    • 5.1 概念
    • 5.2 定义
    • 5.3 具体例子

一、前言

关于指针和函数文章从有两方面入手,一方面是将指针作为实参传给函数;另一方面则是函数指针这一类函数和指针结合的数据类型。

科普

函数名本质上是函数的地址,其用法跟数组名类似。

二、指针函数

2.1 概念

指针函数本质上是一个函数,此函数返回一个指针。

2.2 定义

char *func(char a, char b){}

2.3 具体例子

在函数指针的使用中,大多数情况下需要动态分配内存来存放返回的数据。如果使用局部变量,当函数运行结束后,局部变量会被释放,上层代码访问时可能会导致非法访问,从而引发程序段错误。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>char *my_strcat(char *str1, char *str2)
{char *p = NULL;char *p1 = NULL;char *ptemp = NULL;/* 动态内存分配 */p = (char *)malloc(20 * sizeof(char));if(!p){return NULL;}p1 = p;ptemp = str1;while(*ptemp != '\0'){*p1++ = *ptemp++;}ptemp = str2;while(*ptemp !=  '\0'){*p1++ = *ptemp++;}*p1 = '\0';printf("p=%s\n", p);return p;
}int main()
{char str1[10] = "hello ";char str2[10] = "world!";char *return_str = NULL;return_str = my_strcat(str1, str2);printf("return str[%s]\n", return_str);if(return_str)free(return_str);return 0;
}

三、函数指针

3.1 概念

函数指针本质是一个指针,它指向一个函数的地址,通过它来调用函数。

3.2 定义

首先定义一个函数:

int add(int x, int y){}

此时,函数的地址就是函数名。我们声明一个函数指针并把函数的地址赋值给这个函数指针。这里要注意,定义的函数和声明的函数指针的参数列表应该保持一致。

int (*pf)(int, int) = add;

下面让我们看一个表达式:

(*(void(*)())0)();

这一行代码的意思是:调用0地址处的函数,该函数无参,返回类型是void。

我们再来看一下signal函数的原型

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

函数名:signal

两个参数:int和void(*)(int) (第二个参数也是一个函数指针)

返回值:void(*)(int)(函数指针)

这样看起来很复杂,我们可以使用typedef来简化表达。

首先,我们可以把signal函数的原型写成

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

但是,这种结构语法是不允许,C语言的语法要求函数的返回类型必须明确地写在函数名之前。接下来我们使用typedef简化。

typedef void(*pfun_t)(int);//void(*)(int) pfun_t
pfun_t signal(int, pfun_t);//pfun_t -> void(*)(int)

3.3 具体例子

int add(int a, int b) { return a + b; }int main()
{int (*pf)(int, int) = add;int ret = pf(3,5);printf("ret=%d\n", ret);return 0;
}

3.4 回调函数

3.4.1 概念

回调函数是一种通过函数指针调用的函数。

3.4.2 例子1

Handle函数第二个参数是该函数的首地址,把这个首地址用函数指针接收int (*Callback)(int),在Handle函数中使用这个首地址,Callback(x),这时就会运行主函数Handle第二个参数的函数。

int Callback_1(int a)   ///< 回调函数1
{printf("Hello, this is Callback_1: a = %d ", a);return 0;
}int Callback_2(int b)  ///< 回调函数2
{printf("Hello, this is Callback_2: b = %d ", b);return 0;
}int Callback_3(int c)   ///< 回调函数3
{printf("Hello, this is Callback_3: c = %d ", c);return 0;
}int Handle(int x, int (*Callback)(int)) ///< 注意这里用到的函数指针定义
{Callback(x);
}int main()
{Handle(4, Callback_1);Handle(5, Callback_2);Handle(6, Callback_3);return 0;
}

3.4.3 例子2

这是一个常用在工作中的使用形式。M26_WorkStatus_TypeDef的第二个参数保存的函数的首地址。

/*********  工作状态处理  *********/
typedef struct
{uint8_t mStatus;uint8_t (* Funtion)(void); //函数指针的形式
} M26_WorkStatus_TypeDef;  //M26的工作状态集合调用函数/**********************************************
** >M26工作状态集合函数
***********************************************/
M26_WorkStatus_TypeDef M26_WorkStatus_Tab[] =
{    {GPRS_NETWORK_CLOSE,  M26_PWRKEY_Off  }, //模块关机{GPRS_NETWORK_OPEN,  M26_PWRKEY_On  }, //模块开机{GPRS_NETWORK_Start,   M26_Work_Init  }, //管脚初始化{GPRS_NETWORK_CONF,  M26_NET_Config  }, //AT指令配置{GPRS_NETWORK_LINK_CTC,  M26_LINK_CTC  }, //连接调度中心  {GPRS_NETWORK_WAIT_CTC, M26_WAIT_CTC  },  //等待调度中心回复 {GPRS_NETWORK_LINK_FEM, M26_LINK_FEM  }, //连接前置机{GPRS_NETWORK_WAIT_FEM, M26_WAIT_FEM  }, //等待前置机回复{GPRS_NETWORK_COMM,  M26_COMM   }, //正常工作{GPRS_NETWORK_WAIT_Sig,  M26_WAIT_Sig  },  //等待信号回复{GPRS_NETWORK_GetSignal,  M26_GetSignal  }, //获取信号值{GPRS_NETWORK_RESTART,  M26_RESET   }, //模块重启
}
/**********************************************
** >M26模块工作状态机,依次调用里面的12个函数   
***********************************************/
uint8_t M26_WorkStatus_Call(uint8_t Start)
{uint8_t i = 0;for(i = 0; i < 12; i++){if(Start == M26_WorkStatus_Tab[i].mStatus){          return M26_WorkStatus_Tab[i].Funtion();}}return 0;
}

四、函数指针数组

4.1 概念

函数指针数组本质是一个数组,数组中存放的函数指针。

4.2 定义

int (*pfArr[2])(int, int);

在函数指针的基础上,在函数名后面加上[]。

4.3 具体例子

int add(int a, int b) { return a + b; }
int sub(int a, int b) { return a - b; }
int mul(int a, int b) { return a * b; }
int division(int a, int b) { return a / b; }int main()
{/* 模拟键盘输入 */int input = 2;int ret;int (*pf[5])(int, int) = {NULL, add, sub, mul, division};ret = pf[input](3,5);printf("ret=%d\n", ret);return 0;
}

五、函数指针数组的指针

5.1 概念

函数指针数组的指针本质上是一个指针,指向一个函数指针数组。

5.2 定义

int(* (*p3)[4])(int, int);

在函数指针数组的基础上,在函数名前面加上*号并用小括号括起来。

5.3 具体例子

在这里面,一定是&pf才行,因为p1是指向数组的指针而不是指向数组首地址的指针,我们应该使用&把数组的地址取出来。

int add(int a, int b) { return a + b; }
int sub(int a, int b) { return a - b; }
int mul(int a, int b) { return a * b; }
int division(int a, int b) { return a / b; }int main()
{/* 模拟键盘输入 */int input = 2;int ret;int (*pf[5])(int, int) = {NULL, add, sub, mul, division};int (*(*p1)[5])(int, int) = &pf;ret = (*p1)[input](3,5);printf("ret=%d\n", ret);return 0;
}

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

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

相关文章

【Android】布局文件layout.xml文件使用控件属性android:layout_weight使布局较为美观,以RadioButton为例

目录 说明举例 说明 简单来说&#xff0c;android:layout_weight为当前控件按比例分配剩余空间。且单个控件该属性的具体数值不重要&#xff0c;而是多个控件的属性值之比发挥作用&#xff0c;例如有2个控件&#xff0c;各自的android:layout_weight的值设为0.5和0.5&#xff0…

hot100_21. 合并两个有序链表

将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。 示例 1&#xff1a; 输入&#xff1a;l1 [1,2,4], l2 [1,3,4] 输出&#xff1a;[1,1,2,3,4,4] 示例 2&#xff1a; 输入&#xff1a;l1 [], l2 [] 输出&#xff1a;[…

4 [危机13小时追踪一场GitHub投毒事件]

事件概要 自北京时间 2024.12.4 晚间6点起&#xff0c; GitHub 上不断出现“幽灵仓库”&#xff0c;仓库中没有任何代码&#xff0c;只有诱导性的病毒文件。当天&#xff0c;他们成为了 GitHub 上 star 增速最快的仓库。超过 180 个虚假僵尸账户正在传播病毒&#xff0c;等待不…

Spring Boot项目中解决跨域问题(四种方式)

目录 一&#xff0c;跨域产生的原因二&#xff0c;什么情况下算跨域三&#xff0c;实际演示四&#xff0c;解决跨域的方法 1&#xff0c;CrossOrigin注解2&#xff0c;添加全局过滤器3&#xff0c;实现WebMvcConfigurer4&#xff0c;Nginx解决跨域5&#xff0c;注意 开发项目…

浅析DNS污染及防范

DNS污染&#xff08;DNS Cache Poisoning&#xff09;是一种网络攻击手段&#xff0c;通过篡改DNS服务器的缓存数据&#xff0c;将域名解析结果指向错误的IP地址&#xff0c;从而误导用户访问恶意网站或无法访问目标网站。这种攻击利用了DNS协议的特性&#xff0c;例如“只认第…

五. Redis 配置内容(详细配置说明)

五. Redis 配置内容(详细配置说明) 文章目录 五. Redis 配置内容(详细配置说明)1. Units 单位配置2. INCLUDES (包含)配置3. NETWORK (网络)配置3.1 bind(配置访问内容)3.2 protected-mode (保护模式)3.3 port(端口)配置3.4 timeout(客户端超时时间)配置3.5 tcp-keepalive()配置…

单细胞分析基础-第一节 数据质控、降维聚类

scRNA_pipeline\1.Seurat 生物技能树 可进官网查询 添加链接描述 分析流程 准备:R包安装 options("repos"="https://mirrors.ustc.edu.cn/CRAN/") if(!require("BiocManager")) install.packages("BiocManager",update = F,ask =…

Qt常用控件 输入类控件

文章目录 1.QLineEdit1.1 常用属性1.2 常用信号1.3 例子1&#xff0c;录入用户信息1.4 例子2&#xff0c;正则验证手机号1.5 例子3&#xff0c;验证输入的密码1.6 例子4&#xff0c;显示密码 2. QTextEdit2.1 常用属性2.2 常用信号2.3 例子1&#xff0c;获取输入框的内容2.4 例…

大模型培训讲师老师叶梓分享:DeepSeek多模态大模型janus初探

以下视频内容为叶梓分享DeepSeek多模态大模型janus的部署&#xff0c;并验证其实际效果&#xff0c;包括图生文和文生图两部分。 叶梓老师人工智能培训分享DeepSeek多模态大模型janus初探 DeepSeek 的多模态大模型 Janus 是一款强大的 AI 模型&#xff0c;专注于图像和文本的多…

Linux系统上安装与配置 MySQL( CentOS 7 )

目录 1. 下载并安装 MySQL 官方 Yum Repository 2. 启动 MySQL 并查看运行状态 3. 找到 root 用户的初始密码 4. 修改 root 用户密码 5. 设置允许远程登录 6. 在云服务器配置 MySQL 端口 7. 关闭防火墙 8. 解决密码错误的问题 前言 在 Linux 服务器上安装并配置 MySQL …

17.2 图形绘制7

版权声明&#xff1a;本文为博主原创文章&#xff0c;转载请在显著位置标明本文出处以及作者网名&#xff0c;未经作者允许不得用于商业目的。 17.2.9 字体 17.2.9.1 Font类 Font类定义特定的文本格式&#xff0c;包括字体、字号和样式特性。 Font常用属性&#xff1a; Na…

浅析DDOS攻击及防御策略

DDoS&#xff08;分布式拒绝服务&#xff09;攻击是一种通过大量计算机或网络僵尸主机对目标服务器发起大量无效或高流量请求&#xff0c;耗尽其资源&#xff0c;从而导致服务中断的网络攻击方式。这种攻击方式利用了分布式系统的特性&#xff0c;使攻击规模更大、影响范围更广…

90,【6】攻防世界 WEB Web_php_unserialize

进入靶场 进入靶场 <?php // 定义一个名为 Demo 的类 class Demo { // 定义一个私有属性 $file&#xff0c;默认值为 index.phpprivate $file index.php;// 构造函数&#xff0c;当创建类的实例时会自动调用// 接收一个参数 $file&#xff0c;用于初始化对象的 $file 属…

HarmonyOS NEXT:保存应用数据

用户首选项使用 用户首选项的特点 数据体积小、访问频率高、有加载速度要求的数据如用户偏好设置、用户字体大小、应用的配置参数。 用户搜选项&#xff08;Preferences&#xff09;提供了轻量级配置数据的持久化能力&#xff0c;支持订阅数据变化的通知能力。不支持分布式同…

C++编程语言:抽象机制:模板(Bjarne Stroustrup)

目录 23.1 引言和概观(Introduction and Overview) 23.2 一个简单的字符串模板(A Simple String Template) 23.2.1 模板的定义(Defining a Template) 23.2.2 模板实例化(Template Instantiation) 23.3 类型检查(Type Checking) 23.3.1 类型等价(Type Equivalence) …

OVS-DPDK

dpdk介绍及应用 DPDK介绍 DPDK&#xff08;Data Plane Development Kit&#xff09;是一组快速处理数据包的开发平台及接口。有intel主导开发&#xff0c;主要基于Linux系统&#xff0c;用于快速数据包处理的函 数库与驱动集合&#xff0c;可以极大提高数据处理性能和吞吐量&…

基础项目实战——学生管理系统(c++)

目录 前言一、功能菜单界面二、类与结构体的实现三、录入学生信息四、删除学生信息五、更改学生信息六、查找学生信息七、统计学生人数八、保存学生信息九、读取学生信息十、打印所有学生信息十一、退出系统十二、文件拆分结语 前言 这一期我们来一起学习我们在大学做过的课程…

基于微信小程序的医院预约挂号系统设计与实现(LW+源码+讲解)

专注于大学生项目实战开发,讲解,毕业答疑辅导&#xff0c;欢迎高校老师/同行前辈交流合作✌。 技术范围&#xff1a;SpringBoot、Vue、SSM、HLMT、小程序、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、安卓app、大数据、物联网、机器学习等设计与开发。 主要内容&#xff1a;…

[Linux]从零开始的STM32MP157 U-Boot移植

一、前言 在上一次教程中&#xff0c;我们了解了STM32MP157的启动流程与安全启动机制。我们还将FSBL的相关代码移植成功了。大家还记得FSBL的下一个步骤是什么吗&#xff1f;没错&#xff0c;就是SSBL&#xff0c;而且常见的我们将SSBL作为存放U-Boot的地方。所以本次教程&…

单细胞-第四节 多样本数据分析,下游画图

文件在单细胞\5_GC_py\1_single_cell\2_plots.Rmd 1.细胞数量条形图 rm(list ls()) library(Seurat) load("seu.obj.Rdata")dat as.data.frame(table(Idents(seu.obj))) dat$label paste(dat$Var1,dat$Freq,sep ":") head(dat) library(ggplot2) lib…