首页 > 其他分享 >C语言指针(函数指针的深入)

C语言指针(函数指针的深入)

时间:2024-06-10 20:58:41浏览次数:18  
标签:函数 int void ret C语言 printf 函数指针 指针

在函数指针进阶中初步接触了函数指针

现在来深度学习一下

我们来分析两个题目加深一下对函数指针的理解

例1:

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

分析这个函数的作用是什么?

看到这段语句这么长是不是觉得很难,在我分析了之后就会变得非常简单

分析之前我们先讲解一下C声明的组成

任何C变量的声明都由两部分组成:类型以及一组类似表达式的声明符

最简单的声明符就是单个变量,如:

double f, g;

这个声明的含义是:当对其求值时,表达式f和g的类型为浮点数类型(double)。

因为声明符与表达式相似,所以我们也可以再声明符中任意使用括号:

double ((f));

这个声明的含义是:当对表达式求值时,((f))的类型为浮点型,由此可以推知,f也是一个浮点类型。

同样的逻辑,也适用与函数和指针类型的声明

如:

double  ff();

这个声明的含义是:表达式ff()求值结果,也就是说ff是一个返回值为浮点类型的函数。

double *pf;

这个声明的含义是:*pf是一个浮点数,也就是说,pf是一个指向浮点数的指针。

以上这些声明还可以组合起来

double *g(); //g是一个函数

double (*h)(); //该声明表示h是一个指向的返回值为浮点类型的函数的指针

所以 ( double ( * ) ( ) ) 表示一个“指向返回值为浮点类型的函数的指针”的类型。

回到开始继续分析那个复杂的语句,现在就很简单了

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

以 ( *pf ) ( ) 为来看是不是与上述语句有点像

那我们把pf换成0

(*0)();

这样应该是可行的,声明符类似表达式,对吧。但是上式并不能生效,因为运算符*的操作数必须是一个指针,不仅如此这个指针还得是一个函数指针,这样经过运算符*作用后的结果才能作为函数被调用。即:对0作强制类型转换

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

我们来将这个代码简化一下:

typedef void (*funcptr)();

借助typedef语句 用funcptr将void (*)()文本替换掉

现在我们可以将代码写成:

(*(funcptr)0)();

现在是不是更容易理解了

所以上述代码的作用是调用0地址处的函数,一次函数调用

1.将0转换为返回值为void的一个函数的地址

2.调用0地址处的函数

例2:

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

先分析一下,signal先与后面的括号结合,即signal是一个函数名,所以以上代码是一个函数声明

根据上述代码我们可以得到

signal是函数第一个参数是整型,第二个参数是函数指针返回值为void,函数参数为整型,signal返回类型为函数指针返回值为void,函数参数为整型

我们来将代码简化一下:

typedef void (*pf_t)(int)

现在我们可以将代码写成:

pf_t signal(int, pf_t)

其实刚学习的C语言我也觉得函数指针没啥用,但是学到之后就觉得函数指针作用很大

这里给出一段代码,模拟计算机加减乘除

#include <stdio.h>
void test()
{
    printf("******************\n");
    printf("*  0:退出 1:add  *\n");
    printf("*  2:sub 3:mul  *\n");
    printf("*  4:div        *\n");
    printf("******************\n");
    printf("请选择:");
}

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

int sub(int x, int y)
{
    return x - y;
}

int mul(int x, int y)
{
    return x * y;
}

int div(int x, int y)
{
    return x / y;
}

int main()
{
    int x, y;
    int input = 0;
    int ret = 0;
    do
    {
        test();
        scanf("%d", &input);
        switch (input)
        {
        case 1:
            printf("请输入操作数:");
            scanf("%d%d", &x, &y);
            ret = add(x, y);
            printf("ret = %d\n", ret);
            break;
        case 2:
            printf("请输入操作数:");
            scanf("%d%d", &x, &y);
            ret = sub(x, y);
            printf("ret = %d\n", ret);
            break;
        case 3:
            printf("请输入操作数:");
            scanf("%d%d", &x, &y);
            ret = mul(x, y);
            printf("ret = %d\n", ret);
            break;
        case 4:
            printf("请输入操作数:");
            scanf("%d%d", &x, &y);
            ret = div(x, y);
            printf("ret = %d\n", ret);
            break;
        case 0:
            printf("退出程序");
            break;
        default:
            printf("选择错误,请重新输入");
        }
    } while (input);
    return 0;
}

这段代码中有很多重复的代码,这使得代码很冗余,可读性减低,无意义的增加了工作量

那我们是不是可以将那些重复的代码封装起来,写成一个函数

->改良之后

#include <stdio.h>
void test()
{
    printf("******************\n");
    printf("*  0:退出 1:add  *\n");
    printf("*  2:sub 3:mul  *\n");
    printf("*  4:div        *\n");
    printf("******************\n");
    printf("请选择:");
}

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

int sub(int x, int y)
{
    return x - y;
}

int mul(int x, int y)
{
    return x * y;
}

int div(int x, int y)
{
    return x / y;
}

