如何格式化函数指针?

时间:2010-04-30 00:49:02

标签: c function-pointers

有没有办法在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   禁止转换函数指针   对象指针类型
test.c:7:   警告:ISO C禁止转换   函数指针指向对象   类型
$ ./test
0x400518
  0x400518

它“有效”,但非标准......

6 个答案:

答案 0 :(得分:44)

执行此操作的唯一合法方法是使用字符类型访问构成指针的字节。像这样:

#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 *或任何非字符类型(dreamlax's answer does),是未定义的行为。

构成函数指针的那些字节实际上意味着与实现有关。例如,它们只能表示函数表的索引。

答案 1 :(得分:5)

使用可以绕过警告/错误的联合,但结果仍然(很可能)是未定义的行为:

#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));)来测试它们是否相等(我知道这完全违背了目的并且很可能无关紧要),但是至于实际输出函数指针的地址,发生的事情取决于目标平台的C库和编译器的供应商。

答案 2 :(得分:2)

将函数指针转换为整数,然后再将其强制转换为指针以使用“%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),指向数据的指针和指向函数的指针的大小不同。

答案 3 :(得分:1)

使用gcc 4.3.4,使用开关-O2 -Wstrict-aliasing,dreamlax的答案将产生:

warning: dereferencing type-punned pointer will break strict-aliasing rules

已添加:我认为对于有关 endianness 和大小的回答的反对是合理的,他的解决方案没有解决(没有双关语意图) )。达斯汀关于使用联合演员的建议可能是合法的(虽然从我读到的内容中似乎存在一些争议,但你的编译器确实比法律重要)。但他的代码可以通过单行简化(或根据您的品味进行混淆):

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);

但是对于最初的问题:它的起源在于使用-pedantic,我认为这有点迂腐:)。

进一步修改:请注意,您不能在最后一个示例中使用 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!

因为当然&amp; main 衰退到主要

答案 4 :(得分:1)

试试这个:

#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 c.c

使用这些标志没有发出警告

答案 5 :(得分:0)

我不确定这是否是犹太教,但是它实现了效果,没有循环,联合,强制转换为非指针类型,或除string.h之外的额外依赖

int (*funcptr)() = main;
void* p = NULL;
memcpy(&p, (void**) &funcptr, sizeof(funcptr));
printf("%p", p);

GCC 7.1.1上没有gcc -ansi -pedantic -Wall -Wstrict-aliasing的警告