我试图看到对于给定的函数,内存的堆栈段上的内存分配将以连续的方式发生。所以,我写了下面的代码,我得到了以下输出。
对于int
分配,我看到内存地址按预期发送,但不是字符数组。在内存地址0xbff1599c
之后,我希望下一个地址为0xbff159a0
,而不是0xbff159a3
。此外,由于char
是1个字节而我使用的是4个字节,所以在0xbff159a3
之后我期待0xbff159a7
而不是0xbff159a8
如果删除char部分但是我无法获得具有字符数组的预期内存位置,则所有内存位置都按预期方式进行。
我的基本假设是在堆栈段上,内存将始终是连续的。我希望这没错。
#include <stdio.h>
int main(void)
{
int x = 10;
printf("Value of x is %d\n", x);
printf("Address of x is %p\n", &x);
printf("Dereferencing address of x gives %d\n", *(&x));
printf("\n");
int y = 20;
printf("Value of y is %d\n", y);
printf("Address of y is %p\n", &y);
printf("Dereferencing address of y gives %d\n", *(&y));
printf("\n");
char str[] = "abcd";
printf("Value of str is %s\n", str);
printf("Address of str is %p\n", &str);
printf("Dereferencing address of str gives %s\n", *(&str));
printf("\n");
int z = 30;
printf("Value of z is %d\n", z);
printf("Address of z is %p\n", &z);
printf("Dereferencing address of z gives %d\n", *(&z));
}
输出
Value of x is 10
Address of x is 0xbff159ac
Dereferencing address of x gives 10
Value of y is 20
Address of y is 0xbff159a8
Dereferencing address of y gives 20
Value of str is abcd
Address of str is 0xbff159a3
Dereferencing address of str gives abcd
Value of z is 30
Address of z is 0xbff1599c
Dereferencing address of z gives 30
答案 0 :(得分:2)
此外,由于char是1个字节而我使用4个字节,所以在0xbff159a3之后我期待0xbff159a7而不是0xbff159a8
char
占用1
个字节,但str
是字符串,并且您没有计算位于字符串末尾的'\0'
因此char str[]="abcd"
占用5
个字节。
答案 1 :(得分:1)
我认为这可能是因为地址与边界对齐(例如8字节边界)?。
分配始终与边界对齐并以块的形式分配 在某些操作系统中您可以使用结构进行检查。例如, 结构A. { char a; char b; int c; };
在UNIX / LINUX平台上,struct的大小不会是6个字节。
但它可能因操作系统而异。
类似的东西也适用于其他数据类型。 而且,字符串只指向a中分配的地址 堆如果使用malloc并且分配逻辑可能会有所不同 从OS到OS。以下是Linux框输出 对于同一个程序。
x的值是10 x的地址是0x7ffffa43a50c 取消引用x的地址为10
y的值是20 y的地址是0x7ffffa43a508 取消引用y的地址给出20
str的值是abcd str的地址是0x7ffffa43a500 取消引用str的地址给出了abcd
z的值是30 z的地址是0x7ffffa43a4fc 取消引用z的地址给出30
答案 2 :(得分:1)
来自@ameyCU和@Umamahesh的答案都很好,但没有一个是自给自足的,所以我正在写我的答案并添加更多信息,以便进一步访问的人可以获得最大的知识。
由于名为 Data structure alignment 的概念,我得到了这个结果。根据这一点,计算机将始终尝试以块的方式分配内存(无论是在堆段还是堆栈段或数据段中,在我的情况下是堆栈段),以便它可以读写快。
当现代计算机读取或写入存储器地址时,它将以字大小的块(例如,32位系统上的4字节块)或更大的块来执行此操作。数据对齐意味着将数据放入存储器地址等于字大小的某个倍数,这会因CPU处理内存的方式而提高系统性能。
在32位架构上,计算机字大小为4个字节,因此计算机将始终尝试分配具有4的倍数的地址的内存,以便它可以以4个字节的块快速读写。当字节数较少时,计算机会在开始或结束时填充一些空字节。
就我而言,假设我使用char str[] = "abc";
然后包含EOL字符'\0'
我需要4个字节,因此不会有填充。但是当我char str[] = "abcd";
然后包含EOL字符'\0'
时,我需要5个字节,现在计算机想要在4个块中分配,所以它将添加3个字节的填充(在开始或结束)因此完整的char数组将在内存中超过8个字节。
由于int
,long
内存要求已经是4的倍数,所以没有问题,使char
或short
变得棘手多个4 。这解释了我报告的内容 - “如果我删除了char部分但是我无法通过字符数组获得预期的内存位置,那么所有内存位置都会按预期进行。”
经验法则是指如果您的内存要求不是4的倍数(例如,1 {1}},short
数组大小为2)那么额外的填充将添加然后进行内存分配,以便计算机可以快速读写。
<小时/> 以下是this answer的摘录,它解释了数据结构的对齐。
假设你有结构。
char
如果没有对齐,它将在内存中布局(假设采用32位架构):
struct S {
short a;
int b;
char c, d;
};
问题在于,在某些CPU架构中,从内存加载4字节整数的指令仅适用于字边界。因此,您的程序必须使用单独的指令获取b的每一半。
但如果记忆的布局如下:
0 1 2 3 4 5 6 7
|a|a|b|b|b|b|c|d| bytes
| | | words
然后访问b变得简单明了。 (缺点是需要更多内存,因为填充字节。)