了解 C 中函数指针的 typedef

2024-04-17

当我阅读其他人的代码时,我总是有点困惑,这些代码具有指向带有参数的函数的指针的 typedef 。我记得不久前,当我试图理解用 C 编写的数值算法时,我花了一段时间才得到这样的定义。那么,您能否分享一下关于如何为函数指针编写良好的 typedef(该做和不该做)的技巧和想法,以及为什么它们有用以及如何理解其他人的工作?谢谢!


考虑signal()C 标准中的函数:

extern void (*signal(int, void(*)(int)))(int);

非常晦涩明显 - 它是一个接受两个参数的函数,一个整数和一个指向函数的指针,该函数接受整数作为参数并且不返回任何内容,并且它(signal()) 返回一个指向函数的指针,该函数采用整数作为参数并且不返回任何内容。

如果你写:

typedef void (*SignalHandler)(int signum);

那么你可以声明signal() as:

extern  SignalHandler signal(int signum, SignalHandler handler);

这意味着同样的事情,但通常被认为更容易阅读。更清楚的是,该函数需要一个int and a SignalHandler并返回一个SignalHandler.

不过,这需要一些时间来适应。但你不能做的一件事是使用SignalHandler typedef在函数定义中。

我仍然是老派,更喜欢调用函数指针:

(*functionpointer)(arg1, arg2, ...);

现代语法仅使用:

functionpointer(arg1, arg2, ...);

我可以明白为什么它有效 - 我只是更喜欢知道我需要查找变量的初始化位置而不是调用的函数functionpointer.


萨姆评论道:

我以前看过这个解释。然后,就像现在的情况一样,我认为我没有得到的是这两个陈述之间的联系:

    extern void (*signal(int, void()(int)))(int);  /*and*/

    typedef void (*SignalHandler)(int signum);
    extern SignalHandler signal(int signum, SignalHandler handler);

或者,我想问的是,您可以用来提出第二个版本的基本概念是什么?连接“SignalHandler”和第一个 typedef 的基本原理是什么?我认为这里需要解释的是 typedef 在这里实际上做了什么。

让我们再试一次。其中第一个是直接从 C 标准中提取的 - 我重新输入了它,并检查了括号是否正确(直到我更正它 - 这是一个很难记住的饼干)。

首先,请记住typedef引入类型的别名。所以,别名是SignalHandler,其类型为:

指向以整数作为参数且不返回任何内容的函数的指针。

“不返回任何内容”部分已拼写void;作为整数的参数是(我相信)不言自明的。以下符号简单地(或不是)C 拼写指向函数的指针,该函数接受指定的参数并返回给定的类型:

type (*function)(argtypes);

创建信号处理程序类型后,我可以使用它来声明变量等。例如:

static void alarm_catcher(int signum)
{
    fprintf(stderr, "%s() called (%d)\n", __func__, signum);
}

static void signal_catcher(int signum)
{
    fprintf(stderr, "%s() called (%d) - exiting\n", __func__, signum);
    exit(1);
}

static struct Handlers
{
    int              signum;
    SignalHandler    handler;
} handler[] =
{
    { SIGALRM,   alarm_catcher  },
    { SIGINT,    signal_catcher },
    { SIGQUIT,   signal_catcher },
};

int main(void)
{
    size_t num_handlers = sizeof(handler) / sizeof(handler[0]);
    size_t i;

    for (i = 0; i < num_handlers; i++)
    {
        SignalHandler old_handler = signal(handler[i].signum, SIG_IGN);
        if (old_handler != SIG_IGN)
            old_handler = signal(handler[i].signum, handler[i].handler);
        assert(old_handler == SIG_IGN);
    }

    ...continue with ordinary processing...

    return(EXIT_SUCCESS);
}

Please note How to avoid using printf() in a signal handler? https://stackoverflow.com/questions/16891019

那么,除了省略使代码干净编译所需的 4 个标准标头之外,我们在这里做了什么?

