铸造一个函数指针到另一种类型 [英] Casting a function pointer to another type

查看:149
本文介绍了铸造一个函数指针到另一种类型的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

让我们说我有一个接受无效(*)(无效*)用作一个回调函数指针的函数:

Let's say I have a function that accepts a void (*)(void*) function pointer for use as a callback:

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

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

Now, if I have a function like this:

void my_callback_function(struct my_struct* arg);

我可以这样做安全?

Can I do this safely?

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

我看了这个问题,我已经看了一些C标准,说可以转换为'兼容函数指针',但我找不到什么'兼容函数指针'指的定义。

I've looked at this question and I've looked at some C standards which say you can cast to 'compatible function pointers', but I cannot find a definition of what 'compatible function pointer' means.

推荐答案

至于C标准来讲,如果你投一个函数指针到不同类型的函数指针,然后调用,它的未定义行为的。见附件J.2:

As far as the C standard is concerned, if you cast a function pointer to a function pointer of a different type and then call that, it is undefined behavior. See Annex J.2:

该行为是在下列情况下未定义:

The behavior is undefined in the following circumstances:


      
  • 的指针可以用来调用其类型的函数是不是与指向的兼容
      类型(6.3.2.3)。

  •   

6.3.2.3节,第8段内容如下:

Section 6.3.2.3, paragraph 8 reads:

一个指针一种类型的功能可以被转换为一个指针到另一个功能
  类型,然后再返回;结果应比较等于原始指针。如果转换
  指针用来调用其类型不兼容功能指向的类型,
  该行为是不确定的。

A pointer to a function of one type may be converted to a pointer to a function of another type and back again; the result shall compare equal to the original pointer. If a converted pointer is used to call a function whose type is not compatible with the pointed-to type, the behavior is undefined.

所以,换句话说,你可以投一个函数指针到不同的函数指针类型,再次施放它,并调用它,事情会工作。

So in other words, you can cast a function pointer to a different function pointer type, cast it back again, and call it, and things will work.

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

The definition of compatible is somewhat complicated. It can be found in section 6.7.5.3, paragraph 15:

有关两个函数类型兼容,既应规定兼容的返回类型 127

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

此外,参数类型列表中,如果两者都present,应以数量一致
  参数和使用中的省略号终止子;相应参数应
  兼容类型。如果一种类型的具有一个参数类型列表,并通过一个指定的其它类型的
  函数声明这不是一个函数定义的一部分,包含一个空
  标识符列表,参数列表中不应有一个省略号终结,每个类型
  参数应与从的结果的应用的类型兼容
  默认参数促销。如果一种类型的具有一个参数类型列表及另一种类型是
  通过包含(可能为空)标识符列表函数定义规定的,两个人
  同意的参数的数目,和每个原型参数的类型应
  与从默认参数的应用效果的类型兼容
  促销到相应的识别符的类型。 (式中的测定
  兼容性和复合类型,函数或数组声明的每个参数
  类型作为具有调整的类型和限定的类型声明的每个参数
  被视为有其声明的类型的无保留意见的版本。)

Moreover, the parameter type lists, if both are present, shall agree in the number of parameters and in use of the ellipsis terminator; corresponding parameters shall have compatible types. If one type has a parameter type list and the other type is specified by a function declarator that is not part of a function definition and that contains an empty identifier list, the parameter list shall not have an ellipsis terminator and the type of each parameter shall be compatible with the type that results from the application of the default argument promotions. If one type has a parameter type list and the other type is specified by a function definition that contains a (possibly empty) identifier list, both shall agree in the number of parameters, and the type of each prototype parameter shall be compatible with the type that results from the application of the default argument promotions to the type of the corresponding identifier. (In the determination of type compatibility and of a composite type, each parameter declared with function or array type is taken as having the adjusted type and each parameter declared with qualified type is taken as having the unqualified version of its declared type.)