void callback(int (*pf)(int, int))
{
    int x, y;
    int ret = 0;
    printf("请输入操作数:");
    scanf("%d%d", &x, &y);
    ret = add(x, y);
    printf("ret = %d\n", ret);
}
int main()
{
    int input = 0;
    scanf("%d", &input);
    do
    {
        test();
        switch (input)
        {
        case 1:
            callback(add);
            break;
        case 2:
            calc(sub);
            break;
        case 3:
            callback(mul);
            break;
        case 4:
            callback(div);
            break;
        case 0:
            printf("退出程序");
            break;
        default:
            printf("选择错误,请重新输入");
        }
    } while (input);
    return 0;
}

这个代码就巧妙的使用函数指针将重复的内容封装成calc,大大减少了代码量,使得代码简洁很多,可读性变高

calc也有另外一个名称:回调函数

回调函数(callback)的定义

回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该函数的实现方直接实现调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。

 上面这个说法很复杂,这里的意思就是

回调函数:回头再掉函数,你把你的函数指针通过传参的形式传递给某个函数,该函数再调用它自身之后再调用你传递的函数

标签:函数,int,void,ret,C语言,printf,函数指针,指针
From: https://blog.csdn.net/m0_73634434/article/details/139497076

相关文章

  • 冒泡排序(C语言)
    一.冒泡排序的原理冒泡排序的原理是:从左到右,相邻元素进行比较,以升序为例,第1次遍历将最大的数沉底。经过n次遍历这组元素以升序排列->第1次遍历,两个相邻的数比较大小,若左边的数大于右边的数,则两个元素交换位置,反之,继续向后比较,当第1次遍历结束之后该组元素最大的数就被移动......
  • 函数递归(C语言)(详细过程!)
    函数递归一.递归是什么1.1递归的思想1.2递归的限制条件二.递归举例2.1求n的阶乘2.2按顺序打印一个整数的每一位三.递归与迭代3.1求第n个斐波那契数一.递归是什么递归是学习C语言很重要的一个知识,递归就是函数自己调用自己,是一种解决问题的方法,下面就使用......
  • 【C语言】预处理详解(中卷)
    前言预处理完整系列推荐阅读顺序:预处理详解(上卷)——宏(上卷)——宏(下卷)——预处理详解(中卷)——预处理详解(下卷)本文接着讲预处理相关的内容。#和###运算符#可以将宏的一个参数转换成字符串字面量。它仅允许出现在带参数的宏的替换列表中。#运算符所执行的操作可以理解为“......
  • 【C语言】分支与循环(下)
     目录 6. while循环7. for循环8. do-while循环9. break和continue循环10. 循环的嵌套11. goto语句正文开始——6. while循环  C语言提供了三种循环,while就是其中一种。while语句的语法结构与if语句非常相似。6.1 if和while的对......
  • c语言——字符函数与字符串函数
    文章目录一字符函数(1)字符分类函数:(2)字符转换函数:二字符串函数(1)strlen函数的使用与模拟(2)strcpy函数的使用与模拟(3)strcmp函数的使用与模拟(4)strcat函数的使用与模拟两组函数的区别:(5)strncpy函数(6)strncmp函数的使用(7)strncat函数的使用(8)strstr函数的使用与模拟(9)strtok函数......
  • 【C语言】写一个宏,可以将一个整数的二进制位的奇数位和偶数位交换
    思路:10的二进制是00000000000000000000000000001010,然后交换奇数位和偶数位那么就是第一位和第二位交换,第三位和第四位交换,以此类推解题步骤:(1)将00000000000000000000000000001010的奇数位保留,偶数位变为0则变成00000000000000000000000000000000(2)将00000000000000000000......
  • C++缺省参数、缺省参数的概念、缺省参数的分类、函数重载、函数重载的概念、C++支持函
    文章目录前言一、缺省参数1.缺省参数的概念2.缺省参数的分类二、函数重载1.函数重载的概念2.C++支持函数重载的原理三、引用1.引用的概念2.引用的特性3.常引用4.引用的使用场景5.传值和传引用效率比较6.引用和指针的区别总结前言C++缺省参数、缺省参数......
  • C语言 & 图形化界面方式连接MySQL【C/C++】【图形化界面组件分享】
      博客主页:花果山~程序猿-CSDN博客文章分栏:MySQL之旅_花果山~程序猿的博客-CSDN博客关注我一起学习,一起进步,一起探索编程的无限可能吧!让我们一起努力,一起成长!目录一.配置开发环境 二,接口介绍1.mysql_init2.mysql_real_connect3.mysql_query4.对select结果分析......
  • 线程池原理及c语言实现线程池
    线程池线程池是一种多线程处理机制,其主要目的是提高系统资源利用率、降低系统资源消耗,并通过控制并发线程数量来优化性能。以下是关于线程池的详细解释:定义:线程池是一种线程使用模式,它维护着一组线程,这些线程等待监督管理者分配可并发执行的任务。通过将任务添加到队列中,并......
  • 【C语言】宏offsetof的模拟实现(计算结构体中某变量相对于首地址的偏移)
    首先我们应该特别留意:offsetof是一个宏,并非是一个函数!宏offsetof的介绍:参数:第一个是结构体类型名称,第二个是结构体成员名返回类型:size_t无符号整形引用的头文件:<stddef.h>offsetof的使用举列:#include<stddef.h>structStu//注释为相对于起始位置的偏移量{......