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

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(使用前将#替换为@)

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

  • 如何使用 C# 中的参数将用户重定向到 paypal

    如果我有像下面这样的简单表格 我可以用它来将用户重定向到 PayPal 以完成付款
  • 我如何才能等待多个事情

    我正在使用 C 11 和 stl 线程编写一个线程安全队列 WaitAndPop 方法当前如下所示 我希望能够将一些内容传递给 WaitAndPop 来指示调用线程是否已被要求停止 如果 WaitAndPop 等待并返回队列的元素 则应返回
  • 通过 CMIS (dotCMIS) 连接到 SP2010:异常未经授权

    我正在使用 dotCMIS 并且想要简单连接到我的 SP2010 服务器 我尝试用 C 来做到这一点 如下所示http chemistry apache org dotnet getting started with dotcmis htm
  • WCF RIA 服务 - 加载多个实体

    我正在寻找一种模式来解决以下问题 我认为这很常见 我正在使用 WCF RIA 服务在初始加载时将多个实体返回给客户端 我希望两个实体异步加载 以免锁定 UI 并且我想利用 RIA 服务来执行此操作 我的解决方案如下 似乎有效 这种方法会遇到
  • 在哪里可以找到列出 SSE 内在函数操作的官方参考资料?

    是否有官方参考列出了 GCC 的 SSE 内部函数的操作 即 头文件中的函数 除了 Intel 的 vol 2 PDF 手册外 还有一个在线内在指南 https www intel com content www us en docs in
  • ASP.NET MVC:这个业务逻辑应该放在哪里?

    我正在开发我的第一个真正的 MVC 应用程序 并尝试遵循一般的 OOP 最佳实践 我正在将控制器中的一些简单业务逻辑重构到我的域模型中 我最近一直在阅读一些内容 很明显我应该将逻辑放在域模型实体类中的某个位置 以避免出现 贫血域模型 反模式
  • Asp.NET WebApi 中类似文件名称的路由

    是否可以在 ASP NET Web API 路由配置中添加一条路由 以允许处理看起来有点像文件名的 URL 我尝试添加以下条目WebApiConfig Register 但这不起作用 使用 URIapi foo 0de7ebfa 3a55
  • 嵌套接口:将 IDictionary> 转换为 IDictionary>?

    我认为投射一个相当简单IDictionary
  • 在 Windows 窗体中保存带有 Alpha 通道的单色位图会保存不同(错误)的颜色

    在 C NET 2 0 Windows 窗体 Visual Studio Express 2010 中 我保存由相同颜色组成的图像 Bitmap bitmap new Bitmap width height PixelFormat Form
  • HTTPWebResponse 响应字符串被截断

    应用程序正在与 REST 服务通信 Fiddler 显示作为 Apps 响应传入的完整良好 XML 响应 该应用程序位于法属波利尼西亚 在新西兰也有一个相同的副本 因此主要嫌疑人似乎在编码 但我们已经检查过 但空手而归 查看流读取器的输出字
  • 不同枚举类型的范围和可转换性

    在什么条件下可以从一种枚举类型转换为另一种枚举类型 让我们考虑以下代码 include
  • 在 ASP.NET 5 中使用 DI 调用构造函数时解决依赖关系

    Web 上似乎充斥着如何在 ASP NET 5 中使用 DI 的示例 但没有一个示例显示如何调用构造函数并解决依赖关系 以下只是众多案例之一 http social technet microsoft com wiki contents a
  • C#中如何移动PictureBox?

    我已经使用此代码来移动图片框pictureBox MouseMove event pictureBox Location new System Drawing Point e Location 但是当我尝试执行时 图片框闪烁并且无法识别确切
  • 如何设计以 char* 指针作为类成员变量的类?

    首先我想介绍一下我的情况 我写了一些类 将 char 指针作为私有类成员 而且这个项目有 GUI 所以当单击按钮时 某些函数可能会执行多次 这些类是设计的单班在项目中 但是其中的某些函数可以执行多次 然后我发现我的项目存在内存泄漏 所以我想
  • 如何在 C 中调用采用匿名结构的函数?

    如何在 C 中调用采用匿名结构的函数 比如这个函数 void func struct int x p printf i n p x 当提供原型的函数声明在范围内时 调用该函数的参数必须具有与原型中声明的类型兼容的类型 其中 兼容 具有标准定
  • 如何使用 C# / .Net 将文件列表从 AWS S3 下载到我的设备?

    我希望下载存储在 S3 中的多个图像 但目前如果我只能下载一个就足够了 我有对象路径的信息 当我运行以下代码时 出现此错误 遇到错误 消息 读取对象时 访问被拒绝 我首先做一个亚马逊S3客户端基于我的密钥和访问配置的对象连接到服务器 然后创
  • 对现有视频添加水印

    我正在寻找一种用 C 在视频上加水印的方法 就像在上面写文字一样 图片或文字标签 我该怎么做 谢谢 您可以使用 Nreco 视频转换器 代码看起来像 NReco VideoConverter FFMpegConverter wrap new
  • 如何从两个不同的项目中获取文件夹的相对路径

    我有两个项目和一个共享库 用于从此文件夹加载图像 C MainProject Project1 Images 项目1的文件夹 C MainProject Project1 Files Bin x86 Debug 其中有project1 ex
  • C# 模拟VolumeMute按下

    我得到以下代码来模拟音量静音按键 DllImport coredll dll SetLastError true static extern void keybd event byte bVk byte bScan int dwFlags
  • 使用.NET技术录制屏幕视频[关闭]

    Closed 这个问题正在寻求书籍 工具 软件库等的推荐 不满足堆栈溢出指南 help closed questions 目前不接受答案 有没有一种方法可以使用 NET 技术来录制屏幕 无论是桌面还是窗口 我的目标是免费的 我喜欢小型 低

随机推荐