C语言编程函数指针入门精讲教程

目录
  • 一、指针引子
  • 二、使用步骤
    • 1.取函数地址
    • 2.创建函数指针
    • 3.通过函数指针调用函数的两种方法
  • 三、函数指针进阶
  • 总结

一、指针引子

示例:我们常常接触的指针大多有如下几类:
整形指针-存放整形地址,指向整形
字符指针-存放字符地址,指向字符
数组指针-存放数组地址(注意不是数组首元素地址),指向数组

由以上三个例子,我们能总结指针的共同点:存放某个类型变量的地址,指向那个类型的变量,但是在讲函数指针首先有一个问题:函数也有地址吗?我们用一段简单的代码来验证一下即可。

#include<stdio.h>
int Add(int x,int y)
{
   return x+y;
}
int main()
{
   printf("%p\n",&Add);
   return 0;
}

屏幕上打印出地址:

所以答案是有的,函数也存在地址,那么也就衍生出了今天的知识点-函数指针。

二、使用步骤

1.取函数地址

我们知道&数组名,取出的是数组的地址。单独一个数组名,取出的是数组首元素的地址。但是对于函数来说:函数名==&函数名

我们代码验证一下(示例):

#include<stdio.h>
int Add(int x,int y)
{
   return x+y;
}
int main()
{
   printf("%p\n",&Add);
   printf("%p\n",Add);
   return 0;
}

显然,打印出来的地址是一样的,但是这个时候也会有同学跳出来说:“那数组名和&数组名打印出来的地址还一样呢,但意义明显不一样啊”。但是你想想,函数也没有首元素等其他玩意啊,它就是它本身啊,它也不会出现什么函数首元素啊。

所以再次声明:
在函数指针这一块 函数名==&函数名,它的意义和值,都是一样的

2.创建函数指针

我们知道,数组指针用来存放数组地址,整形指针用来存放整形地址。。。函数指针也不例外,它用来存放函数地址,我们现在定义一个p来存放Add地址,那它的类型怎么创建?我们来看一下具体步骤:

1.p是一个指针对吧,给它一个*是不是必须的 p变成了 * p。为了确保 * 和 p结合(如果没有括号,*或者p有可能会与其他的一些符号结合,具体参见符号优先级)那我在 * p外面加一个括号便于观看也没有问题吧,也就是(*p)

2.那函数总得有参数啊,比如这里是Add(int x,int y)。参数x和y的类型是int
你指针指向的函数是不是要找一下它的参数。所以(*p)(int,int)

3.那函数还有一个性质啊,有没有返回值,要是有的话,类型呢? 这里以Add为例,它是返回int型,所以我们指针也返回int 型 即int(*p)(int,int)

到这里Add函数指针的类型就创建完成啦即为*int(p)(int ,int)

需要注意的是:不同函数的参数类型和返回值类型是不一样的,到时候需要根据不同函数对类型进行转换,这里只是以Add函数为例,其他函数以此类推

ps:一个快速判别类型的方法——去掉变量的名字,剩下的就是类型
代码如下(示例):

	int a = 10;//去掉a 类型int
	int arr[10] = { 0 };//去掉arr 类型int [10]
	int(*parr)[10] = &arr;//去掉parr 类型int(*)[10],数组指针,指向一个10int型元素的数组
	int(*pf)(int, int) = &Add;//去掉pf 类型int(*)(int,int)

3.通过函数指针调用函数的两种方法

法一:
我们平时在调用函数的时候,一般就是函数名( ,)然后把参数传进括号即可,那我们现在有函数指针了呀,指针怎么使用?p不是指向了函数Add嘛,我们用*解引用指针,得到的是地址里的东西,也就是说 *p==Add,用 * p(,)来传参也可以实现Add函数的调用。代码如下:

#include<stdio.h>
int Add(int x, int y)
{
	return x + y;
}
int main()
{
	int ret = Add(2, 3);
	printf("%d\n", ret);//ret=5
	int(*p)(int, int) = &Add;//p是一个指向函数Add的指针
	ret = (*p)(3, 3);//ret=6
	//p指向Add,对p解引用就是Add
	//简言之:*p=Add
	//我们并不总是可以拿到变量,有时是拿到变量的地址
	//对应函数指针同样的道理,有时不直接给你函数,给你函数地址,就这样调用
	printf("%d\n", ret);
}

法二:
我们在二.1取函数地址那一块介绍了,在函数指针这一块,函数名==&函数名, 也就是说创建函数指针的时候可以这样写:int(*p)(int, int) = Add,Add是赋给了p啊,你也可以认为:p就是Add。你可以这样理解,法一是int(*p)(int, int) = &Add,是把Add的地址给p,所以用p来调用函数要解引用一下,但是法二p就是Add,那不用解引用了,直接调用。代码如下:

