【问题标题】:Static Memory allocation in CC中的静态内存分配
【发布时间】:2015-10-22 23:25:34
【问题描述】:
 #include <stdio.h>
 #include <stdlib.h>

 int foo;            /* a staticly allocated variable */

 int recur(int i) {      /* a recursive function */
     int j = i;            /* a stack allocated variable within a recursive function */
     printf("recur call (i)%d: stack@ %lx\n", i, (long unsigned int) &j); /* fix this so it print
 s the address of j */
     if (i > 0) {
         return recur(i-1);
     }
     return 0;
 }

 int stuff = 7;          /* a statically allocarted, pre-initialized variable */

 int main (int argc, char *argv[]) {
     int i;            /* a stack allocated variable */
     char *buf1 = malloc(100); /* dynamically allocate some stuff */
     char *buf2 = malloc(100); /* and some more stuff */
     char *buf3 = malloc(100); /* and some more stuff */
     printf("_main  @ %lx\n", (long unsigned int) &main); /* fix to print address of main */
     printf("_recur @ %lx\n", (long unsigned int) &recur); /* fix to print address of recur */
     printf("main call (i):stack@ %lx\n", (long unsigned int) &i); /* fix to get address of the s
 tack variable */
     printf("_static foo: %lx\n", (long unsigned int) &foo); /* fix to get address of the static v
 ariable */
     printf("_static stuff: %lx\n", (long unsigned int) &stuff); /* fix to get address of a stati
 c variable */
     printf("Heap: malloc 1: %lx\n", (long unsigned int) buf1);
     printf("Heap: malloc 2: %lx\n", (long unsigned int) buf2);
     printf("Heap: malloc 3: %lx\n", (long unsigned int) buf3);
     recur(3);
     return 0;
 }

这个程序的输出结果是:

_main  @ 4005c2
_recur @ 40057d
main call (i):stack@ 7fff26397694
_static foo: 601050
_static stuff: 601048
Heap: malloc 1: 1862010
Heap: malloc 2: 1862080
Heap: malloc 3: 18620f0
recur call (i)3: stack@ 7fff2639766c
recur call (i)2: stack@ 7fff2639763c
recur call (i)1: stack@ 7fff2639760c
recur call (i)0: stack@ 7fff263975dc

谁能解释下?

  • 为什么地址是不同的字节,例如。 main、recur 和 static_varibale 地址为 6*4=24 字节,堆为 7*4=28 字节,栈为 12*4=48 字节地址。
  • 虽然我为每个 malloc 分配 100 个字节,但 malloc 1 和 malloc 2 地址之间的差异是 0x70 = 112 个字节
  • 为什么 foo 和 stuff 的地址之间存在 0x601050 - 0x601048 = 8 的差异,尽管它们都是 int 并且 int 只需要 4 个字节?

【问题讨论】:

  • 对齐,也许???
  • 旁注:对于函数名,与静态分配的数组一样,不需要使用&amp; 来获取地址(例如main&amp;main 必须是相同的值) .
  • 另外,“recur”的堆栈帧恰好是 0x30=48 字节。这个值是固定的还是取决于功能的?如果它的函数依赖,那么 recur 函数只有两个声明为 'i' 和 'j' 的 int 变量,这使得 2*4= 8 个字节,那么为什么是 48 个?

标签: c static-memory-allocation


【解决方案1】:

首先要明确地址是进程内存空间的虚拟内存地址。也就是说,对于 32 位环境,进程内存空间的布局或多或少是这样的:

  0 +----------------------------------------+
    |             .text (code)               |
    +----------------------------------------+
    |  .data (initialized static variables)  |
    +----------------------------------------+
    | .bss (uninitialized static variables)  |
    +----------------------------------------+ <- Heap start, grows upwards
    |                  heap                  |
    +----------------------------------------+
    |                  stack                 |
3GB +----------------------------------------+ <- Stack start, grows downwards 
    |               kernel code              |
4GB +----------------------------------------+
  • 静态变量地址不短,所有地址都是32位地址,只是printf()截断前导零(看上图,静态变量地址低)。

  • malloc() 通常除了分配的缓冲区本身之外还放置簿记数据。不过,这取决于实现。

  • int 只占用 4 个字节”,这取决于您的系统。编译器还可以为堆栈帧对齐添加填充。

【讨论】:

    【解决方案2】:
    • 程序的每个部分(代码、数据、堆栈...)都位于不同的部分。这些部分由操作系统映射到内存区域,因此答案是您看到的差异取决于操作系统。在您的情况下,堆栈恰好映射到更高的区域,但所有地址仍然是 64 位(地址空间可以更小,例如 48 位)。
    • malloc() 保证返回一个对齐的指针。在 64 位环境中,它通常位于 16 字节边界上,因此您分配的 100 字节将被“四舍五入”为 112 (7*16) 以进行下一次分配。此外,malloc() 需要使用附加数据跟踪分配,因此已用空间将始终高于 malloc 的数量。
    • stufffoo 之间的主要区别是一个被初始化而另一个没有被初始化,因此stuff 将被放置在.data 部分,而foo 将被放置在@ 987654328@ 部分(零初始化部分)。这意味着这两个变量不太可能彼此相邻。在你的情况下,它们就在附近,因为没有别的东西。

    【讨论】:

    • 另外,“recur”的堆栈帧恰好是 0x30=48 字节。这个值是固定的还是取决于功能的?如果它的函数依赖,那么 recur 函数只有两个声明为 'i' 和 'j' 的 int 变量,这使得 2*4= 8 个字节,那么为什么是 48 个?
    • @UtkarshGupta 函数使用的堆栈取决于很多东西。您可以将局部变量存储在堆栈上,CPU 寄存器溢出(保存)在堆栈上,还可以为函数调用保留堆栈空间。此外,x64 调用约定要求在函数调用的 16 字节对齐上对齐堆栈。还有一个返回地址指针被压入堆栈,这是不可避免的 8 个额外字节。
    • 术语segment和section有什么区别?
    • @UtkarshGupta 通常segment是指内存分段,所以它与内存更相关。你可以说堆栈和代码在段上是不同的。请注意,在 64 位中不再使用内存分段,因此它是一个有点过时的术语。部分只是分隔程序不同部分的一种方式。您有 .text(代码)、.data(全局变量)、.bss(全局归零)、.rodata(常量,如字符串文字)之类的部分。您甚至可以定义自己的部分。没有真正的堆栈或堆部分,因为它们只是运行时的。
    • @UtkarshGupta 段指的是“内存分段”,这是早期 x86 处理器实现的一种技巧,能够使用 16 位地址寻址 1MB 内存空间。简而言之,一个逻辑内存地址分为两个 16 位地址:第一个是指一段内存(比如 0x0 到 0x0000FFFF),第二个是该段开始的偏移量(比如 0x000F)。所以像segment=0x1000, offset=0x000F这样的访问会导致线性地址0x1000000F。由于现代处理器都使用 32/64 位地址并且不再有这个问题,内核总是使用段 0 和 32/64 位偏移。
    猜你喜欢
    • 2015-07-23
    • 1970-01-01
    • 1970-01-01
    • 2016-02-07
    • 2015-02-27
    • 2012-11-29
    • 1970-01-01
    • 2012-01-13
    • 2016-02-03
    相关资源
    最近更新 更多