将函数指针转换为另一种类型

2024-04-14

假设我有一个函数接受void (*)(void*)用作回调的函数指针:

void do_stuff(void (*callback_fp)(void*), void* callback_arg);

现在,如果我有一个这样的函数:

void my_callback_function(struct my_struct* arg);

我可以安全地这样做吗?

do_stuff((void (*)(void*)) &my_callback_function, NULL);

我看过这个问题 https://stackoverflow.com/questions/188839/function-pointer-cast-to-different-signature我查看了一些 C 标准,它们说可以转换为“兼容函数指针”,但我找不到“兼容函数指针”含义的定义。


就 C 标准而言,如果将函数指针转换为不同类型的函数指针,然后调用它,那就是未定义的行为。参见附件 J.2(资料性):

在以下情况下,该行为是未定义的:

  • 指针用于调用与所指向的类型不兼容的函数 类型(6.3.2.3)。

第 6.3.2.3 节第 8 段内容如下:

指向一种类型函数的指针可以转换为指向另一种类型函数的指针 键入并再次返回;结果应等于原始指针。如果转换成 指针用于调用类型与所指向类型不兼容的函数, 该行为是未定义的。

换句话说,您可以将函数指针转换为不同的函数指针类型,再次将其转换回来,然后调用它,事情就会起作用。

的定义兼容的有点复杂。可以在第 6.7.5.3 节第 15 段中找到:

For two function types to be compatible, both shall specify compatible return types127.

此外,如果参数类型列表都存在,则参数类型列表的数量应一​​致 参数以及省略号终止符的使用;相应的参数应有 兼容类型。如果一种类型具有参数类型列表,而另一种类型由 函数声明符不是函数定义的一部分并且包含空 标识符列表,参数列表不应有省略号终止符以及每个参数的类型 参数应与应用程序产生的类型兼容 默认参数促销。如果一种类型有参数类型列表,另一种类型是 由包含(可能为空)标识符列表的函数定义指定,两者都应 参数个数一致,每个原型参数的类型为 与应用默认参数所产生的类型兼容 晋升为相应标识符的类型。 (在确定类型时 兼容性和复合类型,每个参数都用函数或数组声明 type 被视为具有调整后的类型,并且每个参数都使用限定类型声明 被视为具有其声明类型的非限定版本。)

127) 如果两个函数类型都是“旧式”,则不比较参数类型。

确定两种类型是否兼容的规则在第 6.2.7 节中描述,由于它们相当冗长,我不会在这里引用它们,但是您可以在C99 标准草案 (PDF) http://www.open-std.org/JTC1/SC22/WG14/www/docs/n1256.pdf.

相关规则见第 6.7.5.1 节第 2 段:

为了使两个指针类型兼容,两者都应具有相同的限定,并且两者都应是指向兼容类型的指针。

因此,自从一个void* 不兼容 https://stackoverflow.com/questions/11647220/are-void-pointer-and-pointer-to-some-structure-layout-compatible with a struct my_struct*, 类型的函数指针void (*)(void*)与类型的函数指针不兼容void (*)(struct my_struct*),因此函数指针的这种转换在技术上是未定义的行为。

但实际上,在某些情况下,您可以安全地摆脱函数指针的转换。在 x86 调用约定中,参数被压入堆栈,并且所有指针的大小相同(x86 中为 4 字节,x86_64 中为 8 字节)。调用函数指针归结为将参数压入堆栈并间接跳转到函数指针目标,并且在机器代码级别显然没有类型的概念。

事情你绝对can't do:

  • 在不同调用约定的函数指针之间进行转换。你会弄乱堆栈,最好的情况是崩溃,最坏的情况是,通过一个巨大的安全漏洞默默地成功。在 Windows 编程中,经常传递函数指针。 Win32 期望所有回调函数都使用stdcall调用约定(其中宏CALLBACK, PASCAL, and WINAPI全部展开为)。如果传递使用标准 C 调用约定的函数指针 (cdecl),就会产生不好的结果。
  • 在 C++ 中,类成员函数指针和常规函数指针之间的转换。这常常会让 C++ 新手犯难。类成员函数有一个隐藏的this参数,如果将成员函数转换为常规函数,则没有this反对使用,同样会导致很多不好的结果。

另一个坏主意有时可能有效,但也是未定义的行为:

  • 在函数指针和常规指针之间进行转换(例如,转换void (*)(void) to a void*)。函数指针的大小不一定与常规指针相同,因为在某些体系结构上它们可能包含额外的上下文信息。这在 x86 上可能可以正常工作,但请记住这是未定义的行为。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

