【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.mzph.cn/news/894310.shtml

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

相关文章

初识c语言(关键字)

前言&#xff1a; 注意&#xff1a; 变量的名称不能是关键字 变量的命名&#xff1a; 1、有意义 int age; flat salary; 2、名字必须是字母、数字、下划线组成&#xff0c;不能有特殊字符&#xff0c; 同时不能以数字开头 3、变量的命名不能是关键字 内容&#xff1a; …

FFmpeg(7.1版本)编译:Ubuntu18.04交叉编译到ARM

一、本地编译与交叉编译 1.本地编译 ① 本地编译:指的是在目标系统上进行编译的过程 , 生成的可执行文件和函数库只能在目标系统中使用。 如 : 在 Ubuntu中,本地编译的可执行文件只能在Ubuntu 系统中执行 , 无法在 Windows / Mac / Android / iOS 系统中使用 ; 在 Ubuntu…

对比DeepSeek、ChatGPT和Kimi的学术写作撰写引言能力

引言 引言部分引入研究主题&#xff0c;明确研究背景、问题陈述&#xff0c;并提出研究的目的和重要性&#xff0c;最后&#xff0c;概述研究方法和论文结构。 下面我们使用DeepSeek、ChatGPT4以及Kimi辅助引言撰写。 提示词&#xff1a; 你现在是一名[计算机理论专家]&#…

LabVIEW微位移平台位移控制系统

本文介绍了基于LabVIEW的微位移平台位移控制系统的研究。通过设计一个闭环控制系统&#xff0c;针对微位移平台的通信驱动问题进行了解决&#xff0c;并提出了一种LabVIEW的应用方案&#xff0c;用于监控和控制微位移平台的位移&#xff0c;从而提高系统的精度和稳定性。 项目背…

javaEE-6.网络原理-http

目录 什么是http? http的工作原理&#xff1a; 抓包工具 fiddler的使用 HTTP请求数据: 1.首行:​编辑 2.请求头(header) 3.空行&#xff1a; 4.正文&#xff08;body&#xff09; HTTP响应数据 1.首行&#xff1a;​编辑 2.响应头 3.空行&#xff1a; 4.响应正文…

开启 AI 学习之旅:从入门到精通

最近 AI 真的超火&#xff0c;不管是工作还是生活里&#xff0c;到处都能看到它的身影。好多小伙伴都跑来问我&#xff0c;到底该怎么学 AI 呢&#xff1f;今天我就把自己学习 AI 的经验和心得分享出来&#xff0c;希望能帮到想踏入 AI 领域的朋友们&#xff01; 一、学习内容…

【JAVA基础】双亲委派

双亲委派可以简单理解为, 当收到加载请求时, 会依次向上加载 ; 只有当父类加载器无法完成加载请求时&#xff0c;子类加载器才会尝试自己去加载。 工作原理 类加载请求传递&#xff1a;当应用程序需要加载一个类时&#xff0c;比如通过ClassLoader.loadClass()方法&#xff0…

嵌入式经典面试题之操作系统(一)

文章目录 1 请你说说常用的Linux命令有哪些&#xff1f;2 在linux中如何创建一个新的目录&#xff1f;3 Linux中查看进程运行状态的指令、tar解压文件的参数。4 在linux中&#xff0c;文件权限如何修改&#xff1f;5 怎样以root权限运行某个程序&#xff1f;6 在linux里如何查看…

排查定位jar包大文件

解压 JAR 包&#xff1a; mkdir jar_contents unzip your-jar-file.jar -d jar_contents统计各文件大小&#xff1a; du -ah jar_contents | sort -rh | head -n 20这会列出 JAR 包中最大的文件或目录&#xff0c;方便你定位大文件。 方法 2&#xff1a;使用 jar 工具查看文件…

OpenCV:闭运算

目录 1. 简述 2. 用膨胀和腐蚀实现闭运算 2.1 代码示例 2.2 运行结果 3. 闭运算接口 3.1 参数详解 3.2 代码示例 3.3 运行结果 4. 闭运算的应用场景 5. 注意事项 相关阅读 OpenCV&#xff1a;图像的腐蚀与膨胀-CSDN博客 OpenCV&#xff1a;开运算-CSDN博客 1. 简述…

