指针变量的地址是如何分配的?他们遵循任何模式吗?
How are the addresses given to pointer variables? Do they follow any pattern?
#include<stdlib.h>
int main()
{
char ch, *p1, **p2, ***p3, ****p4;
ch='a';
p1=&ch;
printf("%c %d %c\n", ch, p1, *p1);
p2=&p1;
printf("%d %d %c\n", p2, *p2, **p2);
p3=&p2;
printf("%d %d %d %c\n", p3, *p3, **p3, ***p3);
p4=&p3;
printf("%d %d %d %d %c\n", p4, *p4, **p4, ***p4, ****p4);
}
输出如下:
a 298923415 a
298923416 298923415 a
298923424 298923416 298923415 a
298923432 298923424 298923416 298923415 a
为什么p1和p2分配的地址以1为增量,p3和p4分配的地址以8为增量?
寻址是否遵循任何模式,因为它们被分配到连续的内存位置?
发生什么取决于编译器做什么。语言本身并不能保证,但通常它们会在堆栈中彼此靠近放置。有一个很好的例子说明它如何在 x86-64 上工作(你的电脑可能有)here:
该示例使用以下代码:
long myfunc(long a, long b, long c, long d,
long e, long f, long g, long h)
{
long xx = a * b * c * d * e * f * g * h;
long yy = a + b + c + d + e + f + g + h;
long zz = utilfunc(xx, yy, xx % yy);
return zz + 20;
}
与您的问题相关的是 xx yy zz
部分。就像您的问题一样,这些是在堆栈上声明的变量。正如您在图片中看到的,它们并排放置。 xx
的地址最高,然后是yy
,然后是zz
。为了说明这一点,您可以执行 (&xx)[-1]
并且 可能 获得 yy
的值。请注意,这是 未定义的行为 并且仅在我们知道编译器如何处理它时才起作用,在任何不同的设置中它可能不起作用,可能导致错误并使程序崩溃(或更糟)。
此外,请考虑 this post 内存对齐,这可能会导致您的数据之间存在间隙,以使其 运行 更快。
在这种情况下,您的对象在内存中按顺序排列。他们不一定非得如此。编译器可以根据需要在内存中放置内容。
在你的情况下,你的记忆看起来是这样的:
298923415 +-----------+
| 'a' | ch
298923416 +-----------+
| |
| |
| |
| 298923415 | p1
| |
| |
| |
| |
298923124 +-----------+
| |
| |
| |
| 298923416 | p2
| |
| |
| |
| |
298923132 +-----------+
| |
| |
| |
| 298923424 | p3
| |
| |
| |
| |
298923140 +-----------+
| |
| |
| |
| 298923432 | p4
| |
| |
| |
| |
+-----------+
请记住,在大多数现代系统中,指向对象的指针类型只是整数,其作用是存储内存地址。在您的系统上(我猜 x86_64)指针的大小为 8 个字节。不过,这完全取决于系统。例如,一个指针在 32 位 x86 系统上是 4 个字节,还有许多其他平台具有奇异的指针大小。
char
的大小也定义为 1,因此第一个指针位于 ch
之后的一个字节。
您读取输出的方式有误。 ch
分配在地址 298923415。指针 p1
分配在下一个字节 298923416。从那里开始,每个指针按 8 字节的倍数分配,建议使用 64 位系统。
这只是意味着编译器分配 char
与指针不同。这是因为char
是1个字节,没有对齐要求,所以可能会放在未对齐的地址。
一般来说,编译器可以随意分配变量到它喜欢的地方,你没有任何保证。如果不考虑特定的系统,讨论堆栈内存布局是没有意义的。
将您的示例修改得更清晰,结果并不引人注目:
#include <stdio.h>
int main (void)
{
char ch = 'a';
char* p1 = &ch;
char** p2 = &p1;
char*** p3 = &p2;
char**** p4 = &p3;
printf("ch:\t%p\n", (void*)p1);
printf("p1:\t%p\n", (void*)p2);
printf("p2:\t%p\n", (void*)p3);
printf("p3:\t%p\n", (void*)p4);
printf("p4:\t%p\n", (void*)&p4);
}
输出(x64 Windows PC,十六进制地址):
ch: 000000000022FE4F
p1: 000000000022FE40
p2: 000000000022FE38
p3: 000000000022FE30
p4: 000000000022FE28
#include<stdlib.h>
int main()
{
char ch, *p1, **p2, ***p3, ****p4;
ch='a';
p1=&ch;
printf("%c %d %c\n", ch, p1, *p1);
p2=&p1;
printf("%d %d %c\n", p2, *p2, **p2);
p3=&p2;
printf("%d %d %d %c\n", p3, *p3, **p3, ***p3);
p4=&p3;
printf("%d %d %d %d %c\n", p4, *p4, **p4, ***p4, ****p4);
}
输出如下:
a 298923415 a
298923416 298923415 a
298923424 298923416 298923415 a
298923432 298923424 298923416 298923415 a
为什么p1和p2分配的地址以1为增量,p3和p4分配的地址以8为增量?
寻址是否遵循任何模式,因为它们被分配到连续的内存位置?
发生什么取决于编译器做什么。语言本身并不能保证,但通常它们会在堆栈中彼此靠近放置。有一个很好的例子说明它如何在 x86-64 上工作(你的电脑可能有)here:
该示例使用以下代码:
long myfunc(long a, long b, long c, long d,
long e, long f, long g, long h)
{
long xx = a * b * c * d * e * f * g * h;
long yy = a + b + c + d + e + f + g + h;
long zz = utilfunc(xx, yy, xx % yy);
return zz + 20;
}
与您的问题相关的是 xx yy zz
部分。就像您的问题一样,这些是在堆栈上声明的变量。正如您在图片中看到的,它们并排放置。 xx
的地址最高,然后是yy
,然后是zz
。为了说明这一点,您可以执行 (&xx)[-1]
并且 可能 获得 yy
的值。请注意,这是 未定义的行为 并且仅在我们知道编译器如何处理它时才起作用,在任何不同的设置中它可能不起作用,可能导致错误并使程序崩溃(或更糟)。
此外,请考虑 this post 内存对齐,这可能会导致您的数据之间存在间隙,以使其 运行 更快。
在这种情况下,您的对象在内存中按顺序排列。他们不一定非得如此。编译器可以根据需要在内存中放置内容。
在你的情况下,你的记忆看起来是这样的:
298923415 +-----------+
| 'a' | ch
298923416 +-----------+
| |
| |
| |
| 298923415 | p1
| |
| |
| |
| |
298923124 +-----------+
| |
| |
| |
| 298923416 | p2
| |
| |
| |
| |
298923132 +-----------+
| |
| |
| |
| 298923424 | p3
| |
| |
| |
| |
298923140 +-----------+
| |
| |
| |
| 298923432 | p4
| |
| |
| |
| |
+-----------+
请记住,在大多数现代系统中,指向对象的指针类型只是整数,其作用是存储内存地址。在您的系统上(我猜 x86_64)指针的大小为 8 个字节。不过,这完全取决于系统。例如,一个指针在 32 位 x86 系统上是 4 个字节,还有许多其他平台具有奇异的指针大小。
char
的大小也定义为 1,因此第一个指针位于 ch
之后的一个字节。
您读取输出的方式有误。 ch
分配在地址 298923415。指针 p1
分配在下一个字节 298923416。从那里开始,每个指针按 8 字节的倍数分配,建议使用 64 位系统。
这只是意味着编译器分配 char
与指针不同。这是因为char
是1个字节,没有对齐要求,所以可能会放在未对齐的地址。
一般来说,编译器可以随意分配变量到它喜欢的地方,你没有任何保证。如果不考虑特定的系统,讨论堆栈内存布局是没有意义的。
将您的示例修改得更清晰,结果并不引人注目:
#include <stdio.h>
int main (void)
{
char ch = 'a';
char* p1 = &ch;
char** p2 = &p1;
char*** p3 = &p2;
char**** p4 = &p3;
printf("ch:\t%p\n", (void*)p1);
printf("p1:\t%p\n", (void*)p2);
printf("p2:\t%p\n", (void*)p3);
printf("p3:\t%p\n", (void*)p4);
printf("p4:\t%p\n", (void*)&p4);
}
输出(x64 Windows PC,十六进制地址):
ch: 000000000022FE4F
p1: 000000000022FE40
p2: 000000000022FE38
p3: 000000000022FE30
p4: 000000000022FE28