C语言超详细讲解指向函数的指针

目录
  • 一、函数的指针
  • 二、指向函数的指针变量
  • 三、调用函数的两种方式
  • 四、指向函数的指针的作用
  • 五、用指向函数的指针作函数参数(重点)
  • 六、为什么要将指向函数的指针变量作为函数的形参(重点)

一、函数的指针

首先,函数名代表函数的起始地址,调用函数时,程序会从函数名获取到函数起始地址,并从该地址起执行函数中的代码,函数名就是函数的指针,所以我们可以定义一个指向函数的指针变量,用来存放函数的起始地址,这样一来,就可以通过该变量来调用其所指向的函数。

二、指向函数的指针变量

定义指向函数的指针变量

返回值类型(* 指针变量名)(形参类型列表);

例如:int(*p)(int, int);,这行代码定义了一个可以指向返回值为整型且有两个整型形参函数的指针变量p,符合返回值为整型且有两个整型形参的函数都可以将其地址(即其函数名)赋给p。

使用指向函数的指针变量

在使用指向函数的指针变量时,只需要将函数名赋给指向函数的指针变量即可,因为函数名就是该函数的入口地址。

由于指向函数的指针变量保存了函数的地址,则该指针变量就指向了对应的函数。例如,求最大值的函数命名为max,如果将其函数名赋给指向函数的指针变量p(即p = max)后,则p就指向了max函数,并且可以通过(*p)(a, b);的方式来调用max函数,因为指针变量p保存了max函数的地址,那么*p就是max。需要注意的是,其中*p前的*可以省略,故也可以写成p(a, b);

三、调用函数的两种方式

引例:自定义max函数,求整数ab中的较大者并返回给主调函数,不考虑两数相等的情况通过函数名调用函数

#include <stdio.h>
int max(int, int); // max函数的函数声明
int main()
{
	int a, b;
	printf("请输入两个整数:");
	scanf("%d%d", &a, &b);
	printf("两数中的较大者的值为%d\n", max(a, b));
	return 0;
}
int max(int a, int b)
{
	if (a > b)
		return a;
	else
		return b;
}

通过指向函数的指针变量调用函数

#include <stdio.h>
int max(int, int); // max函数的函数声明
int main()
{
	int a, b;
	int(*p)(int, int); // 定义指向函数的指针p
	p = max; // p指向max函数
	printf("请输入两个整数:");
	scanf("%d%d", &a, &b);
	printf("两数中的较大者的值为%d\n", (*p)(a, b)); // (*p)(a, b) 也可写为 p(a, b)
	return 0;
}
int max(int a, int b)
{
	if (a > b)
		return a;
	else
		return b;
}

四、指向函数的指针的作用

看到这里有人可能会问,既然函数名就可以调用函数,为什么还要弄个奇奇怪怪的指针?这难道不是多此一举嘛?难倒是为了装13?不管怎样,使用指向函数的指针来调用函数肯定不是为了装13,主要的原因是:用函数名调用函数时比较死板,只能调用所指定的一个函数,而通过指针变量调用函数会比较灵活,可以根据不同的情况调用不同的函数。以下面的程序为例: 输入两个整数,然后让用户选择1或2,选1则调用max函数求出两个整数的较大者并将其输出,选2则调用min函数求出两个整数的较小者并将其输出,不考虑两数相等的情况

#include <stdio.h>
int max(int, int); // max函数的函数声明
int min(int, int); // min函数的函数声明
int main()
{
	int a, b, c, n;
	int (*p)(int, int); // 定义指向函数的指针p
	p = NULL; // 先将p赋为空
	printf("请输入两个整数:");
	scanf("%d%d", &a, &b);
	printf("输入1获取两个数中的较大者,输入2获取两个数中的较小者,请输入:");
	scanf("%d", &n);
	if (n == 1)
		p = max; // p指向max函数
	else if (n == 2)
		p = min; // p指向min函数
	c = p(a, b); // 调用p所指向的函数
	if (n == 1)
		printf("两个数中的较大者为:%d\n", c);
	else
		printf("两个数中的较小者为:%d\n", c);
	return 0;
}
int max(int a, int b)
{
	if (a > b)
		return a;
	else
		return b;
}
int min(int a, int b)
{
	if (a < b)
		return a;
	else
		return b;
}

五、用指向函数的指针作函数参数(重点)

指向函数的指针变量的一个重要用途是把函数的入口地址作为实参传递给其他函数。以下面的程序为例:

有两个整数ab,由用户输入1,2,3来决定进行什么操作。输入1则求出ab中的较大者,输入2则求出ab中的较小者,输入3则求出ab之和,不考虑两个数相等的情况

