【问题标题】:How to print the address of a function?如何打印函数的地址?
【发布时间】:2012-06-11 13:44:09
【问题描述】:

我让gcc使用-Wall -pedantic编译下面的例子:

#include <stdio.h>

int main(void)
{
  printf("main: %p\n", main); /* line 5 */
  printf("main: %p\n", (void*) main); /* line 6 */

  return 0;
}

我明白了:

main.c:5: warning: format ‘%p’ expects type ‘void *’, but argument 2 has type ‘int (*)()’
main.c:6: warning: ISO C forbids conversion of function pointer to object pointer type

第 5 行让我更改了第 6 行中的代码。

在打印函数地址时,我缺少什么来删除警告?

【问题讨论】:

  • 不确定它是否对您可用,但您可以使用 register_printf_function 对define your own special format character and converter 进行调查。
  • 问题不在于它“危险”。问题是转换不是由 C 语言定义的,因此不能用于符合 C 代码。你可以通过一个中间整数类型(实现定义的结果)进行转换,只要你知道存在一个可以同时保存函数和对象指针的类型)。
  • @BobJarvis 在使用选项-Wall 进行编译时,您知道如何绕过gcc 抱怨新引入的转换类型字符(warning: unknown conversion type character ‘P’ in format)吗?但这也是另一个故事......
  • @R..:这是 C。我们很危险。
  • @alk:尝试在 -Wall 之后添加 -Wno-format 选项,这(如果有记忆的话)将关闭 printf/scanf 格式检查。

标签: c linux gcc function-pointers gcc-warning


【解决方案1】:

这实际上是打印函数指针的唯一可移植方式。

size_t i;
int (*ptr_to_main)() = main;
for (i=0; i<sizeof ptr_to_main; i++)
    printf("%.2x", ((unsigned char *)&ptr_to_main)[i]);
putchar('\n');

【讨论】:

  • 我喜欢这个,虽然它默默地忽略了任何字节序问题...... ;-)
  • 试图概括这种方法,我想知道是否可以保证不同类型的函数指针具有相同的大小,尽管对 larsman 引用的问题的回答 (stackoverflow.com/a/189126/694576) 让我对此表示怀疑.. .-无论如何,这是另一个故事。
  • 根据 C 标准,定义了任意两个函数指针之间的转换,并且保留了往返值;见 6.3.2.3 结尾。
  • 我会在循环外投射一次,但这是最好的方法。
  • @ouah:确实你错过了一些东西。 ptr_to_main 是一个对象,&amp;ptr_to_main 是一个对象指针。
【解决方案2】:

整个想法确实是不可移植的,因为一些系统使用不同大小的指针来指向代码和数据。

您真正需要的是关于函数指针有多大的特定于平台的知识,以及转换为该大小的整数类型。不幸的是,我认为没有人将intfuncptr_t 标准化为intptr_t,它可以保存任何数据指针。


正如 R. 所说的 in his answer,您始终可以将指针视为(可能是 signedunsignedchar 的数组,这样您就不需要任何大小正确的整数类型。

【讨论】:

  • 不是任何整数数据类型;只有unsigned char(或者可能是char,虽然这很混乱而且很不确定)。任何其他类型都是别名违规,现代编译器可能会发出不符合您要求的错误代码。
  • @R..:转换不能是别名违规。只有当你有两个不同类型的左值引用同一个位置时,才会出现别名冲突。请注意,我说的是reinterpret_cast&lt;integral_type&gt;(fnptr),而不是*reinterpret_cast&lt;integral_type*&gt;(&amp;fnptr)。您的答案涉及合法别名,我的答案根本不涉及别名。哦,现在我明白了,你在谈论我对你方法的解释。是的,那里需要char,我会更新。
  • (uintmax_t)&amp;main,也许还有static_assert( sizeof(uintmax_t) &gt;= sizeof(&amp;main) );,希望确保没有由于超出范围的转换而导致未诊断的UB
  • Re:“你总是可以把指针当作一个数组......”:你能指出C标准中所说的地方吗?
  • 仅供参考:与“您始终可以将指针视为数组...”相关:“必须通过其声明的类型或通过指向字符类型的指针来访问变量”(source) .
【解决方案3】:

就在警告中:ISO C 禁止将函数指针转换为对象指针类型,其中包括void*。另见this question

您根本无法以可移植的方式打印函数的地址,因此您无法摆脱警告。

您可以使用@R.. 的建议打印函数指针。

【讨论】:

  • 对于 POSIX,所有指针的表示都是相同的,因此在 POSIX 系统上,您可以将函数指针变量 memcpy 从函数指针变量转换为数据指针变量,然后打印出来。当然,您可以始终逐字节打印任何类型的表示,而无需 POSIX 依赖。
【解决方案4】:

虽然将函数指针转换为 void 指针在技术上是危险的,但将函数指针转换为 void 指针在 POSIX 标准中使用,因此几乎可以肯定大多数编译器都可以使用。

查找dlsym()

【讨论】:

  • 大部分 C 代码在符合 POSIX 的环境之外运行。
  • dlsym() 的返回值分配给正确声明的函数指针变量会导致类似的问题,但反过来。
  • 因此,基本上,POSIX-2001 与使用与 void* 不同的函数指针大小的 ABI 定义不兼容。很高兴知道。
猜你喜欢
  • 2012-06-22
  • 1970-01-01
  • 1970-01-01
  • 2011-03-05
  • 1970-01-01
  • 1970-01-01
  • 2017-02-21
  • 1970-01-01
相关资源
最近更新 更多