将函数指针转换为另一种类型 的相关文章

  • 尚未注册类型“IServiceProviderFactory[Autofac.ContainerBuilder]”的服务

    当运行以下命令添加数据库迁移脚本时 出现以下错误 dotnet ef migrations add InitialCreate v o Migrations context MyContext 访问 Microsoft Extensions
  • QCombobox 向下箭头图像

    如何更改Qcombobox向下箭头图像 现在我正在使用这个 QSS 代码 但这不起作用 我无法删除向下箭头边框 QComboBox border 0px QComboBox down arrow border 0px background
  • 在实体框架拦截器中向 DbScanExpression 添加内部联接

    我正在尝试使用实体框架 CommandTree 拦截器通过 DbContext 向每个查询添加过滤器 为了简单起见 我有两个表 一个称为 User 有两列 UserId 和 EmailAddress 另一个称为 TenantUser 有两列
  • 我如何在 C# .NET(win7 手机)中使用“DataContractJsonSerializer”读入“嵌套”Json 文件?

    我有一个问题 如果我的 json 文件看起来像这样 Numbers 45387 Words 空间桶 我可以很好地阅读它 但是如果它看起来像这样 Main Numbers 45387 Words 空间桶 某事 数字 12345 单词 克兰斯基
  • 为什么这个没有特殊字符的正则表达式会匹配更长的字符串?

    我正在使用此方法来尝试查找匹配项 例如 Regex Match A2 TS OIL TS OIL RegexOptions IgnoreCase Success 我得到了真实的结果 我很困惑 我认为这应该返回 false 因为模式中没有特殊
  • 找不到 assimp-vc140-mt.dll ASSIMP

    我已经从以下位置下载了 Assimp 项目http assimp sourceforge net main downloads html http assimp sourceforge net main downloads html Ass
  • 类的成员复制

    在学习 复制成员 概念时 书中给出了如下说法 此外 如果非静态成员是引用 const 或没有复制赋值的用户定义类型 则无法生成默认赋值 我不太明白这个声明到底想传达什么 或者说这个说法指的是哪一种场景 谢谢 该语句与编译器自动为您编写的类
  • vs2008 c#:Facebook.rest.api如何使用它来获取好友列表?

    如何在此基础上取得进一步的进步 获取好友列表的下一步是什么 string APIKey ConfigurationManager AppSettings API Key string APISecret ConfigurationManag
  • 如何获取 QTableView 的标题列表?

    我有一个QTableView我的对话框中的对象 我需要访问该表的水平标题并将它们放入QStringList object 尽管进行了大量搜索 但我在 Qt 文档中找不到如何获取此标头列表 编辑 我发现的最接近的地方是this https w
  • Visual Studio Code:如何配置 includePath 以获得更好的 IntelliSense 结果

    我是使用 Visual Studio Code 的完全初学者 我不知道我在做什么 我已经四处搜索 也许还不够 但我找不到像我这样的人如何配置的简单解释c cpp properties json每当我单击带有绿色波浪线下划线的行旁边的黄色灯泡
  • 检查 RoutedEvent 是否有任何处理程序

    我有一个自定义 Button 类 当单击它时 打开特定窗口 它总是执行相同的操作 我添加了一个可以在按钮的 XAML 中分配的 Click 事件 就像常规按钮一样 当它被单击时 我想执行 Click 事件处理程序 如果已分配 否则我想执行默
  • AES 输出是否小于输入?

    我想加密一个字符串并将其嵌入到 URL 中 因此我想确保加密的输出不大于输入 AES 是可行的方法吗 不可能创建任何始终会创建比输入更小的输出的算法 但可以将任何输出反转回输入 如果您允许 不大于输入 那么基本上您只是在谈论同构算法alwa
  • 如何在标准 WPF ListView 中启用 UI 虚拟化

    我正在使用 NET 4 5 VS2012 并且我有一个 ListView 看起来像这样
  • 将标量添加到特征矩阵(向量)

    我刚刚开始使用 Eigen 库 无法理解如何向所有矩阵成员添加标量值 假设我有一个矩阵 Eigen Matrix3Xf mtx Eigen Matrix3Xf Ones 3 4 mtx mtx 1 main cxx 104 13 error
  • C# 中的 strstr() 等效项

    我有两个byte 我想找到第二个的第一次出现byte 在第一个byte 或其中的一个范围 我不想使用字符串来提高效率 翻译第一个byte to a string会效率低下 基本上我相信就是这样strstr 在 C 中做 最好的方法是什么 这
  • 在 EnvDTE 中调试时捕获 VS 局部变量

    是否可以使用 EnvDTE 进行 vsix Visual Studio 扩展来捕获本地和调试窗口使用的调试数据 或者可以通过其他方法吗 我想创建一个自定义的本地窗口 我们可以修改它以根据需要显示一些较重的内容 而无需为高级用户牺牲原始的本地
  • cout 和字符串连接

    我刚刚复习了我的 C 我尝试这样做 include
  • C++ Streambuf 方法可以抛出异常吗?

    我正在尝试找到一种方法来获取读取或写入流的字符数 即使存在错误并且读 写结束时间较短 该方法也是可靠的 我正在做这样的事情 return stream rdbuf gt sputn buffer buffer size 但如果streamb
  • 将 char[][] 转换为 char** 会导致段错误吗?

    好吧 我的 C 有点生疏了 但我想我应该用 C 来做我的下一个 小 项目 这样我就可以对其进行抛光 并且我已经有不到 20 行的段错误了 这是我的完整代码 define ROWS 4 define COLS 4 char main map
  • 使用 QtWebEngine 将 C++ 对象暴露给 Qt 中的 Javascript

    使用 QtWebkit 可以通过以下方式将 C 对象公开给 JavascriptQWebFrame addToJavaScriptWindowObject如中所述https stackoverflow com a 20685002 5959

随机推荐