6

我无法理解以下语句中“(*)”表达式的含义:

#define PySpam_System \
 (*(PySpam_System_RETURN (*)PySpam_System_PROTO) PySpam_API[PySpam_System_NUM])

在哪里

#define PySpam_System_NUM 0
#define PySpam_System_RETURN int
#define PySpam_System_PROTO (const char *command)

(摘自http://docs.python.org/2/extending/extending.html)。那意味着什么?(请原谅我提出一个愚蠢的问题)。类似于函数指针,但语法非常混乱。

更新:感谢 Shahbaz 和 Jonathan,事情变得越来越清楚,但我仍然无法得到两个细微差别:

  1. 为什么const char *command,不只是const char *。我认为在函数指针类型声明和类型转换中应该省略参数的名称?所以一定不能省略,而只是可以?
  2. 为什么(*(int (*)(const char *command)),不只是(int (*)(const char *command)?外部(*[这里的所有其他内容]的目的是什么)?这是对函数本身的函数指针的可选取消引用,对吗?
4

2 回答 2

7
PySpam_System_RETURN (*)PySpam_System_PROTO

扩展为:

int (*)(const char *command)

这是一个函数指针类型。所以基本上如下:

(*(PySpam_System_RETURN (*)PySpam_System_PROTO) PySpam_API[PySpam_System_NUM])

翻译为:

(*(int (*)(const char *command)) PySpam_API[0])

正在调用 的值,PySpam_API[0]就好像它是一个带有const char*参数和int返回值的函数(转换为函数指针)。请注意,这仍然缺少参数,因此使用宏的人应该给它参数,例如:

PySpam_System("some string");

在您的更新中:

  1. 参数名称是可选的。他们本来可以写const char *的。我相信这个名字是为了清楚起见而写的。现在,不用看其他任何地方,我就知道该函数需要一个命令,所以我说这很有帮助。
  2. 再说一次,你是对的。初始*取消引用函数指针。在 C 中,这是完全可选的。说起来会更对称,因为您正在取消引用一个指针,但是由于不取消引用函数指针没有意义,如果您不取消引用它不会有任何区别,C 会做唯一理智的事情为你。事实上,即使你放******了一个而不是一个,它仍然可以工作。
于 2013-04-10T16:50:29.710 回答
5

用预处理器展开PySpam_System,你会看到:

(*(int (*)(const char *command)) PySpam_API[0])

该表达式(或多或少)是对“返回 int 并采用一个类型const char *的参数的函数的指针”的强制转换。之后必须("something")通过指向函数的指针将整体转换为函数调用。因此,在代码中,您可能会看到:

PySpam_System("abc");

编译器会看到:

(*(int (*)(const char *command)) PySpam_API[0])("abc");

那是对存储在PySpam_API索引位置 0 的数组中的函数的调用。显然,参数可能不是简单的字符串文字,但简单的字符串文字是一个代表const char *


问题更新

为什么const char *command,不只是const char *。我认为在函数指针类型声明和类型转换中应该省略参数的名称?所以一定不能省略,而只是可以?

参数的名称(例如command在示例中)在原型声明中是可选的。也没有规定原型声明中的名称必须与函数定义中的名称匹配。遗憾的是,如果您编写函数以符合接口但函数的特定实现不需要参数,则 C 尚未采用 C++ 约定,即名称在函数定义中是可选的。也就是说,在 C++ 中,您可以编写:

typedef int (*interface)(const char *cmd, int arg1, int arg2);

static int implementation(const char *cmd, int arg1, int)  // C++, not C
{
    return some_other_function(cmd, arg1, 23, 19);
}

该函数implementation符合interface类型,但不使用arg2参数,但编译器不会抱怨未使用的参数。C 不允许这种便利性,因此即使您非常清楚它未使用,您也会被告知“未使用的参数”。我相信有一些特定于编译器的方法可以解决这个特定问题。

为什么(*(int (*)(const char *command)),不只是(int (*)(const char *command)?外挂的目的是(*[everything else here])什么?这是对函数本身的函数指针的可选取消引用,对吗?

对。

请注意,通过函数指针调用函数有两种方法,大致是“旧”和“新”(其中新的意思是“由 C89 标准引入和祝福”,因此它不完全是“新”)。

所需的旧符号(提供或获取当时不可用的原型):

#include <math.h>

double (*func)(double) = sin;

double x = (*func)(1.0);

带括号和*函数指针。这个想法是,如果你有一个指向函数的指针,你需要取消引用它来调用函数。更现代的表示法指出,函数的名称变成了指向函数的指针(例如在赋值中),因此您实际上并不需要这种(*func)表示法。因此,较新的样式是:

#include <math.h>

double (*func)(double) = sin;

double x = func(1.0);

由于我在旧式是强制性的时候学习了 C,所以我仍然更喜欢稍微冗长但完全明确的表示法。宏中的代码PySpam_System也使用旧的表示法。

您还可以使用函数指针玩各种愚蠢的游戏,添加额外的*和使用 a &,所有这些都是不必要的。

double (*funca)(double) =   &sin;
double (*func0)(double) =    sin;
double (*func1)(double) =   *sin;
double (*func2)(double) =  **sin;
double (*func3)(double) = ***sin;

只有func0声明是明智的;其余的都是模糊的 C.

于 2013-04-10T16:52:30.350 回答