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)