(*(int(*)())a)()的含义是什么?

时间:2016-12-21 12:05:35

标签: c++ pointers

我是学习C ++的初学者。今天,我看到了像这样的指针函数

(*(int (*)())a)()

我对此的含义以及我如何轻易理解它非常困惑。

3 个答案:

答案 0 :(得分:9)

让我们添加一个typedef,以帮助你做出正面或反面:

typedef int (*int_func_ptr)();

(*(int_func_ptr)a)();

因此a被强制转换为特定原型的函数指针,取消引用(这是多余的),然后被调用。

答案 1 :(得分:6)

int (*)()是一个函数指针类型,它返回int并且不接受任何参数。

我认为a是一个函数指针,其类型“擦除”实际类型(可能是为了能够存储一堆vector中的不同函数指针)我们需要转换为这个指针类型,因此(int(*)())a)将执行该转换。

之后我们要调用该函数,因此提供的代码取消引用指针*,然后用括号()

调用它

实施例

我有一个函数foo,如下所示:

int foo()
{
    std::cout << "foo\n";
    return 1;
}

然后通过reinterpret_cast我得到一个指向函数的指针,该函数返回void(出于类型擦除的原因):

 void(*fptr)() = reinterpret_cast<void(*)()>(&::foo); //§5.2.10/6

稍后,我想调用该函数,所以我需要将其重新转换回原始类型,然后调用它:

 (*(int (*)())fptr)(); // prints `foo`

Demo

取消引用它实际上是不必要的,以下内容是等效的:

((int (*)())fptr)();

为什么它们等价的解释归结为“标准说函数类型和函数指针类型都可以调用”

如果你是标准的头脑,你可以查看说明的§5.2.2[expr.call]

  

函数调用是一个后缀表达式,后跟括号,其中包含可能为空的逗号分隔的 initializer-clauses 列表,它们构成了函数的参数。后缀表达式应具有函数类型或指向函数类型的指针

答案 2 :(得分:2)

StoryTeller和Andy给出了正确的答案。另外,我会给出一般规则。

StoryTeller使用typedef int (*int_func_ptr)();创建一个正确且有用的typedef,它定义了一个函数指针类型。这里要记住两件事。

  • typedef的一般语言设计:它完全模仿了给定类型对象的声明!简单地使用typedef作为声明的前缀会使声明的标识符成为类型别名而不是变量。也就是说,如果int i;声明整数变量, typedef int i;会将i声明为int类型的同义词。因此,声明函数指针 variable 只会读取int (*int_func_ptr)();。正如StoryTeller所做的那样,使用typedef作为前缀,使其成为类型别名。

  • 功能指针的强制转换非常令人困惑。一个原因是必要的括号

    1. 括号有几个不相关的用途:

      • 它们将表达式分组以指示子表达式优先级,如(a+b) * c
      • 它们在声明和调用中分隔函数参数。
      • 它们界定了强制转换中使用的类型名称。
    2. 我们在这里为所有三个目的括起来了!

    3. The operator precedence对于函数指针声明来说是“不自然的”。当然,这是因为它们对于更频繁的使用是很自然的:没有括号,声明将是更熟悉的int *int_func();,它声明函数正确返回一个int指针。原因是参数括号具有比解除引用星号更高的优先级,因此为了推断类型,我们首先在心理上执行调用,而不是解除引用。可以调用的是一个函数。 1 调用的结果可以被取消引用,结果是一个int。
      将其与原始int (*int_func_ptr)();进行比较:附加括号强制我们首先取消引用,因此标识符必须是某种指针。可以调用解除引用的结果,因此它必须是一个函数;通话结果是int

函数指针声明或typedef看起来不自然的另一个原因是声明的标识符往往位于表达式的中心。原因是左边的操作符和应用到标识符的右侧(解除引用,函数调用,最后结果类型声明一直到左边)。

下一个规则是关于构造强制转换。强制转换中使用的类型名称是通过省略变量名来简单地从对应的变量声明构造的!这在简单中很明显case:由于int i声明了一个int变量,(int)没有i是相应的强制转换。

如果我们将它应用于函数指针类型,则通过省略变量名称并将类型名称放在括号中,将int (*int_func_ptr)()转换为奇怪的(int (*)())。请注意,强制优先于星号的括号仍然存在,即使没有任何取消引用的内容!没有它们,(int *())将从int *int_func()派生,因此表示一个返回指针的函数。 2

令人惊讶的是,声明中只有一个地方可以在语法上使用变量名称,因此即使非常复杂的类型表达式也可以很好地定义:这是一个变量名称适合定义的地方演员类型。

根据这些规则,让我们重新检查原始表达式:

(*(int (*)())a) ()

在最外层,我们有两对括号。第二对是空的,因此必须是函数调用操作符。这意味着左边的操作数有函数类型:

* (int (*)()) a

操作数是括号中的优先级表达式。它有三个部分:星号,括号中的表达式和标识符a。由于括号表达式和变量a之间没有运算符,因此它必须是强制转换,实际上是我们在上面检查的转换。 *和类型转换具有相同的优先级并从右到左进行求值:将第一个a强制转换为函数指针,然后*取消引用以获取“功能正常“(在C ++中不是真正的对象)。这适合,因为上面的函数调用操作符将应用于此结果。

<小时/> 1 C允许直接调用函数指针,而不首先解除引用,是语法糖,不在声明中考虑。

2 虽然表达式在语法上是有效的,但在C或C ++中不允许使用这样的强制转换函数。