如何格式化函数指针?
有没有办法在ANSI C中打印指向函数的指针? 当然这意味着你必须将函数指针强制转换为void指针,但是看起来不可能?
#include <stdio.h> int main() { int (*funcptr)() = main; printf("%p\n", (void* )funcptr); printf("%p\n", (void* )main); return 0; }
$ gcc -ansi -pedantic -Wall test.c -o test
test.c:在函数'main'中:
test.c:6:警告:ISO C禁止将函数指针转换为对象指针types
test.c:7:警告:ISO C禁止将函数指针转换为对象指针types
$ ./test
0x400518
0x400518
这是“工作”,但非标准…
唯一合法的方法是使用字符types访问组成指针的字节。 喜欢这个:
#include <stdio.h> int main() { int (*funcptr)() = main; unsigned char *p = (unsigned char *)&funcptr; size_t i; for (i = 0; i < sizeof funcptr; i++) { printf("%02x ", p[i]); } putchar('\n'); return 0; }
将函数指针作为void *
,或者任何非字符types,就像dreamlax的答案一样 ,是未定义的行为。
构成函数指针的那些字节实际上是什么意思是依赖于实现的。 例如,它们可以表示一个函数表的索引。
有可以解决警告/错误的工会的使用,但结果仍然(很有可能)未定义的行为:
#include <stdio.h> int main (void) { union { int (*funcptr) (void); void *objptr; } u; u.funcptr = main; printf ("%p\n", u.objptr); return 0; }
你可以使用if语句比较两个函数指针(例如printf ("%i\n", (main == funcptr));
)来testing它们是否相等(我知道完全无法达到目的,也是不相干的),但就实际输出函数指针的地址而言,会发生什么取决于你的目标平台的C库和你的编译器的供应商。
将函数指针转换为整数,然后再次将其转换为指针以使用“%p”。
#include <stdio.h> int main() { int (*funcptr)() = main; printf("%p\n", (void *)(size_t) funcptr); printf("%p\n", (void *)(size_t) main); return 0; }
请注意,在某些平台上(例如“中等”或“紧凑型”内存模式中的16位DOS),指向数据和指向函数的指针的大小不一样。
尝试这个:
#include <stdio.h> #include <inttypes.h> int main() { int (*funcptr)() = main; unsigned char *p = (unsigned char *)&funcptr; int i; /* sample output: 00000000004005e0 */ printf("%016"PRIxPTR"\n", (uintptr_t)main); /* sample output: 00000000004005e0 */ printf("%016"PRIxPTR"\n", (uintptr_t)funcptr); /* reflects the fact that this program is running on little-endian machine sample output: e0 05 40 00 00 00 00 00 */ for (i = 0; i < sizeof funcptr; i++) { printf("%02x ", p[i]); } putchar('\n'); return 0; }
使用这个标志:
gcc -ansi -pedantic -Wall -O2 -Wstrict-aliasing cc
没有使用这些标志发出的警告
用gcc 4.3.4,使用开关-O2-wstrict-aliasing,dreamlax的答案会产生:
warning: dereferencing type-punned pointer will break strict-aliasing rules
补充:我认为反对caf关于字节顺序和大小的回答是合理的,他的解决scheme没有解决(不是双关语)。 达斯汀提出的使用工会组织的build议可能是合法的(尽pipe从我所读的内容来看,似乎存在一些争论,但是你的编译器确实比法律重要)。 但是他的代码可以通过一行代码来简化(或根据自己的口味进行混淆)
printf("%p\n", ((union {int (*from)(void); void *to;})funcptr).to);
这消除了gcc严格别名警告(但它是“正确的”?)。
如果您正在使用-pedantic开关,或者正在使用例如SGI IRIX,聚合转换将不会“工作”,因此您需要使用:
printf("%p\n", ((union {int (*from)(void); void *to;} *)&funcptr)->to);
但是关于原来的问题:它的起源在于使用“ped </s>”,我认为这有点迂腐:)。
进一步编辑:请注意,在上例中不能使用main ,如下所示:
printf("%p\n", ((union {int (*from)(void); void *to;}) main).to); // ok printf("%p\n", ((union {int (*from)(void); void *to;} *)&main)->to); // wrong!
因为当然主要的衰变是主要的 。