前两个函数是采用单个整数且不返回任何内容的函数。其中一个实际上根本没有回来,这要归功于exit(1);但另一个在打印消息后确实返回。请注意,C 标准不允许您在信号处理程序中执行太多操作;POSIX http://www.opengroup.org/onlinepubs/9699919799/functions/V2_chap02.html#tag_15_04在允许的范围内更慷慨一些,但官方不批准打电话fprintf()。我还打印出收到的信号号。在里面alarm_handler()函数,值永远是SIGALRM因为这是它作为处理程序的唯一信号,但是signal_handler()也许能得到SIGINT or SIGQUIT作为信号编号,因为两者使用相同的函数。

然后,我创建一个结构数组,其中每个元素标识一个信号编号以及要为该信号安装的处理程序。我选择担心 3 个信号;我常常会担心SIGHUP, SIGPIPE and SIGTERM以及它们是否被定义(#ifdef条件编译),但这只会使事情变得复杂。我也可能会使用 POSIXsigaction()代替signal(),但那是另一个问题;让我们坚持我们开始的内容。

The main()函数迭代要安装的处理程序列表。对于每个处理程序,它首先调用signal()找出进程当前是否忽略该信号,并在执行此操作时安装SIG_IGN作为处理程序,这确保信号保持被忽略。如果该信号之前没有被忽略,那么它会调用signal()再次,这次安装首选信号处理程序。 (另一个值大概是 SIG_DFL,信号的默认信号处理程序。)因为第一次调用“signal()”将处理程序设置为SIG_IGN and signal()返回前一个错误处理程序的值old之后if声明必须是SIG_IGN- 因此有这样的断言。 (嗯,这可能是SIG_ERR如果出现严重错误 - 但随后我会从断言触发中了解到这一点。)

然后程序执行其操作并正常退出。

请注意,函数的名称可以被视为指向适当类型的函数的指针。当您不应用函数调用括号时(例如在初始化程序中),函数名称将成为函数指针。这也是为什么通过pointertofunction(arg1, arg2)符号;当你看到alarm_handler(1),你可以考虑alarm_handler是一个指向函数的指针,因此alarm_handler(1)是通过函数指针调用函数。

所以,到目前为止,我已经证明了SignalHandler变量的使用相对简单,只要您有一些正确类型的值可以分配给它 - 这就是两个信号处理函数提供的。

现在我们回到问题——这两个声明是如何做的signal()彼此相关。

让我们回顾一下第二个声明:

 extern SignalHandler signal(int signum, SignalHandler handler);

如果我们像这样更改函数名称和类型:

 extern double function(int num1, double num2);

你可以毫无问题地将其解释为一个需要int and a double作为参数并返回double价值(你会吗?如果这是有问题的,也许你最好不要“坦白”——但如果这是一个问题,也许你应该谨慎地提出像这个这样的问题)。

现在,不再是一个double, the signal()函数需要一个SignalHandler作为其第二个参数,并返回 1 作为结果。

其机制也可以被视为:

extern void (*signal(int signum, void(*handler)(int signum)))(int signum);

很难解释——所以我可能会搞砸。这次我给出了参数名称 - 尽管名称并不重要。

一般来说,在 C 中,声明机制是这样的:

type var;

然后当你写的时候var它代表给定的值type。例如:

int     i;            // i is an int
int    *ip;           // *ip is an int, so ip is a pointer to an integer
int     abs(int val); // abs(-1) is an int, so abs is a (pointer to a)
                      // function returning an int and taking an int argument

在标准中,typedef在语法中被视为存储类,就像static and extern是存储类。

typedef void (*SignalHandler)(int signum);

意味着当你看到一个类型的变量时SignalHandler(比如alarm_handler)调用为:

(*alarm_handler)(-1);

结果有type void- 没有结果。和(*alarm_handler)(-1);是一个调用alarm_handler()有论据-1.

所以,如果我们声明:

extern SignalHandler alt_signal(void);

代表着:

(*alt_signal)();

代表一个空值。因此:

extern void (*alt_signal(void))(int signum);

是等价的。现在,signal()更复杂,因为它不仅返回一个SignalHandler,它也接受 int 和 aSignalHandler作为参数:

extern void (*signal(int signum, SignalHandler handler))(int signum);

extern void (*signal(int signum, void (*handler)(int signum)))(int signum);

如果这仍然让你感到困惑,我不知道如何提供帮助 - 它在某种程度上对我来说仍然很神秘,但我已经习惯了它的工作原理,因此可以告诉你,如果你再坚持 25 年或者这样,这将成为你的第二天性(如果你聪明的话,甚至可能会更快一些)。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

了解 C 中函数指针的 typedef 的相关文章

  • 检测到 NuGet 包的版本冲突

    我正在开发 ASP Net core 2 1 Web 应用程序项目 我的解决方案中有 1 个项目和 3 个其他库 它是高级架构 数据访问层 DAL 业务层 BL 公共层 CL 所以我需要添加引用来连接一些库和项目 我已经添加了CL参考我的项
  • C++ 中的软(不是:弱)引用 - 这可能吗?有实施吗?

    在 C 中我正在使用boost shared ptr and boost weak ptr自动删除不再需要的对象 我知道这些与引用计数一起工作 在 Java 中 内存由垃圾收集器管理 它将内置对象引用视为strong WeakReferen
  • Qt - 无法让 lambda 工作[重复]

    这个问题在这里已经有答案了 我有以下功能 我想在其中修剪我的std set
  • 如果.Net Core可以在Windows上运行,为什么不能在.Net Framework中引用.Net Core DLL?

    我明白为什么 Net Framework 可能会在 Net Core IE 中导致问题 因为不存在特定于 Windows 平台的 API 但是为什么不能直接引用 Net Core 作为 Net Framework 中的库呢 如果 Net C
  • 在 OpenCL 中将函数作为参数传递

    是否可以在 OpenCL 1 2 中将函数指针传递给内核 我知道可以用C实现 但不知道如何在OpenCL的C中实现 编辑 我想做这篇文章中描述的同样的事情 在 C 中如何将函数作为参数传递 https stackoverflow com q
  • Blazor 与 Razor

    随着 Blazor 的发明 我想知道这两种语言之间是否存在显着的效率 无论是在代码创建方面还是在代码的实际编译 执行方面 https github com SteveSanderson Blazor https github com Ste
  • 有什么工具可以说明每种方法运行需要多长时间?

    我的程序的某些部分速度很慢 我想知道是否有我可以使用的工具 例如它可以告诉我可以运行 methodA 花了 100ms 等等 或者类似的有用信息 如果您使用的是 Visual Studio Team System 性能工具 中有一个内置分析
  • ZLIB 解压缩

    我编写了一个小型应用程序 该应用程序应该解压缩以 gzip deflate 格式编码的数据 为了实现这一点 我使用 ZLIB 库 使用解压缩功能 问题是这个功能不起作用 换句话说 数据不是未压缩的 我在这里发布代码 int decompre
  • Xamarin Android:获取内存中的所有进程

    有没有办法读取所有进程 而不仅仅是正在运行的进程 如果我对 Android 的理解正确的话 一次只有一个进程在运行 其他所有进程都被冻结 后台进程被忽略 您可以使用以下代码片段获取当前正在运行的所有 Android 应用程序进程 Activ
  • C++派生模板类继承自模板基类,无法调用基类构造函数[重复]

    这个问题在这里已经有答案了 我试图从基类 模板 继承 派生类也是模板 它们具有相同的类型 T 我收到编译错误 非法成员初始化 Base 不是基类或成员 为什么 如何调用基类构造函数 include
  • 事件日志写入错误

    很简单 我想向事件日志写入一些内容 protected override void OnStop TODO Add code here to perform any tear down necessary to stop your serv
  • C# 创建数组的数组

    我正在尝试创建一个将使用重复数据的数组数组 如下所示 int list1 new int 4 1 2 3 4 int list2 new int 4 5 6 7 8 int list3 new int 4 1 3 2 1 int list4
  • “MyClass”的类型初始值设定项引发异常

    以下是我的Windows服务代码 当我调试代码时 我收到错误 异常 CSMessageUtility CSDetails 的类型初始值设定项引发异常 using System using System Collections Generic
  • std::bind 重载解析

    下面的代码工作正常 include
  • C# 搜索目录中包含字符串的所有文件,然后返回该字符串

    使用用户在文本框中输入的内容 我想搜索目录中的哪个文件包含该文本 然后我想解析出信息 但我似乎找不到该字符串或至少返回信息 任何帮助将不胜感激 我当前的代码 private void btnSearchSerial Click object
  • 过期时自动重新填充缓存

    我当前缓存方法调用的结果 缓存代码遵循标准模式 如果存在 则使用缓存中的项目 否则计算结果 在返回之前将其缓存以供将来调用 我想保护客户端代码免受缓存未命中的影响 例如 当项目过期时 我正在考虑生成一个线程来等待缓存对象的生命周期 然后运行
  • Fluent NHibernate 日期时间 UTC

    我想创建一个流畅的 nhibernate 映射来通过以下方式映射 DateTime 字段 保存时 保存 UTC 值 读取时 调整为本地时区值 实现此映射的最佳方法是什么 就我个人而言 我会将日期存储在 UTC 格式的对象中 然后在读 写时在
  • boost::program_options:带有固定和可变标记的参数?

    是否可以在 boost program options 中使用此类参数 program p1 123 p2 234 p3 345 p12 678 即 是否可以使用第一个标记指定参数名称 例如 p 后跟一个数字 是动态的吗 我想避免这种情况
  • Swagger 为 ASP.CORE 3 中的字典生成错误的 URL

    当从查询字符串中提取的模型将字典作为其属性之一时 Swagger 会生成不正确的 URL 如何告诉 Swagger 更改 URL 中字典的格式或手动定义输入参数模式而不自动生成 尝试使用 Swashbuckle 和 NSwag 控制器 pu
  • WPF/数据集:如何通过 XAML 将相关表中的数据绑定到数据网格列中?

    我正在使用 WPF DataSet 连接到 SQL Server Express XAML 和 C Visual Studio 2013 Express 我从名为 BankNoteBook 的现有 SQL Server Express 数据

随机推荐

  • 如何让 CBC 在时限内返回最佳解决方案? (皮莫)

    我正在尝试在 Pyomo 上使用 CBC v2 10 3 来解决整数线性问题 执行求解器时 我当前设置的时间限制为 600 秒 opt SolverFactory cbc opt options seconds 600 在这个时间限制内 求
  • 在Python中跳过范围函数中的值

    循环一系列数字并跳过一个值的Python式方法是什么 例如 范围是从 0 到 100 我想跳过 50 编辑 这是我正在使用的代码 for i in range 0 len list x listRow list i for j in ran
  • BringIntoView 不起作用

    我在事件处理程序后面有这段代码 private void comboActiveStudentAssignmentType SelectionChanged object sender SelectionChangedEventArgs e
  • Faye 在 jruby 的铁轨上

    我搜索了很多 但找不到任何资源表明我可以将 Faye 与 jruby 一起使用 我已经发现this one https groups google com forum fromgroups topic faye users wvp K38v
  • 在资源路径中找不到 com/sun/jna/android-arm/libjnidispatch.so

    以下所有操作均在 Android Studio 中完成 我已成功编译并测试了 Android Watson Speech to Text 演示应用程序 然后 我创建了一个包含 Watson 相关 API 的库项目和一个带有引用 Watson
  • 如何向 Next.js 静态站点添加网站图标?

    我正在尝试将网站图标添加到 Next js 静态站点 但运气不佳 我尝试使用以下组件自定义文档 next document https nextjs org docs custom document https nextjs org doc
  • 如何连接字符串列表? [复制]

    这个问题在这里已经有答案了 对于你们大多数人来说 这可能非常容易解决 但我无法简单地解决这个问题str 周围可以吗 我想转换这个列表 A B C into A B C In 1 L A B C In 2 join L Out 2 A B C
  • Apache 中的手动内容压缩

    我需要 Apache 上的手动压缩解决方案 我的目标 在我的服务器上提供 gzip 编码的内容以及未压缩的内容 文件已预先压缩 并非所有文件都经过压缩 我想指定这些文件 并且选择不是基于类型 扩展名 的 提供许多内容类型 自定义内容类型 并
  • NuGet 不更新项目引用

    我最近将所有 Visual Studio 2013 项目迁移到 Visual Studio 2015 并按照本文档中记录的步骤进行操作article https docs nuget org consume package restore
  • 为什么在这种无竞争的情况下原子比锁慢得多?

    我使用原子而不是锁编写了一些东西 并且对它在我的情况下慢得多感到困惑 我编写了以下小型测试 include
  • 找不到版本“CXXABI_1.3.8”(...需要)[重复]

    这个问题在这里已经有答案了 当我在编译后运行程序时 出现以下错误 prog usr lib x86 64 linux gnu libstdc so 6 版本CXXABI 1 3 8 not found required by prog pr
  • getchar() 和 putchar() 是函数还是宏?

    我引用了两个可靠的信息来源 两者似乎对同一事物有不同的定义 http www cplusplus com reference clibr E2 80 A6 http www cplusplus com reference clibr E2
  • 如何实时更新glance小部件?

    有可能吗live update的数据Widget using Glance喷气背包组成 就像在数据库发生变化时更新数据一样 例如 当我使用时flow所以 Flow 应该能够更新小部件数据 对吗 我们可以更新点击操作 例如 class MyW
  • 是否可以在没有订阅密钥的情况下调用 Azure API Rest url?

    我只是希望能够使用 Azure Web Api 管理进行休息调用 而无需在 url 中传递订阅密钥 那可能吗 Azure API 管理允许匿名订阅 创建产品而不需要订阅 产品 gt 设置 gt 需要订阅 将您的 API 添加到此产品 htt
  • 是否有理由将数据库连接池与 ActiveRecord 一起使用?

    使用外部连接池有什么好处 我听说大多数其他应用程序都会为每个工作单元打开一个连接 例如 在 Rails 中 我认为这意味着每个请求都可以打开一个新连接 我假设连接池将使这成为可能 我能想到的唯一好处是它允许您拥有 1 000 个前端进程 而
  • 如何用 Java 检测类 Unix 操作系统?

    好的 我知道System getProperty os name 会给我正在运行的操作系统的名称 但这并没有多大帮助 我需要知道的是 我运行的操作系统是否是 类 Unix 操作系统 我不在乎它是 HP UX AIX Mac OS X 还是其
  • 如何在Spring集成中使用两个参数动态调用.handle()内部的重载方法?

    我想让我的 Spring 集成流程对于通过网关传入的不同类型的请求通用 并且我想要重载方法 并希望为通过网关传入的特定消息调用特定的重载方法 flow handle validatorService validateRequest 类似于上
  • Mac 版 Docker 上的 Xdebug 和 SSH 隧道

    我最近在 Docker 社区阅读了很多关于如何使用 Docker for Mac 在 PHPStorm 中调试 PHP 应用程序的帖子 所有这些都包含有用的信息 但在一处还没有看到有效的解决方案 这对我有用 Docker 容器内部 编辑 x
  • 我应该将什么值传递给 TimeZoneInfo.FindSystemTimeZoneById(String)?

    我想使用TimeZoneInfo FindSystemTimeZoneById String http msdn microsoft com en us library system timezoneinfo findsystemtimez
  • 了解 C 中函数指针的 typedef

    当我阅读其他人的代码时 我总是有点困惑 这些代码具有指向带有参数的函数的指针的 typedef 我记得不久前 当我试图理解用 C 编写的数值算法时 我花了一段时间才得到这样的定义 那么 您能否分享一下关于如何为函数指针编写良好的 typed