首页 > 其他分享 >C语言函数指针在形参列表和返回值中的函数声明写法

C语言函数指针在形参列表和返回值中的函数声明写法

时间:2022-10-29 16:08:31浏览次数:105  
标签:函数 形参 int C语言 add func 函数指针 声明



tags: C/C++ Pointer

写在前面

最近看网络, 发现系统调用中的信号函数的声明有点奇怪, 如下:

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

虽然书中给出了解释, 但是奈何自己的C语言基础不好, 看着比较费劲, 下面就重新研究一下C语言中的函数指针, 包括以下的几种情况.

  1. 函数指针的声明(两种, 算上​​C++​​的话就是三种, 其实采用宏定义的方式也可以, 但是宏定义只是简单的变量替换, 不建议在这里使用);
  2. 函数指针作为函数返回值时函数的声明;
  3. 函数指针作为函数参数时函数的声明.

传统的这种写法(例如上面提到的​​signal()​​函数)看起来实在是不好理解, 特别是括号的嵌套法则很容易出错, 这里还是推荐先对函数指针进行声明之后再写入函数的声明中, 也相当于是一种划分子问题的思想.

函数指针的声明

先来看教科书中给出的函数指针的声明方式:(这也是最基本的一种)

类型标识符 (*指针变量名) (形参列表)

举个例子, 对于一个只返回两数之和的函数, 其形参列表当然是​​(int, int)​​, 如下所示:

// 函数声明
int add(int a, int b);
int add(int, int);// 可以不加形参变量名, 推荐这种写法
// 函数定义
int add(int a, int b){return a+b;}

采用基本写法声明指向这类函数的函数指针:

int (*funp) (int, int);

这里我采用了​​这类​​​, 是因为对于其他函数, 只要其为一种返回值为​​int​​​, 形参列表为​​(int, int)​​​的函数, 那么就可以用​​funp​​这个指针指向该函数, 例如:

int minus(int a,int b){return a-b;}
funp = minus;

当然, 虽然初学者经常使用基本写法来声明函数指针, 但是这种方法在比较复杂的情况中(我后面要提到的两种情况)声明容易出错, 下面来看通过给出别名的方式声明函数指针, 仍以相加函数为例:(写成大写是类型定义的变量命名规则)

typedef int (*FUNP)(int, int);

这种写法虽然看起来跟上面没有很大区别, 但是在声明函数返回值为函数指针的时候比较有用, 稍后我介绍具体的例子.

当然, 对于习惯使用​​C++​​​的朋友来说, 使用​​using​​关键字指定类型别名更加方便, 上面的函数指针声明可以这样写:

using FUNP = int (*)(int, int);

这种写法与​​typedef​​​的写法是等价的, 都是给出了一个​​FUNP​​类型作为函数指针的类型, 使用起来比较方便.

返回函数指针的函数的声明

仍以上面的两数相加为例, 如果这时候想定义一个新的函数​​func​​​, 这个新的函数传入两个数, 但是返回值是上面定义的​​add()​​函数的函数指针, 那这个函数的声明应该怎么写呢?

先来看第一种.

基本形式

延续上面基本的函数指针声明形式, 可以写出如下的函数声明:

int add(int, int);
int (*func(int, int))(int, int);

可以看出来这次的声明就比较复杂了, 在指针变量名后面还有一个括号, 为方便理解, 大家可以从内层括号往外看:

  1. ​func(int, int)​​​是函数​​func​​​的带参数列表的声明形式, 其参数类型为两个​​int​​;
  2. ​func​​​的返回值类型是​​int(*)(int, int)​​​, 即一个函数指针, 该指针所指向的函数是: **参数类型是两个​​int​​​, 返回值类型是​​int​​**的函数.

下面是具体的实例代码:

#include <stdio.h>

int add(int, int);
int (*func(int, int))(int, int);

int main() {
int a = 5, b = 5;
int a1 = 3, b1 = 3;
// 函数调用, `func(a, b)`返回一个函数指针
// 后面的`(a,b)`才是真正进行相加运算的形参
int ans = func(a1, b1)(a, b);
printf("ans=%d\n", ans);
return 0;
/*
a1=3
b1=3
ans=10
*/
}

int add(int x, int y) { return x + y; }

// 返回函数指针的函数, 返回的函数指针,形参列表为两个int, 返回值为int
int (*func(int a1, int b1))(int, int) {
printf("a1=%d\n", a1);
printf("b1=%d\n", b1);
return add;
}

在​​func​​​函数的调用中, 为避免引起混淆, 传参时我写了两组值, 分别是​​a, b​​​和​​a1, b1​​​, 首先传入的​​a1, b1​​​并不是真正作加法的参数, 而是仅被​​func​​​内部的​​printf​​​打印输出了, ​​func(a1, b1)​​​返回了一个函数指针, 这之后的​​(a, b)​​​才是真正用作​​add()​​函数形参的两数.

这里还要注意, 声明与定义的写法框架是一样的, 但是在函数定义的时候需要给出形参变量名, 这就要注意形参变量名不能加在外部, 而是要加在​​func()​​​这个括号里面, 外部(即函数体左大括号左边的小括号内)的形参类型是​​func​​返回的函数指针所指向的函数的形参列表, 刚接触函数指针时, 这一点尤其容易出错.

采用typedef定义函数指针类型

通过上面的例子可以看出, 用基本的函数指针声明方法来声明返回值为函数指针的函数的时候, 往往是很复杂的, 还要照顾到形参列表中变量名的位置, 一不小心括号套错了也会引发编译错误. 下面来看一种写法很简洁的函数声明方式, 就是通过上面介绍的采用​​typedef​​为函数指针定义新的类型(别名)的方式.