127),如果这两个函数类型''旧样式',参数类型不比较。

127) If both function types are ‘‘old style’’, parameter types are not compared.

确定两类无论是在6.2.7节中描述兼容,我也不会在这里引用他们,因为他们是相当长的,但你可以在draft C99标准(PDF)的。

The rules for determining whether two types are compatible are described in section 6.2.7, and I won't quote them here since they're rather lengthy, but you can read them on the draft of the C99 standard (PDF).

这里的有关规则是在6.7.5.1节,第2款规定:

The relevant rule here is in section 6.7.5.1, paragraph 2:

有关两种指针类型兼容,两者应是相同的合格都应当指向兼容的类型。

For two pointer types to be compatible, both shall be identically qualified and both shall be pointers to compatible types.

因此​​,因为无效* 是一个结构my_struct * ,类型的函数指针不兼容无效(*)(无效*)不是类型无效的函数指针(*)(结构my_struct *)兼容,所以这个转换函数指针是技术上未定义的行为。

Hence, since a void* is not compatible with a struct my_struct*, a function pointer of type void (*)(void*) is not compatible with a function pointer of type void (*)(struct my_struct*), so this casting of function pointers is technically undefined behavior.

在实践中,不过,你可以放心地逃脱在某些情况下铸造的函数指针。在x86调用约定,参数被压入堆栈,所有指针都是相同的尺寸(86 4个字节或x86_64的8个字节)。调用一个函数指针归结为推栈上的参数和做一个间接的跳转到函数指针目标,显然有在机器code级没有类型的概念。

In practice, though, you can safely get away with casting function pointers in some cases. In the x86 calling convention, arguments are pushed on the stack, and all pointers are the same size (4 bytes in x86 or 8 bytes in x86_64). Calling a function pointer boils down to pushing the arguments on the stack and doing an indirect jump to the function pointer target, and there's obviously no notion of types at the machine code level.

你的东西一定的不能做的:


  • 不同的调用约定的函数指针之间进行转换。你会弄乱堆栈,充其量,崩溃,在最坏的情况,默默的巨大巨大的安全漏洞成功。在Windows编程中,你经常传递函数指针左右。 Win32的期望所有回调函数使用 STDCALL 调用约定(该宏回调 PASCAL WINAPI 都扩展为)。如果您通过使用标准的C调用约定( CDECL ),不良将导致一个函数指针。

  • 在C ++中,类成员函数指针和普通函数指针之间进行转换。这往往绊倒了C ++的新手。类的成员函数有一个隐藏的这个参数,如果你投一个成员函数普通函数,没有这个对象使用,并再次,很多不良会导致。

  • Cast between function pointers of different calling conventions. You will mess up the stack and at best, crash, at worst, succeed silently with a huge gaping security hole. In Windows programming, you often pass function pointers around. Win32 expects all callback functions to use the stdcall calling convention (which the macros CALLBACK, PASCAL, and WINAPI all expand to). If you pass a function pointer that uses the standard C calling convention (cdecl), badness will result.
  • In C++, cast between class member function pointers and regular function pointers. This often trips up C++ newbies. Class member functions have a hidden this parameter, and if you cast a member function to a regular function, there's no this object to use, and again, much badness will result.

另一个不好的想法,有时会工作,但也是未定义的行为:

Another bad idea that might sometimes work but is also undefined behavior:


  • 函数指针和定期的指针之间铸件(如铸造无效(*)(无效)无效* )。函数指针不一定是大小作为普通指针一样,因为在一些系统上它们可能包含额外的上下文信息。这可能会在x86好的工作,但请记住,这是不确定的行为。

  • Casting between function pointers and regular pointers (e.g. casting a void (*)(void) to a void*). Function pointers aren't necessarily the same size as regular pointers, since on some architectures they might contain extra contextual information. This will probably work ok on x86, but remember that it's undefined behavior.

这篇关于铸造一个函数指针到另一种类型的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