【问题标题】:Understanding sizeof(char) in 32 bit C compilers了解 32 位 C 编译器中的 sizeof(char)
【发布时间】:2010-08-10 16:38:14
【问题描述】:

(sizeof) char 在 32 位 GCC 编译器中总是返回 1

但是由于32位编译器的基本块大小是4,那么在基本大小为4字节的情况下,char如何占用一个字节???

考虑以下几点:

struct st 
{
int a;
char c;
};

sizeof(st) 返回 8 与默认的 4 字节块大小一致(因为分配了 2 个块)

我永远无法理解为什么当分配一个大小为 4 的块时 sizeof(char) 返回为 1

有人能解释一下吗???

我会非常感谢任何解释它的回复!!!

编辑:“位”的错字已更改为“字节”。我向第一次编辑的人问对不起。我回滚了 EDIT,因为我没有注意到 U 所做的更改。 感谢所有指出必须改变它的人,特别是@Mike Burton 否决了这个问题,感谢@jalf 似乎对我对概念的理解得出了结论!

【问题讨论】:

  • 在编写可移植的 C 程序时,我学会了不要假设太多;甚至声明 the basic block size in 32 bit compiler is 4 bytes 也是一个可能不正确的假设,因为数据大小和地址大小不需要相同。 “基本块大小”取决于系统的内存访问架构,不必与数据寄存器大小相对应。顺便说一句,这不会影响您关于 C 结构大小的基本问题。
  • 我猜你的意思是字节,而不是位?
  • @James,我不认为这是重复的。另一个问题假设不知道填充或对齐。这个问题是关于为什么 sizeof 在某些情况下显然返回填充值,而不是其他情况。
  • @Shyam,在您的问题中,应该是:“但是由于 32 位编译器中的基本块大小是 4 个字节,那么当基本大小为 4 个字节时,char 如何占用单个字节?? ?", and, "sizeof(st) 返回 8 与默认块大小 4 字节一致(因为分配了 2 个块)" 一个字节中有 8 位,32 位中有 4 个字节。
  • @jalf - 你似乎误解了这个网站的性质。您认为编辑和社区编辑功能究竟有什么用?它不是为了好看。如果英语不好的人回滚了语言清理编辑,那也是错误的。对于大多数访问它的人来说,这个问题的含义是显而易见的。在第一次编辑后对 OP 来说应该是显而易见的。无需评论。

标签: c++ c sizeof


【解决方案1】:

sizeof(char) 始终为 1。始终。您所说的“块大小”只是机器的本机字大小 - 通常是导致最有效操作的大小。您的计算机仍然可以单独寻址每个字节 - 这就是 sizeof 运算符告诉您的内容。当您执行 sizeof(int) 时,它会返回 4 来告诉您 int 在您的机器上是 4 个字节。同样,您的结构是 8 个字节长。 sizeof 没有关于一个字节中有多少位的信息。

您的结构长度为 8 字节而不是 5 字节(如您所料)的原因是编译器正在向结构中添加 padding 以使所有内容都与本机字长很好地对齐,再次提高效率。大多数编译器为您提供了pack 结构的选项,可以使用#pragma 指令或其他编译器扩展,在这种情况下,您可以强制结构采用最小大小,而不管您机器的字长如何.

char 的大小为 1,因为这是您的计算机可以处理的最小访问大小 - 对于大多数机器来说是 8 位值。 sizeof 运算符为您提供所有其他数量的大小,单位为有多少 char 对象与您询问的大小相同。出于性能原因,编译器会将 填充(见下面的链接)添加到您的数据结构中,因此在实践中它比您从结构定义中想象的要大。

有一篇名为Data structure alignment 的维基百科文章有很好的解释和示例。

