【问题标题】:Function pointers in C - address operator "unnecessary"C中的函数指针-地址运算符“不必要”
【发布时间】:2010-09-20 11:32:40
【问题描述】:

在 C 中使用 qsort 我们传入一个比较函数,例如

int cmp(const void*, const void*);

qsort 的原型需要 int (* )(const void* , const void*),所以我们调用:

qsort(..., cmp);

但调用同样有效:

qsort(..., &cmp);

如果我们在 C++ 中传入一个静态成员函数,这就是我们必须做的。 Kernighan & Ritchie (2nd Edition, 5.11 "Pointers To Functions" p119) 指出“由于 [cmp] 已知是一个函数,因此 & 运算符不是必需的,就像在数组名称之前不需要它一样。 "

有没有其他人对此感到有点不舒服(尤其是关于类型安全)?

【问题讨论】:

    标签: c++ c function-pointers


    【解决方案1】:

    嗯,答案是按值传递函数会产生函数指针,就像按值传递数组会产生指向其第一个元素的指针一样。有人说数组和函数“衰减”。只有少数情况不会发生这种衰减。 例如 sizeof(array) 产生数组的大小,而不是它的第一个元素指针。 sizeof(function) 无效(函数不是对象),你必须做 sizeof(&function)。其他场合绑定引用:

    void baz();
    
    void foo(void (&bar)()) {
        bar();
    }
    
    // doesnt work, since a reference to a function is requested. 
    // you have to pass 'bar' itself, without taking its address 
    // explicitely.
    foo(&baz);  
    

    这个,顺便说一句,这就是你能做到的原因

    template<typename T, int N>
    void ByRef(T (&foo)[N]) { 
        ...
    }
    

    因为在考虑参考参数时数组还没有衰减。

    【讨论】:

    • 我不认为“通过值传递函数会产生函数指针”,因为函数是它自己的指针。 void (*bazPtr)() = *&amp;******&amp;*&amp;*baz; 很好。
    【解决方案2】:

    请注意,“取消引用”(即调用)函数指针也是如此;你不必

    (*funcptr)(arg1, arg2);
    

    作为

    funcptr(arg1, arg2);
    

    足够了。

    【讨论】:

      【解决方案3】:

      我怀疑你只是觉得不舒服,因为你不习惯像 C 允许的那样“接近金属”编码。 C 不需要函数的地址运算符的原因是,除了调用或传递函数之外,您实际上无法对函数做任何事情。

      换句话说,对于操作函数的“值”没有合理的定义。

      而对于整数,您可以添加或减去值,这对函数没有意义。

      无论如何,C 早于 C++ 和它自己的标准化 - 最初的 K&R C 甚至没有原型,而 ANSI 必须确保其标准化不会尽可能破坏现有代码。

      【讨论】:

      • 肯定有。您可以执行 functionptr++ 并对堆栈做坏事。 ;)
      • @PaulNathan:如果functionPtr 是指向同类型函数指针数组的指针,则可以正常工作。
      【解决方案4】:

      与其说是一种不适感,不如说是对有效但冲突的语法选项的厌恶。 cmp 是指针,应该被一致对待,或者它是其他类型——恕我直言,这在语法上具有误导性。

      更进一步,我还需要调用以取消引用指针(以突出显示它是指针的事实)或不取消引用(因为函数名 is 是指针).. .但不允许两者兼而有之。

      函数指针可能非常强大,但对于新手和有经验的程序员来说,它们似乎都是一个困惑的根源。部分困难可以通过要求一个单一的、有意义的语法来缓解,澄清它们的使用,而不是模糊它。

      【讨论】:

        【解决方案5】:

        这是一个语法细节。如果您不喜欢不使用 & 来指示函数指针,请始终使用它。如果您喜欢节省额外的按键并且在编码中不那么明确,那么请利用它。

        关于类型安全,我看不出它有什么不同。编译器被赋予一个函数作为参数,它可以完全从名称中计算出函数签名是什么。您没有调用它(使用 () 语法)这一事实表明编译器需要在此位置有一个函数指针。 & 只是一种语法上的精巧,向人们表明他们正在查看某个描述的指针。

        如果您使用的是 C++,那么我建议您将 boost 仿函数视为一种更好的传递函数的方法。这些可爱的实体为 C++ 中的所有函数提供了统一且相当清晰的语法:)

        【讨论】:

          【解决方案6】:

          无论您是否感到不舒服,都不会改变 C 不是类型安全语言这一事实​​。举个例子:

          int main()
          {
             int integer = 0xFFFFFF; 
             void (*functionPointer)() = (void(*)())integer; 
          
             functionPointer(); 
          
             return 0; 
          }
          

          这在编译时完全有效,但显然不安全。

          【讨论】:

          • 是的,但是使用该演员表来告诉编译器“相信我;我知道我在做什么,即使你不这么认为”。
          猜你喜欢
          • 2021-12-27
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2017-06-28
          • 1970-01-01
          • 2014-01-12
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多