【问题标题】:sizeof Pointer differs for data type on same architecturesizeof 指针在同一架构上的数据类型不同
【发布时间】:2014-06-18 22:51:33
【问题描述】:

我浏览了一些帖子并注意到指针可以根据sizeof 的大小不同,具体取决于代码编译和运行的架构。对我来说似乎足够合理(即:32 位架构上的 4 字节指针,64 位上的 8 字节指针,完全有意义)。

令我吃惊的一点是,指针的大小可能会根据它指向的数据类型而有所不同。我会假设,在 32 位架构上,所有指针的大小都是 4 字节,但事实证明函数指针的大小可以不同(即:比我预期的要大)。为什么会这样,在 C 编程语言中?我发现一篇文章解释了 C++,以及 the program may have to cope with virtual functions 如何解释这一点,但这似乎不适用于纯 C。此外,似乎不再需要使用“远”和“近”指针,所以我没有看到那些进入等式的人。

那么,在 C 语言中,有什么理由、标准或文档描述了为什么在同一架构上并非所有指针的大小都相同?

谢谢!

【问题讨论】:

  • 请告诉我们您看到的 char * 指针和 (*fn)() 指针的平台(Linux?Windows?x86?amd64?)、编译器(MSVC?GCC?)和 sizeof()这种行为。
  • 除了@FoggyDay 的请求之外,请提供一些示例代码来演示此行为。
  • 再次欺骗。 stackoverflow.com/questions/3520059/…。这似乎出现了很多。
  • @SeanPerry 这个问题值得重复。而且这里的答案质量不同(更好+参考)。

标签: c pointers sizeof


【解决方案1】:

C 标准规定了要求的法律:

  • 所有数据指针都可以转换为void* 并返回而不会丢失信息。
  • 所有struct-指针具有相同的表示+对齐,因此可以相互转换。
  • 所有union-指针具有相同的表示+对齐,因此可以相互转换。
  • 所有字符指针和空指针都具有相同的表示+对齐方式。
  • 所有指向合格和不合格兼容类型的指针都应具有相同的表示形式+对齐方式。 (例如相同类型的无符号/有符号版本是兼容的)

  • 所有函数指针都具有相同的表示+对齐方式,可以转换为任何其他函数指针类型并再次转换回来。

不需要更多。
委员会通过检查所有当前的实现和机器并编写尽可能多的保证来获得这些保证。

在指针自然是字指针而不是字符指针的架构上,您会得到不同大小的数据指针。
在具有不同大小的代码/数据空间(许多微处理器)的架构上,或者需要额外信息来正确调用函数(如安腾,尽管它们通常将其隐藏在数据指针后面),你会得到不同大小的代码指针数据指针。

【讨论】:

  • 谢谢。这很有帮助。
  • 第二点看起来像结构 联合具有相同的表示。这仅适用于结构和联合。
  • @self.:您认为这是委员会的意图还是标准措辞的缺陷?
  • @self.: 是的,我刚看到那句话。但是,如果标准说明了它应该在那里做什么,或者委员会的措辞有误,那么引用标准并不能回答这个问题。
  • 它在两个不同的句子中清楚地表明结构是相同的,联合是相同的。他们之间没有任何联系。我不明白你在那里看到了什么错误?
【解决方案2】:

那么,在 C 语言中,什么理由、标准或文档描述了为什么在同一架构上并非所有指针的大小都相同?

C11:6.2.5 p(28):

指向void 的指针应具有与指向字符类型的指针相同的表示和对齐要求。类似地,指向兼容类型的合格或不合格版本的指针应具有相同的表示和对齐要求。所有指向结构类型的指针都应具有彼此相同的表示和对齐要求。所有指向union 类型的指针都应具有彼此相同的表示和对齐要求。 指向其他类型的指针不必具有相同的表示或对齐要求

6.3.2.3 指针 p(8):

指向一种类型的函数的指针可以转换为指向另一种类型的函数的指针,然后再返回;结果应与原始指针比较。 如果转换后的指针用于调用类型与指向的类型不兼容的函数,则行为未定义

这阐明了指向数据的指针和指向函数的指针大小不同。

【讨论】:

    【解决方案3】:

    补充一点:

    问:那么,是否可以肯定地说,虽然我在定义指针时不必显式使用 far/near 关键字,但编译器会自动“在后台”处理它?

    答:http://www.unix.com/programming/45002-far-pointer.html

    这是分段架构的历史时代错误,例如 8086.

    过去有 8080,这是 8 位 具有 16 位地址总线的处理器,因此是 16 位指针。

    8086 随之而来,为了支持某种程度的落后 兼容性它采用了分段架构,让使用 16 位、20 位或 32 位指针,具体取决于日期 星期。其中指针是 16 位段寄存器和 16 位接近偏移。这导致了微型、小型、中型、 具有近、远和巨大指针的大型和巨大内存模型。

    68000等其他架构没有采用这种方案,有 所谓的平面内存模型。

    在 80386 和真正的 32 位模式下,所有指针都是 32 位的,但是 具有讽刺意味的是,现在真的很接近指针但 32 位宽,操作 系统会向您隐藏这些片段。

    【讨论】:

      【解决方案4】:

      我在三个不同的平台上编译了这个; char * 指针在每种情况下都与函数指针相同:

      代码:

      #include <stdio.h>
      
      int main (int argc, char *argv[]) {
        char * cptr = NULL;
        void (*fnptr)() = NULL;
      
        printf ("sizeof cptr=%ld, sizeof fnptr=%ld\n",
          sizeof (cptr), sizeof (fnptr));
      
        return 0;
      }
      

      结果:

                           char ptr    fn ptr
                           --------    ------
      Win8/MSVS 2013       4           4
      Debian7/i686/GCC     4           4
      Centos/amd64/GCC     8           8
      

      【讨论】:

      • 很好,但这并不能回答问题。
      • 引用标准当然不能回答问题。反编译一个重现它的示例可能给出答案。不幸的是,我无法重现它。
      • @FoggyDay:前提是您使用的实现中没有错误,它/它们符合标准,并且调查的行为对它/它们来说是合同的。完全不存在不合理概括的危险,尤其是由于测试人数有限。
      • 两次 x86 在 32 位平面内存模式下,一次 x86 在 64 位模式下(硬件仅支持平面内存)。几乎没有测试三个平台所暗示的那么大的平台差异。
      【解决方案5】:

      一些架构支持多种地址空间。虽然标准中没有任何内容要求实现提供对底层平台支持的所有地址空间的访问,而且实际上标准没有提供关于如何提供此类支持的指导,但支持多个地址空间的能力可能使意识到他们的程序员编写的代码比其他方式可能更好。

      在某些平台上,一个地址空间将包含所有其他地址空间,但访问该地址空间中的内容将比访问已知位于其中特定部分的内容要慢(有时慢 2 倍或更多)。在其他平台上,不会有任何“主”地址空间,因此需要不同类型的指针来访问不同空间中的内容。

      我不同意多个地址空间的存在应该被视为遗物的说法。在许多 ARM 处理器上,一个程序有可能拥有高达 1K-4K(取决于确切的芯片)的全局变量,这些全局变量的访问速度是“正常”的两倍,而且代码更少全局变量。我不知道有任何 ARM 编译器会利用这一点,但 ARM 的编译器没有理由不能这样做。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2023-04-09
        • 1970-01-01
        • 1970-01-01
        • 2019-03-18
        • 1970-01-01
        • 2017-04-29
        • 2014-12-05
        • 2012-09-13
        相关资源
        最近更新 更多