【讨论】:

  • 我将问题解读为:“我了解块大小,但为什么 sizeof(char) 返回小于块大小,而 sizeof(struct) 返回填充值。”
  • @Aaron,我认为我的第二段涵盖了这一点,对吧?你认为我需要补充一些说明吗?
  • 他问为什么sizeof(char) 不包括填充。答案是char没有填充,它可以分配在1个字节上,我们可以将它存储在malloc(1)分配的空间上。 st 不能存储在用 malloc(5) 分配的空间上,因为当复制 st 结构时,整个 8 个字节都被复制了。
  • @Aaron,这就是确切的问题!!! @Carl,我认为关于 Aaron 的问题,第二段需要进一步澄清。
  • @Shyam,我添加了另一段来扩展数据结构填充的解释。
【解决方案2】:

它是与填充对齐的结构。 c 使用 1 个字节,3 个字节未使用。更多here

【讨论】:

  • 遗憾的是,链接似乎已失效。我知道已经 11 年了,但如果你能在其他地方找到它,我想我不会是唯一感兴趣的人 :)
  • @A.Ocannaille 确实页面不见了。不要在答案中使用链接的宝贵经验。我替换为 MSDN 的实际链接。
  • 非常感谢您的版本和您的反应:-)
【解决方案3】:

展示结构对齐的示例代码:

struct st 
{
int a;
char c;
};

struct stb
{
int a;
char c;
char d;
char e;
char f;
};

struct stc
{
int a;
char c;
char d;
char e;
char f;
char g;
};

std::cout<<sizeof(st) << std::endl; //8
std::cout<<sizeof(stb)  << std::endl; //8
std::cout<<sizeof(stc)  << std::endl; //12

struct 的大小大于其各个组件的总和,因为 32 位编译器将其设置为可被 4 个字节整除。这些结果在不同的编译器上可能会有所不同,尤其是在 64 位编译器上时。