#include <stdio.h>
int fun(int, int, int (*p)(int, int));
int max(int, int);
int min(int, int);
int sum(int, int);
int main()
{
	int a = 34, b = -21, n;
	printf("输入1获得两数中的较大者,输入2获得两数中的较小者,输入3获得两个数的和,请输入:");
	scanf("%d", &n);
	if (n == 1)
		printf("两数中的较大者为%d\n", fun(a, b, max));
		// 向fun函数中传参时,只需要传入两个整数或整型变量以及想要在fun函数内执行的函数的函数名即可
		// 函数名会传递给对应的形参指针变量
	else if (n == 2)
		printf("两数中的较小者为%d\n", fun(a, b, min));
	else if (n == 3)
		printf("两个数的和为%d\n", fun(a, b, sum));
	return 0;
}
// fun函数的作用是获取最终结果
int fun(int x, int y, int (*p)(int, int))
{
	int result;
	result = p(x, y); // 用result接收最终结果,不管执行max,min,sum中的哪个函数,fun函数内部代码都不用改变
	return result;
}
int max(int x, int y)
{
	if (x > y)
		return x;
	else
		return y;
}
int min(int x, int y)
{
	if (x < y)
		return x;
	else
		return y;
}
int sum(int x, int y)
{
	return x + y;
}

从上面的程序中可以清晰地看出,不管调用maxminsum中的哪个函数,fun函数均没有任何变化,在fun函数内部的result只用来获取结果并将结果返回,但不去判断到底要通过哪个函数来计算这一结果,主调函数向其传入哪个函数,其内部就执行哪个函数。maxminsum函数用来计算,fun函数用来获取结果,这体现出了整个程序的模块化。

六、为什么要将指向函数的指针变量作为函数的形参(重点)

举一个例子,我们在学习数组的过程中,想要把数组中的所有元素输出,通常会接触一个新词,遍历。其实遍历的含义并不是将一个结构中的元素输出的过程,然而我在初学时便认为遍历等同于输出,这是我在初学时对遍历这个词不准确的理解,我相信也一定有人跟我一样这样认为。其实遍历指的是依次访问某种结构中的所有元素,至于对这些元素怎么操作,由程序员自己决定,比如,你想输出所有的元素,那就可以调用输出函数将每次获取到的元素输出;你想将所有元素的值翻倍,那就调用对应的翻倍函数将每次获取到的元素翻倍。但是这样一来,遍历函数的功能就变得十分单一,只能进行一种操作,要么是遍历并输出,要么是遍历并翻倍,如果在一个程序中,开始想要遍历并翻倍,后又想要遍历并输出,就只能定义两个函数来实现,但是我们发现不管对元素怎么操作,访问每个元素的代码都是相同,并且只要想对结构中的每个元素进行操作,首先要做的就是访问每个元素。但是如果为了输出而定义一个先遍历后输出的函数,为了将每个元素的值翻倍而定义一个先遍历后翻倍的函数,这样遍历元素的代码就是重复的。那要怎么办呢?既然遍历的操作是重复的,那我们就定义一个专门的遍历函数,该函数只用来访问元素,再定义其它多个操作数据的函数,至于我们对遍历后的数据执行什么样的操作,我们只需要将对应的操作函数通过遍历函数的形参接收过来,这样就可以实现在遍历函数中根据不同情况执行不同操作的目的,如此一来既体现出了程序设计的结构化与模块化,又减少了编程时的代码量。

