【问题标题】:Why is using the function name as a function pointer equivalent to applying the address-of operator to the function name?为什么将函数名用作函数指针等同于将地址运算符应用于函数名?
【发布时间】:2012-08-28 02:50:12
【问题描述】:

有趣的是使用函数名作为函数指针相当于将地址运算符应用于函数名

这是示例。

typedef bool (*FunType)(int);
bool f(int);
int main() {
  FunType a = f;
  FunType b = &a; // Sure, here's an error.
  FunType c = &f; // This is not an error, though. 
                  // It's equivalent to the statement without "&".
                  // So we have c equals a.
  return 0;
}

使用名称是我们在数组中已经知道的。但是你不能写像

int a[2];
int * b = &a; // Error!

这似乎与语言的其他部分不一致。这种设计的基本原理是什么?

This question explains the semantics of such behavior and why it works. 但我很感兴趣为什么这种语言是这样设计的。

更有趣的是,函数类型作为参数使用时可以隐式转换为指向自身的指针,但作为返回类型使用时不会转换为指向自身的指针!

示例:

typedef bool FunctionType(int);
void g(FunctionType); // Implicitly converted to void g(FunctionType *).
FunctionType h(); // Error!
FunctionType * j(); // Return a function pointer to a function 
                    // that has the type of bool(int).

【问题讨论】:

    标签: c++ syntax function-pointers


    【解决方案1】:

    由于您特别询问了这种行为的基本原理,这是我能找到的最接近的东西(来自 ANSI C90 基本原理文档 - http://www.lysator.liu.se/c/rat/c3.html#3-3-2-2):

    3.3.2.2 函数调用

    指向函数的指针可以用作(*pf)()pf()。 后一种结构,未在基础文档中批准,出现在 某些当前版本的 C 是明确的,不会使旧代码无效, 并且可以是一个重要的速记。速记对于 仅提供一个外部名称的包,该名称指定一个 充满指向对象和函数的指针的结构:成员 函数可以被称为graphics.open(file) 而不是 (*graphics.open)(file)。函数指示符的处理可以 导致一些奇怪但有效的句法形式。鉴于 声明:

    int f ( ) , ( *pf ) ( ) ; 
    

    那么以下所有表达式都是有效的函数调用:

    ( &f)(); f(); (*f)(); (**f)(); (***f)();
    pf(); (*pf)(); (**pf)(); (***pf)();
    

    每行的第一个表达式在前面讨论过 段落 。二是常规用法。随后的所有 表达式利用函数的隐式转换 指针值的指示符,几乎在所有表达式上下文中。 委员会认为允许这些表格并没有真正的危害;取缔 像(*f)() 这样的形式,同时仍然允许*a(对于int a[]), 只是看起来麻烦多于值得。

    基本上,添加了函数指示符和函数指针之间的等价性以使使用函数指针更加方便。

    【讨论】:

    • 我想这是因为函数指示符实际上是用于人类目的的语法糖。函数名实际上只是内存地址上的一个标签。环境实际上并没有看到单独的函数和指向它们的指针——它只是看到一条指令分支到一个地址。这与环境将使用不同寻址模式(直接或间接)的对象不同
    【解决方案2】:

    这是一个继承自 C 的特性。

    在 C 语言中,它被允许主要是因为函数名本身并没有太多其他含义。你可以用一个实际的函数做的就是调用它。如果您不打电话给它,您可以做的唯一 事情就是获取地址。由于没有歧义,只要函数名称后面没有 ( 来表示对该函数的调用,该名称就会计算为函数的地址。

    这实际上有点类似于语言的其他部分——数组的名称计算为数组的第一个元素的地址,除非在一些相当有限的情况下(用作& 的操作数或sizeof)。

    既然 C 允许这样做,C++ 也可以这样做,主要是因为同样如此:你可以对函数做的唯一事情就是调用它或获取它的地址,所以如果名称后面没有 (表示函数调用,然后名称计算为地址,没有歧义。

    【讨论】:

    • The only things you can do with a function are call it or take its address -- 你也可以用它来初始化对函数的引用。
    • @Cubbi: C 没有引用,这个答案的重点是行为是从 C 继承的。
    • 从技术上讲,在 C 中,函数名的计算结果为函数的地址即使它用于函数调用表达式; “表示被调用函数的表达式”被要求具有指向函数的类型,并且为了适应这一点,裸函数名的计算结果为指向函数的指针“除非它是 sizeof 的操作数或一元 @ 987654327@"(分别为 C99 §§ 6.5.2.2p1 和 6.3.2.1p4)。
    【解决方案3】:

    对于数组,使用address-of操作符时没有指针衰减:

    int a[2];
    int * p1 = a;      // No address-of operator, so type is int*
    int (*p2)[2] = &a; // Address-of operator used, so type is int (*)[2]
    

    这是有道理的,因为数组和指针是不同的类型,例如可以返回对数组的引用或在函数中传递对数组的引用。

    但是,对于函数,还有什么其他类型是可能的?

    void foo(){}
    &foo; // #1
    foo;  // #2
    

    假设只有#2 给出了void(*)() 的类型,&foo 的类型是什么?没有其他可能。

    【讨论】:

    • 你能解释一下类型(*)[2]。我不知道我是否能正确想象它,但它像数组(指针)p2 的第三个值吗?它的价值也是地址?作为(*)[2] = &。纠正我对指针(*)[2] 的值的概念,但它基本上是,不是吗?
    • @Herdsman:int (*)[2] 是一个指向 2 个 ints 的数组的指针。 This site给出了详细的解释。
    • 第二还是第三?你没有从零索引开始计数
    • 您阅读我的链接了吗? int (*)[2] 索引到数组中。它表示数组的 size 为 2。
    猜你喜欢
    • 2012-05-01
    • 1970-01-01
    • 2016-10-20
    • 1970-01-01
    • 2017-04-04
    • 1970-01-01
    • 1970-01-01
    • 2012-03-22
    相关资源
    最近更新 更多