【问题标题】:How does compiler understand the pointer type?编译器如何理解指针类型?
【发布时间】:2010-06-14 14:26:58
【问题描述】:

C++ 编译器如何理解指针类型?据我所知,指针的大小等于操作系统的 WORD(32 或 64)。那么它是否在 32 位(或 64 位)中存储了一些关于类型的信息?仅仅因为您不能在一种类型上拥有一个指针,然后为该指针分配另一个具有不同类型的指针。

【问题讨论】:

  • 我认为 OP 的意思是“一些信息”。已编辑。
  • 不要混淆编译时间和运行时间。在编译时,所有类型信息都是已知的并存储在编译器中。在运行时,大部分类型信息已被丢弃,并且绝对不会作为指针的一部分存储(生成的代码专门针对它知道在运行时重新雇用的类型而不参考该类型)
  • 对象呢?他们有类型信息运行时吗?
  • 有点。但它一般不会在运行时使用(例外当然是dynamic_cast)。编译器植入的代码通常特定于编译时推导的类型。有一些类型信息可应要求提供,但在代码中使用它是不好的做法,而且通常没有帮助。类型特定的功能是通过虚函数在语言级别实现的,并且在运行时通过方法间接访问此功能(即可能使用 v-tables 调用方法)。

标签: c++ pointers compiler-construction typeinfo


【解决方案1】:

编译器知道指针是什么类型,因为源代码说明了指针是什么类型:

int* ip;   // ip is a pointer to an int

float* fp; // fp is a pointer to a float

void* vp;  // vp is a pointer to some unknown type; need to cast it to a pointer
           // to an actual type in order to access the pointed-at object

【讨论】:

  • +1 当然,您可以将一种类型的指针分配给另一种类型的指针,您只需要强制转换即可。
  • 这是一个整数!现在它是一个浮动!现在它是一个整数!现在它是一个浮动!
  • @glowcoder:放松,伙计。你是两个指针! (来自魔兽的少年笑话)
  • @John:查了一下,发现你的笑话很有趣。 +1
【解决方案2】:

指针通常只是基于 x86 架构的内存地址(我不了解其他架构)。编译器在编译时使用不同的指针强制执行类型安全 - 例如,将指向字符的指针分配给指向 int 的指针是没有意义的,特别是因为指向的对象大小不同(所以你会如果您访问它们,则获取随机内存)。您可以显式覆盖它并使用reinterpret_cast<T> 或使用其他类型的强制转换(如static_cast<T>dynamic_cast<T>)将任何指针分配给任何其他指针(通常建议使用后两者,因为它们“更安全”,但每个都有自己的使用)。

所以在机器级别,内存地址只是一个内存地址,CPU 将尽职尽责地执行任何访问或调用。然而这很危险,因为您可能会混淆类型并且可能不知道它。编译时检查有助于避免这种情况,但通常没有任何关于在运行时存储在指针本身内的实际类型的任何信息。

使用迭代器(STL 提供的指针包装器)的一个优点是,许多实现都有许多额外的检查,可以在运行时启用:比如检查你使用的是正确的容器,当您比较它们时,它们是相同类型的迭代器,依此类推。这是在指针上使用迭代器的主要原因 - 但标准并不要求这样做,因此请检查您的实现。

【讨论】:

  • 使用 reinterpret_cast 并非不安全。它的含义是明确定义的,如果使用正确是完全安全的。如果使用不正确,它是不安全的,但对于任何使用不正确的东西都可以这样说。
  • 另请注意,在算法中比较或使用迭代器时,不需要检查它们是否来自同一个容器,因此不提供比指针更多的安全性(尽管 STL 的某些调试实现确实提供此功能有助于调试,但不应依赖此功能)
  • 从历史上看,“指针是内存地址”一直是正确的。然而,在现代操作系统上,你得到的最接近的是虚拟内存地址,甚至这只是一个实现细节。
  • @jurily:除非我们回到 60 年代,否则它始终是虚拟内存地址。但这对于任何应用程序代码(特别是硬件细节)都是无关紧要和透明的。
【解决方案3】:

正如James 所说,编译器“知道”指针是什么类型,因为你告诉了它。

不过,不那么轻率的是,在幕后发生的事情(在一个非常简化的解释中)是解析器在读取您的代码时,用它需要检查和执行规则的信息来注释它的每一个重要部分它所识别的语言。所以给出这个示例代码:

int*    ip;
// do some stuff
double* dp = ip;

编译器将在幕后执行类似的操作(再次以极其简化的形式):

嗯...有一个叫做“ip”的东西。我最好记下它是一个整数指针。好的,这是一个叫做“dp”的东西。我最好记下它是一个双指针。好的,现在,他想将ip分配给dp。但是……等一下! ip 是整数,dp 是双精度数。我做不到!

...编译器在屏幕上呕吐...

现实同时比上面的要简单得多(因为计算机根本不会思考任何事情——它都是非常机械的)而且要复杂得多(因为我已经在那个机械中掩盖了大约十亿个细节过程)。

【讨论】:

  • +1。如果我的回答听起来轻率,请道歉;那不是我的意图。
  • 我不认为它是恶意轻率,只是一个玩笑的回应。
【解决方案4】:

这是将源代码翻译成机器代码的句法分析的一部分。在最简单的示例中,您可以将其视为检查赋值两边的类型:

dest = source
// make sure that type of source == type of dest

【讨论】:

    【解决方案5】:

    指针只保存一个内存地址,仅此而已。

    意识到在汇编级别(所有 C / C++ 代码都被翻译成),实际上没有任何类型的概念,就像在高级语言中那样。 ASM 指令都对二进制值(字节、字、双字等)进行操作,而不太关心程序是否认为给定的一组位是 int、char 或其他东西。

    (当然除了我们有不同的指令来操作整数值和浮点值,但这并不是本次讨论的重点。)

    所以简短的回答是 type 完全是一个编译时构造,存储在编译器程序本身的 symbol table 中,将标识符映射到类型。在正在编译的程序中,类型不存在。

    【讨论】:

      【解决方案6】:

      大小等于操作系统的WORD

      CPU word你想说什么?最好不要使用word 来描述类型,因为该术语严重超载,并且根据读者的背景可能很容易被误解。

      仅仅因为你不能在一种类型上拥有一个指针,然后为该指针分配另一个类型不同的指针。

      视情况而定。继续阅读:Von Neumann architecture

      【讨论】:

        【解决方案7】:

        在现代架构上,没有关于内存中的单词是命令、数字、字符串的一部分还是指针的运行时信息。编译完成后,所有这些信息都将丢失(尽管它可能仍可用于调试符号)。如果一个词被编译代码用作指针,那么它一定是一个指针——CPU不会为你检查。

        用于在运行时维护这些信息的更老、更奇特的架构。看这里:http://wapedia.mobi/en/Burroughs_large_systems?p=2

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2022-01-03
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多