C中的指针实现细节

Wil*_*ord 16 c pointers sizeof virtual-memory

我想知道违反我在下面列出的假设的架构.此外,我想知道所有架构的假设是否都是错误的(也就是说,如果它们中的任何一个完全错误的话).

  1. sizeof(int*)== sizeof(char*)== sizeof(void*)== sizeof(func_ptr*)

  2. 无论指向哪种数据类型,给定体系结构的所有指针的内存中表示都是相同的.

  3. 指针的内存中表示与与体系结构相同的位长的整数相同.

  4. 指针数据类型的乘法和除法仅被编译器禁止.注意:是的,我知道这是荒谬的.我的意思是 - 是否有硬件支持禁止这种不正确的用法?

  5. 所有指针值都可以转换为单个整数.换句话说,哪些架构仍然使用分段和偏移?

  6. 递增指针相当于添加sizeof(the pointed data type)指针存储的内存地址.如果pint32*p+1则等于4字节后的内存地址p.

我最习惯在连续的虚拟内存空间中使用指针.对于这种用法,我通常可以将它们视为数字线上的地址.请参阅堆栈溢出问题指针比较.

jal*_*alf 10

我不能给你所有这些的具体例子,但我会尽我所能.

sizeof(int *) == sizeof(char *) == sizeof(void *) == sizeof(func_ptr *)
Run Code Online (Sandbox Code Playgroud)

我不知道任何系统,我知道这是假的,但考虑:

移动设备通常具有一定量的只读存储器,其中存储程序代码等.可以想象,只读值(常量变量)可以存储在只读存储器中.并且由于ROM地址空间可能小于普通RAM地址空间,因此指针大小也可能不同.同样,指向函数的指针可能具有不同的大小,因为它们可能指向加载程序的只读存储器,否则无法修改(因此您的数据不能存储在其中).

所以我不知道我观察到的任何平台上面都没有,但我可以想象系统可能就是这种情况.

无论指向哪种数据类型,给定体系结构的所有指针的内存中表示都是相同的.

想想成员指针和常规指针.它们没有相同的表示(或大小).成员指针由this指针和偏移量组成.

如上所述,可以想象一些CPU会将常量数据加载到单独的内存区域,该区域使用单独的指针格式.

指针的内存中表示与与体系结构相同的位长的整数相同.

取决于如何定义该位长度.:) int在许多64位平台上仍然是32位.但指针是64位.如前所述,具有分段内存模型的CPU将具有由一对数字组成的指针.同样,成员指针由一对数字组成.

指针数据类型的乘法和除法仅被编译器禁止.

最终,指针数据类型仅存在于编译器中.CPU使用的不是指针,而是整数和内存地址.因此,有没有其他地方在哪里指针类型这些操作可能会被禁止.您可能还要求CPU禁止连接C++字符串对象.它不能这样做,因为C++字符串类型仅存在于C++语言中,而不存在于生成的机器代码中.

但是,要回答您的意思,请查看Motorola 68000 CPU.我相信它们有整数和内存地址的独立寄存器.这意味着他们可以轻易地禁止这种无意义的操作.

所有指针值都可以转换为单个整数.

你在那里安全.无论内存空间布局,CPU架构还是其他任何东西,C和C++标准都保证始终可以实现这一点.具体来说,它们保证了实现定义的映射.换句话说,您始终可以将指针转换为整数,然后将该整数转换回原始指针.但是C/C++语言没有说明中间整数值应该是什么.这取决于单个编译器及其所针对的硬件.

增加指针等效于将sizeof(指向的数据类型)添加到指针存储的内存地址.

再次,这是有保证的.如果从概念上考虑,指针不指向一个地址,它指向一个对象,那么这是完全合理的.然后向指针添加一个显然会指向下一个对象.如果一个对象是20个字节长,那么递增指针将移动它20个字节,以便它移动到下一个对象.

如果一个指针只是一个线性地址空间中的内存地址,如果它基本上是一个整数,那么递增它会给地址加1 - 也就是说,它会移动到下一个字节.

最后,正如我在对您的问题的评论中提到的那样,请记住C++只是一种语言.它并不关心编译它的架构.在现代CPU中,许多这些限制似乎都很模糊.但是,如果你的目标是过去的CPU,那该怎么办?如果您的目标是下一个十年的CPU,该怎么办?你甚至不知道它们是如何工作的,所以你不能对它们做太多假设.如果您的目标是虚拟机怎么办?编译器已经存在,它为Flash生成字节码,准备从网站运行.如果要将C++编译为Python源代码,该怎么办?

遵守标准中规定的规则可确保您的代码适用于所有这些情况.


Meh*_*ari 8

我没有特定的现实世界的例子,但"权威"是C标准.如果标准不需要某些内容,您可以构建符合要求的实现,故意不遵守任何其他假设.这些假设中的一些在大多数情况下都是正确的,因为将指针实现为表示可以由处理器直接获取的存储器地址的整数是方便的,但这只是"方便"的结果而不能保持为一个普遍的事实.

  1. 标准不要求(见这个问题).例如,sizeof(int*)可能不相等size(double*).void*保证能够存储任何指针值.
  2. 标准不要求.根据定义,大小是表示的一部分.如果大小可以不同,则表示也可以不同.
  3. 不必要.事实上,"架构的位长"是一个模糊的陈述.什么是64位处理器,真的吗?是地址总线吗?寄存器的大小?数据总线?什么?
  4. "乘以"或"除"指针是没有意义的.这是编译器禁止的,但你当然可以乘以或除去底层表示(这对我来说没有意义),并导致未定义的行为.
  5. 也许我不理解你的观点,但数字计算机中的所有东西都只是某种二进制数.
  6. 是; 的种类.它保证指向一个sizeof(pointer_type)更远的位置.它不一定等同于数字的算术加法(即这里的逻辑概念更远.实际表示是特定于体系结构的)


Pet*_*sen 7

对于6:指针不一定是存储器地址.例如,请参阅Stack Overflow用户jalf的 " The Great Pointer Conspiracy " :

是的,我在上面的评论中使用了"地址"这个词.重要的是要意识到我的意思.我不是指"数据物理存储的内存地址",而只是一个抽象的"我们需要的任何东西来定位值.我的地址可能是任何东西,但一旦我们拥有它,我们总能找到并修改我."

和:

指针不是内存地址!我在上面提到了这一点,但让我们再说一遍.指针通常由编译器实现,只是作为内存地址,是的,但它们不一定是."


Chr*_*oph 6

有关C99标准指针的更多信息:

  • 6.2.5§27保证void*char*具有相同的表示,即它们可以互换地使用而不进行转换,即相同的地址由相同的位模式表示(对于其他指针类型不必如此)
  • 6.3.2.3§1规定任何指向不完整或对象类型的指针都可以转换为(和从中)void*并再次返回并仍然有效; 这不包括函数指针!
  • 6.3.2.3§6表示void*可以转换为(和)整数,7.18.1.4§1提供了适当的类型intptr_tuintptr_t; 问题:这些类型是可选的 - 标准明确提到不需要一个足够大的整数类型来实际保存指针的值!