考虑以下代码:
void f() {};
void* v = f;
int i = f;
int main() { }
为什么将函数地址存储到
int
变量中会给我一个错误:
错误:初始化元素不是编译时常量
但是对于
void*
变量则不然?
当我编译这个时,我得到:
$ gcc foo.c
foo.c:3:5: warning: incompatible pointer to integer conversion initializing 'int' with an expression of type 'void ()' [-Wint-conversion]
int i = f;
^ ~
foo.c:3:9: error: initializer element is not a compile-time constant
int i = f;
^
1 warning and 1 error generated.
真的,我认为警告应该是一个错误。您试图将地址放入整数中,这通常是不好的形式。
也就是说,如果编译器继续进行转换,结果不是编译时常量(因为它是转换后的值)。因此出现错误。
虽然您不是在问 C++,但我很好奇这两种语言有何不同,因此我检查了编译器的行为方式。在这种语言中,来自
f
的两项赋值都是非法的,这是有充分理由的。 void*
不是指向无参数函数的指针,int
也不是。
$ g++ foo.c
clang: warning: treating 'c' input as 'c++' when in C++ mode, this behavior is deprecated
foo.c:2:7: error: cannot initialize a variable of type 'void *' with an lvalue of type 'void ()'
void* v = f;
^ ~
foo.c:3:5: error: cannot initialize a variable of type 'int' with an lvalue of type 'void ()'
int i = f;
^ ~
2 errors generated.
在文件范围(“全局”)声明的变量具有静态存储持续时间。
所有具有静态存储持续时间的变量必须初始化为编译时常量。在 C 中,其他变量(甚至不是
const
变量)不计为编译时常量,因此会出现错误。
此外,您不能将指针分配给整数,这样做是无效的 C。您必须首先通过强制转换将指针手动转换为整数类型。
此外,演员阵容
void* v = f;
也没有明确定义。 C 标准仅指定 void*
和指向对象类型的指针之间的转换。
现在你应该做的是获取函数的地址:
#include <stdint.h>
uintptr_t i = (uintptr_t) f;
int main (void)
{ ...
uintptr_t
保证足够大以包含任何指针的地址。
v
和i
的初始化都是非法的。
但是,某些编译器允许从函数指针转换为
void*
作为编译器扩展。
尝试在编译时打开警告(按照习惯),您也可能会在
void* v = f;
行收到警告。
存储指向函数
f
的指针的正确方法是
void (*p)() = f;
如果您确实想将指针的底层位放入
int
中,您可以尝试以下操作:
typedef union _UPTRINT
{
void *ptr;
int i;
} UPTRINT;
然后将函数指针分配给
UPTRINT::ptr
并以 i
的形式访问它(假设指针和 ints
在您的平台上大小相同;根据需要调整 i
的类型)。