#include<stdio.h>
int Add(int x, int y)
{
	return x + y;
}
int main()
{
	//我们由前面的知识知道:函数add取地址时,add=&add
	int(*p)(int, int) = Add;//把Add赋给p,这里p即可看做Add
	//与法一不同的是,法一将&Add赋给p,p是Add的地址,所以要解引用,这里p就可以看做是Add本身,可以不解引用
	int ret = p(3, 6);
	printf("%d", ret);
}//如果是为了方便理解,一般是用第一种方法,如果是为了操作方便,可以用第二种方法

三、函数指针进阶

大家来看这样一个代码( * (void(*)() ) 0)(),乍一看非常复杂,我们来细化一下
1 . ( * (void( * )() ) 0)() 我们抽出加粗部分
这是我们熟悉的老朋友:void( * )(),这不就是一个函数指针嘛,该函数无参,返回类型void

2 . (void( * )() ) 0是什么?我们联想一下(int)3.14,不就是对3.14强制类型转换嘛,将3.14这个浮点型强制转换成整形。这里同样的道理,是将整形0强制转换成类型为void( * )()的一个函数指针

3 .现在有了(void( * )() ) 0,我们在这个东西前面加一个 *,这个是什么意思,我们知道(void( * )() ) 0已经被转换成一个指针(指针即地址)了,地址前面加一个 *表示解引用,取出地址里的东西,也就是找到了那个函数

4 .(void( * )() ) 0表示那个函数那再在后面加一个()即是对函数的调用,也就是( * (void(*)() ) 0)()

总结

提示:本文介绍了函数指针的原理和多种使用方法,对于函数指针想要进阶提升的小伙伴一定要认真研读本文中的进阶题目,指针是一个大头,但相信坚持不懈的你一定可以战胜它,加油!

更多关于C语言函数指针的资料请关注我们其它相关文章!

(0)