【讨论】:

    【解决方案4】:

    首先,sizeof 返回一个字节的数量,而不是位。 sizeof(char) == 1 告诉您char 的长度为八位(一个字节)。 C 中的所有基本数据类型都至少有一个字节长。

    您的结构返回的大小为 8。这是三项的总和:int 的大小、char 的大小(我们知道是 1)以及任何额外的大小编译器添加到结构中的填充。由于许多实现使用 4 字节的int,这意味着您的编译器正在向您的结构添加 3 字节的填充。这很可能是在char 之后添加的,以使结构的大小成为 4 的倍数(32 位 CPU 在 32 位块中访问数据的效率最高,32 位是四个字节)。

    编辑:仅仅因为块大小是四个字节并不意味着数据类型不能小于四个字节。当 CPU 将一个字节的char 加载到 32 位寄存器中时,该值将自动(由硬件)进行符号扩展以填充寄存器。 CPU 足够聪明,可以以 N 字节为增量(其中 N 是 2 的幂)处理数据,只要它不大于寄存器。将数据存储在磁盘或内存中时,没有理由将每个char 存储为四个字节。结构中的 char 恰好看起来像四个字节长,因为它后面添加了填充。如果您将结构更改为具有 两个 char 变量而不是一个,您应该看到结构的大小是相同的(您添加了一个额外的数据字节,编译器添加了一个字节的填充)。

    【讨论】:

    • 为了迂腐,sizeof(char) == 1 告诉你一个 char 的大小是一个 char。 C 标准不要求系统以字节为单位进行操作。
    • @Darron- 这是不正确的。 C 规范 (open-std.org/jtc1/sc22/wg14/www/docs/n1256.pdf) 的第 6.5.3.4 节指定“sizeof 运算符产生其操作数的大小(以字节为单位)”。
    • 该部分的第 3 段“当应用于具有 char、unsigned char 或 signed char 类型的操作数(或其限定版本)时,结果为 1”。第 3.6 节将字节定义为“数据存储的可寻址单元,大到足以容纳执行环境的基本字符集的任何成员”。这不是人们通常想到的字节。
    • @Darron:但它仍然是一个字节。它只是不需要 8 位宽。
    【解决方案5】:

    C 和 C++ 中的所有对象大小都是根据 bytes 而不是 bits 定义的。字节是计算机上最小的可寻址内存单元。位是单个二进制数字,01

    在大多数计算机上,一个字节是 8 位(因此一个字节可以存储从 0 到 256 的值),尽管存在其他字节大小的计算机。

    内存地址标识一个字节,即使在 32 位机器上也是如此。地址 N 和 N+1 指向两个后续字节。

    int,通常为 32 位,覆盖 4 个字节,这意味着存在 4 个不同的内存地址,每个地址都指向 int 的一部分。

    在 32 位机器中,所有 32 实际上意味着 CPU 被设计为有效地处理 32 位值,并且地址是 32 位长。这并不意味着内存只能以 32 位块的形式寻址。

    CPU 仍然可以寻址单个字节,这在处理 chars 时很有用。

    至于你的例子:

    struct st 
    {
    int a;
    char c;
    };
    

    sizeof(st) 返回 8 不是因为所有结构的大小都可以被 4 整除,而是因为 alignment。为使 CPU 有效地读取整数,它必须位于可被整数大小(4 字节)整除的地址上。所以int 可以放在地址 8、12 或 16 上,但不能放在地址 11 上。

    char 只要求其地址可以被char (1) 的大小整除,因此它可以放置在任何地址上。

    所以理论上,编译器可以给你的结构一个 5 个字节的大小......除非你创建了一个 st 对象数组。

    在数组中,每个对象都紧跟在前一个对象之后,没有填充。因此,如果数组中的第一个对象放置在可被 4 整除的地址处,则下一个对象将放置在高 5 个字节的地址处,该地址不能被 4 整除,因此第二个对象数组中的结构不会正确对齐。

    为了解决这个问题,编译器在结构体插入了填充,所以它的大小变成了它的对齐要求的倍数。

    不是因为不可能创建大小不是 4 倍数的对象,而是因为 st 结构的成员之一需要 4 字节对齐,因此每次编译器放置int 在内存中,它必须确保它被放置在一个可以被 4 整除的地址。

    如果你创建一个有两个chars的struct,它的大小不会是4。通常会得到2的大小,因为当它只包含chars时,对象可以放在任何地址,因此对齐不是问题。

    【讨论】:

      【解决方案6】:

      Sizeof 以字节为单位返回值。你说的是比特。 32 位架构是字对齐和字节引用的。体系结构如何存储字符无关紧要,但对于编译器,您必须一次引用 1 个字节的字符,即使它们用完不到 1 个字节。

      这就是 sizeof(char) 为 1 的原因。

      ints 是 32 位,因此 sizeof(int)= 4,double 是 64 位,因此 sizeof(double) = 8,等等。

      【讨论】:

      • int 在 32 位架构上通常为 32 位,但未定义为始终为 32 位。
      • 这就是我的意思,对不起。没有指定用于这些东西的存储,只是 short
      【解决方案7】:

      由于添加了优化填充,所以对象的大小为 1、2 或 n*4 字节(或类似的东西,谈论 x86)。这就是为什么向 5 字节对象添加填充而不向 1 字节添加填充。单个char 不必填充,它可以分配在1 个字节上,我们可以将它存储在malloc(1) 分配的空间中。 st 不能存储在用 malloc(5) 分配的空间上,因为当复制 st 结构时,整个 8 个字节都被复制了。

      【讨论】:

        【解决方案8】:

        它的工作原理与使用半张纸相同。您将一部分用于字符,另一部分用于其他内容。编译器将对您隐藏这一点,因为将 char 加载和存储到 32 位处理器寄存器取决于处理器。

        有些处理器有指令只加载和存储 32 位的一部分,而其他处理器则必须使用二进制运算来提取字符的值。

        寻址一个 char 是因为它是 AFAIR 根据定义最小的可寻址内存。在 32 位系统上,指向两个不同整数的指针至少相隔 4 个地址点,字符地址仅相隔 1 个。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2023-04-08
          • 2013-12-05
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2016-06-01
          • 1970-01-01
          • 2013-03-27
          相关资源
          最近更新 更多