Python-基于PyQt5,pdf2docx,pathlib的PDF转Word工具

前言:日常生活中,我们常常会跟WPS Office打交道。作表格,写报告,写PPT......可以说,我们的生活已经离不开WPS Office了。与此同时,我们在这个过程中也会遇到各种各样的技术阻碍,例如部分软件的PDF转Word需要收取额外费用等。那么,可不可以自己开发一个小工具来实现PDF转…

C++中的类与对象(下)

上一节我们将类与对象中一个比较难的也是一个比较重要的模块学习了&#xff0c;在这节主要是一些细节上的补充。 文章目录 目录 前言 一、初始化列表 初始化列表的性质 初始化列表的总结 二、类型转换 C中的类型转换 三、static成员 static的特点 一般情况下构造函数调用顺序&a…

rust跨平台调用动态库

动态库在不同的操作系统&#xff0c;扩展名是不一样的&#xff0c;所以要做处理: static LIB: Lazy<Mutex<Option<Library>>> Lazy::new(|| Mutex::new(None));type CreateFunc unsafe extern "C" fn(*const c_char, *const c_char) -> c_int…

四、jQuery笔记

(一)jQuery概述 jQuery本身是js的一个轻量级的库,封装了一个对象jQuery,jquery的所有语法都在jQuery对象中 浏览器不认识jquery,只渲染html、css和js代码,需要先导入jQuery文件,官网下载即可 jQuery中文说明文档:https://hemin.cn/jq/ (二)jQuery要点 1、jQuery对象 …

Versal - 基础4(VD100+Versal IBERT)

1. 简介 在之前的一篇博文中&#xff0c;我分享了在 Zynq Ultrascale MPSoC 中使用 IBERT 的方法。 《Vivado - 集成眼图分析仪 Serial I/O IBERT 误码率_vivado ibert-CSDN博客》 本文进一步探讨 Versal 中使用 IBERT 的方法。 2. 硬件平台 芯片&#xff1a;XCVE2302-SF…

《HelloGitHub》第 106 期

兴趣是最好的老师&#xff0c;HelloGitHub 让你对编程感兴趣&#xff01; 简介 HelloGitHub 分享 GitHub 上有趣、入门级的开源项目。 github.com/521xueweihan/HelloGitHub 这里有实战项目、入门教程、黑科技、开源书籍、大厂开源项目等&#xff0c;涵盖多种编程语言 Python、…

英语语法 第一天

I’m a student. 我是个学生 我是个新东方的学生 I’m a student of New Oriental School 我爱你 I love you 我在心中爱你 I love you in my heart. 这是一朵花 This is a flower 这是一朵在公园里的花 This is a flower in the park.(修饰部分在修饰词后面) 主干…

Day50:字典的合并

在 Python 中&#xff0c;字典是一个可变的数据类型&#xff0c;经常需要将多个字典合并成一个字典。合并字典的方式有多种&#xff0c;今天我们将学习几种常见的方法。 1. 使用 update() 方法合并字典 update() 方法可以用来将一个字典中的键值对添加到另一个字典中。如果目…

USB鼠标的数据格式

USB鼠标的数据格式由HID&#xff08;Human Interface Device&#xff09;协议定义&#xff0c;通常包含3个字节的标准数据&#xff0c;具体格式如下&#xff1a; 字节内容描述第1字节按键状态Bit 0: 左键按下&#xff08;1&#xff09;<br>Bit 1: 右键按下&#xff08;1…

为AI聊天工具添加一个知识系统 之79 详细设计之20 正则表达式 之7

本文要点 要点 “正则表达式” 本来是计算机科学计算机科学的一个概念。本项目将它推广&#xff08;扩张&#xff09;到认知科学的“认知范畴”概念&#xff0c; 聚合&#xff08;收敛&#xff09;到 神经科学 的“神经元”概念。 做法是&#xff1a;用reg 来系统化定义认知…