zoukankan      html  css  js  c++  java
  • 《征服 C 指针》摘录4:函数 与 指针

    一、指向函数的指针

    函数名可以在表达式中被解读成“指向函数的指针”,因此,正如代码清单 2-2 的实验那样,写成 func 就可以取得指向函数的指针。

    “指向函数的指针”本质上也是指针(地址),所以可以将它赋给指针型变量。

    比如有下面的函数原型:

    int func(double d);

    保存指向此函数的指针的变量的声明如下:

    int (*func_p)(double);

    然后写成下面这样,就可以通过 func_p 调用 func,

    int (*func_p)(double);  // 声明
    func_p = func;  // 将func 赋给func_p
    func_p(0.5);  // 此时,func_p 等同于func
    

    将“指向函数的指针”保存在变量中的技术经常被运用在如下场合:

    • GUI 中的按钮控件记忆“当自身被按下的时候需要调用的函数”
    • 根据“指向函数的指针的数组”对处理进行分配
    •  

    后者的“指向函数的指针的数组”,像下面这样使用:

    int (*func_table[])(double) = {
        func0,
        func1,
        func2,
        func3,
    };
    ┊
    func_table[i](0.5);  // 调用func_table[i]的函数,参数为0.5

    使用上面的写法,不用写很长的 switch case,只需通过 i 的值就可以对处理进行分配。

    哦?不明白为什么?

    确实,像

    int (*func_p)(double);  // 指向函数的指针

    还有,

    int (*func_table[])(double);  // 指向函数的指针的数组

    这样的声明,是不能用普通的方法来读的。

    关于这种声明的解读方式,会在后面进行说明。

    二、关于指向函数的指针引起的混乱

    正如上面说明的那样,对于 C 语言,表达式中的函数名可以被解读成“指向函数的指针

    在信号处理、事件驱动的程序中,这种特性往往以回调函数的形式被使用。

    /*如果发生SIGSEGV(Segmentation falut),回调函数segv_handler */
    signal(SIGSEGV, segv_handler);

    可是,如果基于之前说明过的 C 语言声明规则,int func()这样的声明会被解释为“返回 int的函数”,如果 函数名在表达式中,只是取出 func,则解释成“指向返回 int 函数的指针”,是不是感觉很怪异?如果一定要使用指向函数的指针,必须要写成&func。

    对于上面信号处理的函数,写成

    signal(SIGSEGV, &segv_handler);

    这样,实际上也能顺利地执行

    相反,像

    void (*func_p)();

    这样,变量 func_p 声明为指向函数的指针,进行函数调用的时候,可以写成

    func_p();

    但是像 int func()这种声明,都是用 func()这样的方式进行调用的,从对称性的角度考虑,对于 void (*func_p)(),必须要写成

    (*func_p)();* 

    * 早期的 C 语言中,好像也只能这么写……

    这样也是能毫无问题地执行的。

    是不是感觉 C 语言的关于指向函数的指针的语法比较混乱?

    混乱产生的原因就是:“表达式中 的函数可以解读成‘指向函数的指针’”这个意图不明的规则(难道就是为了和数组保持一致?)。

    为了照顾到这种混乱,ANSI C 标准对语法做了以下例外的规定:

    • 表达式中的函数自动转换成“指向函数的指针”。但是,当函数是地址运算符&或者 sizeof 运算符的操作数时,表达式中的函数名不能变换成“指向函数的指针”。
    • 函数调用运算符()的操作数不是“函数”,而是“函数的指针”。

    如果对“指向函数的指针”使用解引用*,它暂时会成为函数,但是因为在表达式中,所以它会被瞬间地变回成“指向函数的指针”。

    结论就是,即使对“指向函数的指针”使用*运算符,也是对牛弹琴,因为此时的运算符*发挥不了任何作用。

    因此,下面的语句也是能顺利执行的,

    (**********printf)("hello, world
    ");  // 无论如何,*就是什么也没做
    

    延伸阅读:

    《征服 C 指针》摘录1:什么是空指针?区分 NULL、0 和 ''

    《征服 C 指针》摘录2:C变量的 作用域 和 生命周期(存储期)

    《征服 C 指针》摘录3:数组 与 指针

    《征服 C 指针》摘录4:函数 与 指针

    《征服 C 指针》摘录5:函数形参 和 空的下标运算符[]

    《征服 C 指针》摘录6:解读 C 的声明

    《征服 C 指针》摘录7:练习——挑战那些复杂的声明

  • 相关阅读:
    C语言修炼-第2天
    static_cast, dynamic_cast, reinterpret_cast, const_cast的区别
    构造函数不能为虚函数的原因
    matlab2016b ubuntu命令行安装 + matconvnet的安装
    python debug open_files
    构造函数不能被继承的原因
    NNVM代码阅读
    ncnn阅读
    Deep TEN: Texture Encoding Network
    git命令笔记
  • 原文地址:https://www.cnblogs.com/52php/p/5683329.html
Copyright © 2011-2022 走看看