还是上面的例子, 先通过​​typedef​​给出函数指针类型的声明, 然后直接就能以类型别名​​FUNP​​作为函数的返回值类型了.

typedef int (*FUNP)(int, int); // function ptr
int add(int, int);
FUNP fun(int, int);

具体的代码如下:

#include <stdio.h>

typedef int (*FUNP)(int, int); // function ptr
int add(int, int);
FUNP fun(int, int);

int main() {
int a = 5, b = 5;
int a1 = 3, b1 = 3;
int ans = fun(a1, b1)(a, b);

printf("ans=%d\n", ans);
return 0;
/* a1=3 */
/* b1=3 */
/* ans=10 */
}

int add(int x, int y) { return x + y; }

FUNP fun(int a1, int b1) {
printf("a1=%d\n", a1);
printf("b1=%d\n", b1);
return add;
}

可以看出这种写法虽然与上面基本形式等价, 但是理解起来很容易, 也不容易出错. 实际使用中还是推荐第二种写法.

函数指针作为参数的函数声明

这里就比较简单了, 直接在形参列表中做文章即可, 这里也就不赘述了. 依旧是两种写法.

基本写法

#include <stdio.h>

int add(int, int);
int func(int, int, int (*)(int, int));


int main(int argc, char *argv[]) {
int a = 3, b = 3;
int ans = func(a, b, add);
printf("ans=%d \n", ans); // ans=6
return 0;
}

int add(int a, int b) { return a + b; }
int func(int x, int y, int (*f1)(int, int)) { return f1(x, y); }

typedef写法

#include <stdio.h>

int add(int, int);
typedef int (*FUNP)(int, int);
int func(int, int, FUNP);


int main(int argc, char *argv[]) {
int a = 3, b = 3;
int ans = func(a, b, add);
printf("ans=%d \n", ans); // ans=6
return 0;
}

int add(int a, int b) { return a + b; }
int func(int x, int y, FUNP f1) { return f1(x, y); }

结束语

有了上面的各类型的函数声明的分析, 相信大家已经能看出来​​signal()​​函数的声明意味着什么吧:

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

还是用我给出的从内向外看的方法,

  1. 看函数​​signal()​​​的形参列表​​(int sig, void(*func)(int))​​;
  1. 第一参数为​​int​​类型;
  2. 第二参数为返回值类型为​​void​​​, 形参类型为​​int​​的函数指针;
  1. 看​​signal()​​​的返回值, 是一个返回值类型为​​void​​​, 形参类型为​​int​​的函数指针.

看到这里, 希望大家对含有函数指针的函数声明有了一个新的认识. 如果有任何问题也欢迎大家一起交流学习~


标签:函数,形参,int,C语言,add,func,函数指针,声明
From: https://blog.51cto.com/u_15366127/5806203

相关文章

  • c语言和python区别
    除了在语法使用上面的区别外,c语言和python的本质区别可能是在其语言性质上面的区别。简单来说C语言是编译语言,Python是脚本语言。1.C语言是编译型语言,经过编译后,生成机器码,......
  • C语言笔记 | Struct结构体
    结构体的概念由一系列具有相同类型或不同类型的数据构成的数据集合,叫做结构struct是一种复合数据类型,结构类型在C语言中,结构体(struct)是复合数据类型的一......
  • 基于C语言的通用型数据结构与容器库
    仓库地址:github:https://github.com/hellototoro/hlibcgitee:https://gitee.com/totorohello/hlibclist双向序列容器,用于将它们的元素保持为线性排列,并允许在序列的任何......
  • C语言“我的家谱”程序
    C语言“我的家谱”程序建立“我的家谱”问题家谱:又称族谱,是记载一个家族的世系繁衍及重要人物事迹的书。家谱中记录着父亲、母亲和孩子的姓名。请大家利用二叉树设计一个......
  • 学习笔记之——C语言 函数
    采用函数的原因:随着程序规模的变大,产生了以下问题:——main函数变得相当冗杂——程序复杂度不断提高——代码前后关联度提高,修改代码往往牵一发而动全身——变量使用过......
  • c语言中内存管理的基础知识(上)
    作用域​C语言变量的作用域:(C语言编译器可以确认四种不同类型的作用域)代码块作用域作用范围就是{}之间的代码原型作用域函数体去掉代码块的部分函数作用......
  • C语言——动态内存管理
    一.为什么存在动态内存分配//局部变量inta=2;//在栈区开辟4个字节chararr[10]={0};//在栈区开辟10个字节的连续的空间上述的空间开辟方式有两个特点:开辟空间大小是固......
  • 初始C语言
     基本了解C语言的基础知识,对C语言有一个大概的认识。每个知识点就是简单认识,不做详细讲解重点:什么是C语言第一个C语言程序数据类型变量、常量字符串+转义字符+注释选择语句......
  • 初识c语言
    前言小伙伴们大家好,今天开始,我们即将开启c语言的正是学习,从初级到进阶,想必大家都听说过c生万物,可想而知c语言对于我们程序员的重要之处,从此篇博客开始将持续更新c语言的相关......
  • c语言—自定义类型(结构体,枚举,联合)进阶篇—笔记
    个人觉得结构体相当于类,应该是比较实用的功能。1.结构体结构是一些值的集合,这些值称为成员变量。结构的每个成员可以是不同类型的变量。structtag{member-list;}variable-l......