简要总结
(我也会放在顶部):
(0) 将指针视为地址通常是一个很好的学习工具,并且通常是指向普通数据类型的指针的实际实现。
(1) 但是在许多,也许是大多数,编译器指向函数的指针不是地址,而是大于地址(通常是 2 倍,有时更多),或者实际上是指向内存中结构的指针,而不是包含函数地址之类的东西一个常量池。
(2) 指向数据成员的指针和指向方法的指针往往更加陌生。
(3) 带有 FAR 和 NEAR 指针问题的旧 x86 代码
(4) 几个例子,最著名的是 IBM AS/400,具有安全的“胖指针”。
我相信你能找到更多。
细节:
嗯!!!!到目前为止,许多答案都是相当典型的“程序员小问题”答案——但不是编译器小问题或硬件小问题。由于我假装自己是一个硬件小精灵,并且经常与编译器小精灵一起工作,所以让我投入两分钱:
在许多(可能是大多数)C 编译器上,指向数据类型的指针T
实际上是T
.
美好的。
但是,即使在许多这些编译器上,某些指针也不是地址。您可以通过查看来判断这一点sizeof(ThePointer)
。
例如,指向函数的指针有时比普通地址大很多。或者,它们可能涉及一定程度的间接性。 本文提供了一种描述,涉及英特尔安腾处理器,但我见过其他的。通常,要调用函数,您不仅必须知道函数代码的地址,还必须知道函数常量池的地址 - 一个使用单个加载指令加载常量的内存区域,而不是编译器必须生成几个加载立即数和移位和或指令中的一个 64 位常量。因此,您需要 2 个 64 位地址,而不是单个 64 位地址。一些 ABI(应用程序二进制接口)将其移动为 128 位,而另一些则使用间接级别,函数指针实际上是包含刚刚提到的 2 个实际地址的函数描述符的地址。哪个更好?取决于您的观点:性能、代码大小、和一些兼容性问题 - 通常代码假定指针可以转换为 long 或 long long,但也可能假定 long long 正好是 64 位。这样的代码可能不符合标准,但客户可能希望它工作。
我们中的许多人都对旧的 Intel x86 分段架构有着痛苦的回忆,包括 NEAR POINTERs 和 FAR POINTERS。谢天谢地,这些现在几乎绝迹了,所以只是一个简单的总结:在 16 位实模式下,实际的线性地址是
LinearAddress = SegmentRegister[SegNum].base << 4 + Offset
而在保护模式下,它可能是
LinearAddress = SegmentRegister[SegNum].base + offset
根据段中设置的限制检查结果地址。有些程序使用的不是真正标准的 C/C++ FAR 和 NEAR 指针声明,但许多程序只是说*T
--- 但是有编译器和链接器开关,因此,例如,代码指针可能是近指针,只是相对于其中的任何内容的 32 位偏移量CS(代码段)寄存器,而数据指针可能是 FAR 指针,为 48 位值指定 16 位段号和 32 位偏移量。现在,这两个量肯定都与地址有关,但是由于它们的大小不同,所以它们中的哪一个是地址?此外,除了与实际地址相关的内容之外,这些段还带有权限——只读、读写、可执行。
一个更有趣的例子,恕我直言,是(或者,也许是)IBM AS/400 系列。这台计算机是最早使用 C++ 实现操作系统的计算机之一。此机器上的指针通常是实际地址大小的 2 倍 - 例如,本演示文稿说是 128 位指针,但实际地址是 48-64 位,还有一些额外的信息,即所谓的能力,它提供诸如读、写之类的权限,以及防止缓冲区溢出的限制。是的:你可以用 C/C++ 兼容地做到这一点——如果这无处不在,中国解放军和斯拉夫黑手党就不会侵入这么多西方计算机系统。但从历史上看,大多数 C/C++ 编程都忽略了性能安全性。最有趣的是,AS400 系列允许操作系统创建安全指针,这些指针可以提供给非特权代码,但非特权代码无法伪造或篡改。同样,安全性,虽然符合标准,但很多草率的不符合标准的 C/C++ 代码将无法在这样一个安全的系统中工作。再次,有官方标准,
现在,我将离开我的安全肥皂盒,并提到其他一些(各种类型的)指针通常不是真正地址的方式:指向数据成员的指针、指向成员函数方法的指针,以及它们的静态版本大于普通地址。正如这篇文章 所说:
有很多方法可以解决这个问题[与单继承与多继承以及虚拟继承相关的问题]。下面是 Visual Studio 编译器决定处理它的方式:指向多重继承类的成员函数的指针实际上是一个结构。”他们接着说“转换函数指针可以改变它的大小!”。
正如您可能从我对(不)安全性的夸夸其谈中猜到的那样,我参与了 C/C++ 硬件/软件项目,在这些项目中,指针被视为一种能力而不是原始地址。
我可以继续,但我希望你明白。
简要总结
(我也会放在顶部):
(0) 将指针视为地址通常是一个很好的学习工具,并且通常是指向普通数据类型的指针的实际实现。
(1) 但在许多,也许是大多数,编译器指向函数的指针不是地址,而是大于地址(通常是 2X,有时更多),或者实际上是指向内存中结构的指针,而不是包含函数地址之类的东西一个常量池。
(2) 指向数据成员的指针和指向方法的指针往往更加陌生。
(3) 带有 FAR 和 NEAR 指针问题的旧 x86 代码
(4) 几个例子,最著名的是 IBM AS/400,具有安全的“胖指针”。
我相信你能找到更多。