C中数据类型的值是如何存储在内存中的
How data type value is stored in memory in C
我实际上是 C 的新手,我在使用 gdb 调试 C 程序时尝试过这个。
这是我的程序
int main(){
int a = 4;
int b = 8;
}
然后我用Gdb调试了程序,单步调试了2行汇编代码,设置了a
和b
的值
mov DWORD PTR [rbp-0x4],0x4
mov DWORD PTR [rbp-0x8],0x8
rbp 的值为 0x7fffffffde70
由于int
数据类型有4个字节,栈从高值地址向低值地址增长,所以a
的地址必须是0x7fffffffde70 - 0x4
= 0x7fffffffde6c
和地址b
必须是 0x7fffffffde70 - 8
= 0x7fffffffde68
,它按预期返回了 4
和 8
。
但是当我在地址 0x7fffffffde6b 0x7fffffffde6a 0x7fffffffde69
打印值时,它打印 0x00000400 0x00040000 0x04000000
这看起来很奇怪,因为 a
有值 4
可以通过仅使用第一个字节和那些3 个地址应该包含 0x0
不是吗?
int
在您的 C 实现中是 32 位。使用十六进制显示所有用于表示值的32位,4用0000000416表示,8用0000000816.[=12表示=]
当一个整数值存储在内存中时,您的 C 实现首先存储它的最低值字节,然后是下一个最低值字节,然后是下一个最低值字节,依此类推。因此,如果 0000000416 存储在地址 7fffffffde6c16 和 0000000816 存储在 7fffffffde6816,则内存内容为:
Address
Contents
7fffffffde6816
0816
7fffffffde6916
0016
7fffffffde6a16
0016
7fffffffde6b16
0016
7fffffffde6c16
0416
7fffffffde6d16
0016
7fffffffde6e16
0016
7fffffffde6f16
0016
当您要求调试器从地址 7fffffffde6b16 打印一个 32 位整数时,它从该地址开始加载四个字节,得到 0016 、0416、0016、0016。由于首先存储最低值的字节,因此这些构成了整数 0000040016.
类似地,从 7fffffffde6a16 打印一个 32 位整数会产生字节 0016, 0016, 0416, 0016, 制作 0004000016, 打印一个 32-来自 7fffffffde6916 的位整数产生字节 0016, 0016, 00 16, 0416, 制作 0400000016.
请注意,当堆栈在您的系统中“向下”(向较低地址)增长时,这适用于函数调用堆栈帧的排序方式以及各个“push”和“pop”处理器指令的行为方式。它不控制编译器如何在堆栈帧中安排变量。编译器在框架内放置变量的顺序不需要与声明它们的顺序相匹配。尤其是当你开启优化,变量类型不同的时候,编译器可能会对变量进行不同的排序。
此外,虽然调试器可能能够从任何地址加载 32 位整数,但您的系统可能有对齐规则,要求 32 位整数在普通情况下位于四个字节的倍数处。不要指望常规程序而不是调试器能够从未对齐的地址加载 32 位整数而不产生中断执行的程序错误。
我实际上是 C 的新手,我在使用 gdb 调试 C 程序时尝试过这个。
这是我的程序
int main(){
int a = 4;
int b = 8;
}
然后我用Gdb调试了程序,单步调试了2行汇编代码,设置了a
和b
mov DWORD PTR [rbp-0x4],0x4
mov DWORD PTR [rbp-0x8],0x8
rbp 的值为 0x7fffffffde70
由于int
数据类型有4个字节,栈从高值地址向低值地址增长,所以a
的地址必须是0x7fffffffde70 - 0x4
= 0x7fffffffde6c
和地址b
必须是 0x7fffffffde70 - 8
= 0x7fffffffde68
,它按预期返回了 4
和 8
。
但是当我在地址 0x7fffffffde6b 0x7fffffffde6a 0x7fffffffde69
打印值时,它打印 0x00000400 0x00040000 0x04000000
这看起来很奇怪,因为 a
有值 4
可以通过仅使用第一个字节和那些3 个地址应该包含 0x0
不是吗?
int
在您的 C 实现中是 32 位。使用十六进制显示所有用于表示值的32位,4用0000000416表示,8用0000000816.[=12表示=]
当一个整数值存储在内存中时,您的 C 实现首先存储它的最低值字节,然后是下一个最低值字节,然后是下一个最低值字节,依此类推。因此,如果 0000000416 存储在地址 7fffffffde6c16 和 0000000816 存储在 7fffffffde6816,则内存内容为:
Address | Contents |
---|---|
7fffffffde6816 | 0816 |
7fffffffde6916 | 0016 |
7fffffffde6a16 | 0016 |
7fffffffde6b16 | 0016 |
7fffffffde6c16 | 0416 |
7fffffffde6d16 | 0016 |
7fffffffde6e16 | 0016 |
7fffffffde6f16 | 0016 |
当您要求调试器从地址 7fffffffde6b16 打印一个 32 位整数时,它从该地址开始加载四个字节,得到 0016 、0416、0016、0016。由于首先存储最低值的字节,因此这些构成了整数 0000040016.
类似地,从 7fffffffde6a16 打印一个 32 位整数会产生字节 0016, 0016, 0416, 0016, 制作 0004000016, 打印一个 32-来自 7fffffffde6916 的位整数产生字节 0016, 0016, 00 16, 0416, 制作 0400000016.
请注意,当堆栈在您的系统中“向下”(向较低地址)增长时,这适用于函数调用堆栈帧的排序方式以及各个“push”和“pop”处理器指令的行为方式。它不控制编译器如何在堆栈帧中安排变量。编译器在框架内放置变量的顺序不需要与声明它们的顺序相匹配。尤其是当你开启优化,变量类型不同的时候,编译器可能会对变量进行不同的排序。
此外,虽然调试器可能能够从任何地址加载 32 位整数,但您的系统可能有对齐规则,要求 32 位整数在普通情况下位于四个字节的倍数处。不要指望常规程序而不是调试器能够从未对齐的地址加载 32 位整数而不产生中断执行的程序错误。