相关推荐

  • 深入解析C语言中函数指针的定义与使用

    1.函数指针的定义     函数是由执行语句组成的指令序列或者代码,这些代码的有序集合根据其大小被分配到一定的内存空间中,这一片内存空间的起始地址就成为函数的地址,不同的函数有不同的函数地址,编译器通过函数名来索引函数的入口地址,为了方便操作类型属性相同的函数,c/c++引入了函数指针,函数指针就是指向代码入口地址的指针,是指向函数的指针变量. 因而"函数指针"本身首先应该是指针变量,只不过该指针变量指向函数.这正如用指针变量可指向整形变量.字符型.数组一样,这里是指向函数.C在编译时

  • C语言中函数指针的三种使用方法总结

     C语言中函数指针的三种使用方法总结 在这里分享一下自己的心得,希望和大家一起分享技术,如果有什么不足,还请大家指正.写出这篇目的,就是希望大家一起成长,我也相信技术之间没有高低,只有互补,只有分享,才能使彼此更加成长. 定义方式:int (*p)(int x, int y); 实现代码: #include <stdio.h> int sum(int x, int y){ return x + y; } int reduce(int x, int y){ return x - y; } int

  • C语言指针详解及用法示例

    新手在C语言的学习过程中遇到的最头疼的知识点应该就是指针了,指针在C语言中有非常大的用处.下面我就带着问题来写下我对于指针的一些理解. 指针是什么? 指针本身是一个变量,它存储的是数据在内存中的地址而不是数据本身的值.它的定义如下: int a=10,*p; p=&a int a=10; int *p=&a; 首先我们可以理解 int* 这个是要定义一个指针p,然后因为这个指针存储的是地址所以要对a取地址(&)将值赋给指针p,也就是说这个指针p指向a. 很多新手都会对这两种定义方法

  • 详解C语言结构体中的函数指针

    结构体是由一系列具有相同类型或不同类型的数据构成的数据集合.所以,标准C中的结构体是不允许包含成员函数的,当然C++中的结构体对此进行了扩展.那么,我们在C语言的结构体中,只能通过定义函数指针的方式,用函数指针指向相应函数,以此达到调用函数的目的. 函数指针 函数类型 (*指针变量名)(形参列表):第一个括号一定不能少. "函数类型"说明函数的返回类型,由于"()"的优先级高于"*",所以指针变量名外的括号必不可少.  注意指针函数与函数指针表示

  • C语言中的函数指针基础学习教程

    顾名思义,函数指针就是函数的指针.它是一个指针,指向一个函数.看例子: A) char * (*fun1)(char * p1,char * p2); B) char * *fun2(char * p1,char * p2); C) char * fun3(char * p1,char * p2); 看看上面三个表达式分别是什么意思? C)这很容易,fun3是函数名,p1,p2是参数,其类型为char *型,函数的返回值为char *类型. B) 也很简单,与C)表达式相比,唯一不同的就是函数的

  • C语言编程函数指针入门精讲教程

    目录 一.指针引子 二.使用步骤 1.取函数地址 2.创建函数指针 3.通过函数指针调用函数的两种方法 三.函数指针进阶 总结 一.指针引子 示例:我们常常接触的指针大多有如下几类: 整形指针-存放整形地址,指向整形 字符指针-存放字符地址,指向字符 数组指针-存放数组地址(注意不是数组首元素地址),指向数组 由以上三个例子,我们能总结指针的共同点:存放某个类型变量的地址,指向那个类型的变量,但是在讲函数指针首先有一个问题:函数也有地址吗?我们用一段简单的代码来验证一下即可. #include<

  • C语言八道笔试题精讲带你掌握指针

    目录 题目一 题目二 题目三 题目四 题目五 题目六 题目七 题目八 为了题目的准确性和我们一般学习过程中的习惯,这里所有的题目代码都是在 X86 环境(32 位平台)下运行的. 题目一 #include <stdio.h> int main() { int a[5] = { 1, 2, 3, 4, 5 }; int* ptr = (int*)(&a + 1); printf("%d,%d", *(a + 1), *(ptr - 1)); return 0; } /

  • C语言运用函数指针数组实现计算器功能

    本文实例为大家分享了C语言运用函数指针数组制作计算器的具体代码,供大家参考,具体内容如下 先来回顾一下概念: 指针数组 -- 存放指针的数组 函数指针 -- 存放函数地址的指针 函数指针数组 -- 存放函数指针的数组 接下来说说这次要制作的计算器的功能: 1.add -- 加法 2.sub -- 减法 3.mul -- 乘法 4.div -- 除法 0.exit -- 退出 具体来通过代码讲解: (1)首先写一个菜单程序,在运行程序时首先打印一次菜单. void menu() { printf(

  • C语言用函数指针实现一个特别的计算器

    目录 函数指针 实现计算器程序的思想 用函数指针调用加减乘除函数 完整代码与运行截图 函数指针 函数指针就是存放函数地址的指针. 函数指针的重要内容: 1.函数指针的使用:函数的类型(指针)(函数的形参类型)= 函数名:注意:函数的形参那里是不需要形参的变量名的.,如果函数没有形参,就不需要填任何东西,void也不行.这么使用完之后,这个指针就是一个函数指针了. 2.函数指针调用函数的方法:****(指针)(实参);注意:这里的指针有两种表达方式,比如定义了一个叫*p 的函数指针,函数的返回类型

  • Mysql数据库的主从复制与读写分离精讲教程

    目录 前言 一.MySQL主从复制 1.支持的复制类型 2.主从复制的工作过程是基于日志 3.请求方式 4.主从复制的原理 5.MySQL集群和主从复制分别适合在什么场景下使用 6.为什么使用主从复制.读写分离 7.用途及条件 8.mysql主从复制存在的问题 9.MySQL主从复制延迟 二.主从复制的形式 三.读写分离 1.原理 2.为什么要读写分离呢? 3.什么时候要读写分离? 5.目前较为常见的MySQL读写分离 四.案例实施 1.案例环境 2.实验思路(解决需求) 3.准备 4.搭建My

  • C语言中函数指针与软件设计经验总结

    函数指针与软件设计 记得刚开始工作时,一位高手告诉我,说,longjmp和setjmp玩得不熟,就不要自称为C语言高手.当时我半信半疑,为了让自己向高手方向迈进,还是花了一点时间去学习longjmp和setjmp的用法.后来明白那不单是跳来跳去那样简单,而是一种高级的异常处理机制,在某些情况下确实很有用. 为了显示自己的技巧,也在自己的程序中用过几次.渐渐发现这样的技巧带来的好处是有代价的,破坏了程序的结构化设计,程序变得很难读,尤其对新手来说.终于明白这种技巧不过是一种调味料,在少数情况使用几

  • C语言文件操作的入门详解教程

    一.一些需要掌握的知识点 文件有千千万万,但是在我们的程序设计当中,我们谈的文件一般有两种: 1.程序文件 包括源程序文件(后缀为.c),目标文件(windows环境后缀为.obj),可执行程序(windows环境后缀为.exe). 2.数据文件 文件的内容不一定是程序,而是程序运行时读写的数据,比如程序运行需要从中读取数据的文件,或者输出内容的文件. 而在本节中,我们主要提到的是数据文件. 1.文件名 我们知道,名字都是用来标识和区别事物的,那么文件名也是这样,是区别各个文件的标识. 一个文件

  • 讲解C语言编程中指针赋值的入门实例

    从const int i 说起 你知道我们声明一个变量时象这样int i :这个i是可能在它处重新变赋值的.如下: int i = 0; /* . . . */ i = 20; /*这里重新赋值了*/ 不过有一天我的程序可能需要这样一个变量(暂且称它变量),在声明时就赋一个初始值.之后我的程序在其它任何处都不会再去重新对它赋值.那我又应该怎么办呢?用const . /* . . . */ const int ic =20; /* . . . */ ic = 40; /*这样是不可以的,编译时是无

随机推荐