C题:为啥char实际上占用了4个字节的内存?

Posted

技术标签:

【中文标题】C题:为啥char实际上占用了4个字节的内存?【英文标题】:C Question: why char actually occupies 4 bytes in memory?C题:为什么char实际上占用了4个字节的内存? 【发布时间】:2011-07-06 05:54:48 【问题描述】:

我写了一个小程序来检查 char 在我的内存中占用了多少字节,它显示 char 实际上在内存中占用了 4 个字节。我知道这主要是因为字对齐,并且看不到 char 只有 1 个字节的优势。为什么不使用 4 个字节作为 char?

int main(void)

  int a;
  char b;
  int c;
  a = 0;
  b = 'b';
  c = 1;
  printf("%p\n",&a);
  printf("%p\n",&b);
  printf("%p\n",&c);
  return 0;

输出: 0x7fff91a15c58 0x7fff91a15c5f 0x7fff91a15c54

更新: 我不相信 malloc 只会为 char 分配 1 个字节,即使 sizeof(char) 作为参数传递,因为 malloc 包含一个标头将确保标头是字对齐的。有cmets吗?

更新2: 如果你被要求在没有填充的情况下有效地使用内存,唯一的方法是创建一个特殊的内存分配器吗?或者是否可以禁用填充?

【问题讨论】:

你可以用预处理宏、shift + 和打包它们 @Gabriel,你能详细解释一下吗? 你是如何从内存地址中确定char的sizeof的? 丹尼尔问对了问题。您的问题基于错误的假设。 也许可以试试 char* myArray = malloc(sizeof(char)*4);我的数组[0] = 0; ……然后看那个?可能是您使用堆栈变量导致实验失败。 【参考方案1】:

我假设它与堆栈中的变量打包有关。我相信在您的示例中,它强制整数为 4 字节对齐。因此,在 char 变量之前(或之后)需要有 3 个字节的填充(取决于我想的编译器)。

【讨论】:

他如何从内存地址中判断变量由4个字节组成? 那部分我不确定。他的变量地址并不完全有意义。【参考方案2】:

这是由于对齐限制。字符大小仅为 1 字节,但整数与 4 字节的倍数对齐。字符后面也可以跟着可能有更宽松对齐约束的其他字符(或者说短字符)。在这些情况下,如果 char 的大小确实如您所建议的那样为 4 个字节,我们将消耗不必要的空间。

【讨论】:

【参考方案3】:

变量本身不占用 4 个字节的内存,它占用 1 个字节,然后是 3 个字节的填充,因为堆栈上的下一个变量是一个 int,因此必须是字对齐的。

像下面这样的情况,你会发现变量anotherChar的地址比b的地址大1个字节。然后在 int c 之前加上 2 个字节的填充

int main(void)

  int a;
  char b;
  char anotherChar;
  int c;
  a = 0;
  b = 'b';
  c = 1;
  printf("%p\n",&a);
  printf("%p\n",&b);
  printf("%p\n",&anotherChar);
  printf("%p\n",&c);
  return 0;

【讨论】:

【参考方案4】:

你有 int、char、int

请参阅“为什么要限制字节对齐?”下的图片。 http://www.eventhelix.com/realtimemantra/ByteAlignmentAndOrdering.htm

          Byte 0 Byte 1 Byte 2  Byte 3
0x1000               
0x1004  X0     X1     X2      X3
0x1008               
0x100C         Y0     Y1      Y2

如果它以 4 字节、1 字节和 4 字节的形式存储它们,则需要 2 个 cpu 周期来检索 int c 并进行一些位移才能使 c 的实际值正​​确对齐以供使用作为一个整数。

【讨论】:

【参考方案5】:

回答你问题的最后一部分:为什么不使用 4 个字节作为 char?

为什么不对char[1000000] 使用 400 万字节?

【讨论】:

【参考方案6】:

对齐

让我们看看打印 a、b 和 c 的地址的输出:

输出:0x7fff91a15c58 0x7fff91a15c5f 0x7fff91a15c54

注意 b 不在同一个 4 字节边界上? a 和 c 是相邻的吗?这是它在内存中的样子,每行占用 4 个字节,最右边的列是第 0 位:

| b | x | x | x | 0x5c5c
-----------------
| a | a | a | a | 0x5c58 
-----------------
| c | c | c | c | 0x5c54 

这是编译器优化空间和保持字对齐的方式。即使您的 b 地址是 0x5c5f,它实际上并不占用 4 个字节。如果您使用相同的代码并添加一个短 d,您会看到:

| b | x | d | d | 0x5c5c
-----------------
| a | a | a | a | 0x5c58 
-----------------
| c | c | c | c | 0x5c54 

其中d的地址是0x5c5c。短裤将与两个字节对齐,因此在 c 和 d 之间仍有一个字节未使用的内存。添加另一个字符,你会得到:

| b | e | d | d | 0x5c5c
-----------------
| a | a | a | a | 0x5c58 
-----------------
| c | c | c | c | 0x5c54 

这是我的代码和输出。请注意,我的地址会略有不同,但它是我们真正关心的地址中的最低有效数字:

int main(void)

  int a;
  char b;
  int c;
  short d;
  char e;
  a = 0;
  b = 'b';
  c = 1;
  printf("%p\n",&a);
  printf("%p\n",&b);
  printf("%p\n",&c);
  printf("%p\n",&d);
  printf("%p\n",&e);
  return 0;


$ ./a.out 
0xbfa0bde8
0xbfa0bdef
0xbfa0bde4
0xbfa0bdec
0xbfa0bdee

Malloc

malloc 的手册页说它“分配 size 字节并返回指向已分配内存的指针。”它还说它将“返回一个指向已分配内存的指针,该指针适合任何类型的变量”。根据我的测试,对 malloc(1) 的重复调用以“双字”增量返回地址,但我不会指望这一点。

注意事项

我的代码在 x86 32 位机器上运行。其他机器可能会略有不同,一些编译器可能会以不同的方式进行优化,但这些想法应该是正确的。

【讨论】:

以上是关于C题:为啥char实际上占用了4个字节的内存?的主要内容,如果未能解决你的问题,请参考以下文章

用指针作为参数传值是否更节省内存?(c/c++/golang)

C语言结构体占用字节数及存储与空间分配

编译器如何在 struct 中完成内存分配? [复制]

在C语言中,一个char数据在内存中所占字节数为,其数值范围为 ?

c指针类型的作用

为啥C语言中bool型变量占用一个字节