到此这篇关于C语言超详细讲解指向函数的指针的文章就介绍到这了,更多相关C语言指向函数的指针内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • C语言 函数指针(指向函数的指针)详解

    一个函数总是占用一段连续的内存区域,函数名在表达式中有时也会被转换为该函数所在内存区域的首地址,这和数组名非常类似.我们可以把函数的这个首地址(或称入口地址)赋予一个指针变量,使指针变量指向函数所在的内存区域,然后通过指针变量就可以找到并调用该函数.这种指针就是函数指针. 函数指针的定义形式为: returnType (*pointerName)(param list); returnType 为函数返回值类型,pointerNmae 为指针名称,param list 为函数参数列表.参数列表中

  • C语言超详细讲解指向函数的指针

    目录 一.函数的指针 二.指向函数的指针变量 三.调用函数的两种方式 四.指向函数的指针的作用 五.用指向函数的指针作函数参数(重点) 六.为什么要将指向函数的指针变量作为函数的形参(重点) 一.函数的指针 首先,函数名代表函数的起始地址,调用函数时,程序会从函数名获取到函数起始地址,并从该地址起执行函数中的代码,函数名就是函数的指针,所以我们可以定义一个指向函数的指针变量,用来存放函数的起始地址,这样一来,就可以通过该变量来调用其所指向的函数. 二.指向函数的指针变量 定义指向函数的指针变量

  • C语言超详细讲解字符串函数和内存函数

    目录 字符串函数 长度不受限制的字符串函数 strlen strcpy strcat strcmp 长度受限制的字符串函数介绍 strncpy strncat strncmp 字符串查找以及错误报告 strstr strtok strerror 内存操作函数 memcpy memmove memcmp 字符串函数 长度不受限制的字符串函数 strlen size_t strlen ( const char * str ) 求字符串长度: 字符串以'\0' 作为结束标志,strlen函数返回的是在

  • C语言超详细讲解getchar函数的使用

    目录 一.getchar 函数 二.缓冲区 1.什么是缓冲区 2.为什么要存在缓冲区 3.缓冲区的类型 4.缓冲区的刷新 三.getchar 函数的正确使用 1.getchar 的换行问题 2.getchar 与 scanf 的混合使用 一.getchar 函数 从上面的介绍来看,我们要正确使用getchar函数,首先得了解什么是缓冲区. 二.缓冲区 1.什么是缓冲区 缓冲区又称为缓存,它是内存空间的一部分. 也就是说,在内存空间中预留了一定的存储空间,这些存储空间用来缓冲输入或输出的数据,这部

  • C语言超详细讲解函数栈帧的创建和销毁

    目录 1.本节目标 2.相关寄存器 3.相关汇编指令 4.什么是函数栈帧 5.什么是调用堆栈 6.函数栈帧的创建和销毁 (1).main函数栈帧的创建与初始化 (2).main函数的核心代码 (3).Add函数的调用过程 (4).Add函数栈帧的销毁 (5).调用完成 7.对开篇问题的解答 1.本节目标 C语言绝命七连问,你能回答出几个? 局部变量是如何创建的?为什么局部变量不初始化其内容是随机的?有些时候屏幕上输出的"烫烫烫"是怎么来的?函数调用时参数时如何传递的?传参的顺序是怎样的

  • C语言超详细讲解函数指针的运用

    目录 前言 计算器的例子 回调函数 转移表 前言 前面我们学习了各种各样的指针类型,有些指针可以说是稀奇百怪,特别是函数指针,有些朋友可能觉得,函数指针有些多余,调用函数为什么要用指针调用,直接调用不好吗? 接下来我们从具体的实例来回答同学们的问题,加深对函数指针的理解. 计算器的例子 接下来我们写一个简单的计算器程序,完成不同的计算功能比如加减乘除: #include <stdio.h> //相加函数 int add(int a, int b) { return a + b; } //相减函

  • C语言 超详细讲解链接器

    目录 1 什么是链接器 2 声明与定义 3 命名冲突 3.1 命名冲突 3.2 static修饰符 4 形参.实参.返回值 5 检查外部类型 6 头文件 1 什么是链接器 典型的链接器把由编译器或汇编器生成的若干个目标模块,整合成一个被称为载入模块或可执行文件的实体–该实体能够被操作系统直接执行. 链接器通常把目标模块看成是由一组外部对象组成的.每个外部对象代表着机器内存中的某个部分,并通过一个外部名称来识别.因此,==程序中的每个函数和每个外部变量,如果没有被声明为static,就都是一个外部

  • C语言 超详细讲解库函数

    目录 1 返回整数的getchar函数 2 更新顺序文件 3 缓冲输出与内存分配 4 库函数 练习 1 返回整数的getchar函数 代码: #include<stdio.h> int main() { char c; while((c = getchar())!=EOF)//getchar函数的返回值为整型 putchar(c); return 0; } 上述代码有三种可能: 某些合法的输入字符在被"截断"后使得c的取值与EOF相同,程序将在复制的中途停止. c根本不可能

  • C语言超详细讲解轮转数组

    目录 题目描述 实例 解题思路 1. 先整体逆转 2.逆转子数组[0, k - 1] 3.逆转子数组[k, numsSize - 1] 易错点 代码 题目描述 给你一个数组,将数组中的元素向右轮转 k 个位置,其中 k 是非负数.OJ链接 实例 1.实例1 输入: nums = [1,2,3,4,5,6,7], k = 3输出: [5,6,7,1,2,3,4]解释:向右轮转 1 步: [7,1,2,3,4,5,6]向右轮转 2 步: [6,7,1,2,3,4,5]向右轮转 3 步: [5,6,7

  • C语言超详细讲解数据结构中双向带头循环链表

    目录 一.概念 二.必备工作 2.1.创建双向链表结构 2.2.初始化链表 2.3.动态申请节点 2.4.打印链表 2.5.销毁链表 三.主要功能 3.1.在pos节点前插入数据 尾插 头插 3.2.删除pos处节点数据 尾删 头删 3.3.查找数据 四.总代码 List.h 文件 List.c 文件 Test.c 文件 五.拓展 一.概念 前文我们已经学习了单向链表,并通过oj题目深入了解了带头节点的链表以及带环链表,来画张图总体回顾下: 在我们学习的链表中,其实总共有8种,都是单双向和带不带

  • C语言超详细讲解文件的操作

    目录 一.为什么使用文件 二.什么是文件 1.程序文件 2.数据文件 3.文件名 三.文件指针 四.文件的打开和关闭 五.文件的顺序读写 六.文件的随机读写 fseek ftell rewind 七.文件结束判定 一.为什么使用文件 当我们写一些项目的时候,我们应该要把写的数据存储起来.只有我们自己选择删除数据的时候,数据才不复存在.这就涉及到了数据的持久化的问题,为我们一般数据持久化的方法有,把数据存在磁盘文件.存放到数据库等方式.使用文件我们可以将数据直接存放在电脑的硬盘上,做到了数据的持久

随机推荐