函数指针的大小有什么保证?

41

在C语言中,我需要知道一个结构体的大小,其中包含函数指针。我可以确定在所有平台和架构上:

  • void*的大小与函数指针相同吗?
  • 函数指针的大小不会因其返回类型而有所不同吗?
  • 函数指针的大小不会因其参数类型而有所不同吗?

我假设所有这些问题的答案都是肯定的,但我想要确认一下。为了背景信息,我只是调用sizeof(struct mystruct),没有其他操作。

4个回答

41
根据 C99 规范第6.2.5节第27段所述:指向void的指针应具有与指向字符类型的指针相同的表示和对齐要求。类似地,指向兼容类型的限定或非限定版本的指针应具有相同的表示和对齐要求。所有指向结构类型的指针应具有彼此相同的表示和对齐要求。所有指向联合类型的指针应具有彼此相同的表示和对齐要求。指向其他类型的指针不需要具有相同的表示或对齐要求。
因此,不能保证 `void *` 可以保存函数指针。
根据 C99 规范第6.3.2.3节第8段所述:一个类型的函数指针可以转换为另一个类型的函数指针,然后再转换回来;结果应与原始指针相等。
这意味着一个函数指针类型可以保存任何其他函数指针值。严格来说,并不保证函数指针类型的大小不变,仅仅是它们的值占用了相同的范围。

23

不,不,不。

C语言并不偏爱哈佛架构的处理器,这种处理器使用大小不同的代码和数据指针,因为理想情况下,当你为这样的架构编程时,想要将数据存储在程序内存中(如字符串文字等),而要做到这一点,你需要将对象指针指向代码区。但它并没有禁止使用这种处理器,因此就标准而言,函数指针可以引用与数据地址空间大小不同的地址空间。

然而,任何函数指针都可以转换为另一个函数指针类型[*],并且再转换回去时不会损坏值,就像任何对象指针都可以转换为 void *,再转换回去一样。因此,如果函数指针的大小根据其签名而异,则会令人惊讶。如果您必须能够以某种方式在更少的空间中存储相同的值,然后在进行转换时检索它,那么额外的空间就没有明显的“用途”。

[*] 感谢schot。


5
一般而言:任何函数指针都可以转换为任何其他函数指针类型,然后再转回原类型。(C99 6.3.2.3/8) - schot
4
“C并不真正支持哈佛架构”,我认为这是一个误导。有很多这样的架构支持C语言! - Oliver Charlesworth
@Oli:同意,“支持”这个词用错了。你可以编写符合规范的实现,但如果你想要(a)对象指针比函数指针更小,以及(b)将数据存储在代码空间中作为程序员控制的优化,你需要一些丑陋的技巧。我再试一次。 - Steve Jessop
1
@Oli:当然,你可以将东西复制到数据空间中,但这样就有了(a)而没有(b)。你占用了宝贵的内存,这正是激励程序员控制优化的动力所在。我所说的“丑陋的黑客”是指http://www.nongnu.org/avr-libc/user-manual/pgmspace.html。你不能得到一个真正的“对象”指针,例如程序本身可以传递给`memcpy`的指针,因为`memcpy`不知道如何解引用它。要想得到它,你必须放弃,并增加对象指针的大小,这又会消耗宝贵的内存,可能需要在每次内存访问时进行运行时切换! - Steve Jessop
@Oli:是的,这是效率的期望。如果你用汇编语言编写,你会使用特定的指令根据需要从ROM中提取数据。C语言使得在von Neumann架构下代码中的数据透明,但在Harvard架构下不是这样。我甚至不确定在一个如此受限的平台上手动管理C语言时,你想使用哪些高级语言。也许是Lisp。 - Steve Jessop
显示剩余6条评论

13

除其他答案之外,维基百科还说:

http://en.wikipedia.org/wiki/Function_pointer

尽管在 C 和 C++ 中,函数指针可以被实现为简单的地址,因此通常情况下 sizeof(Fx)==sizeof(void *),但是在 C++ 中,成员指针经常被实现为“fat 指针”,通常是一个简单函数指针的两到三倍大小,以处理虚继承问题。
函数指针是一种抽象。只要满足标准要求,任何事情都有可能。例如,如果您的程序中不到 256 个函数,则函数指针可以使用单个字节实现,其中值 0 表示 NULL,值 1 到 255 表示物理地址表中的索引。如果超过 255 个函数,则可以扩展为使用 2 个字节。

我不认为这有什么问题。尽管我会很惊讶如果真的有这样一种实现,但这确实是 C 语言合法实现的可能性。 - nategoose
9
不是我给你的投票点踩了,但C++成员指针相关的内容与问题无关,因为问题是关于C语言的。不过最后一段话说得很好,函数指针确实可以合法地作为某个表格的索引,再通过额外的间接层级来调用它们。 - Steve Jessop

7

有一个实际的例子,它涉及到不同的大小,这在以前是很常见的。在 MS-DOS 和早期的 Windows C 编程中,在“medium”内存模型中,您有16位数据指针和32位函数指针,而“compact”内存模型则相反。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接