以下对象存储在内存中的何处?

时间:2018-11-19 23:21:27

标签: c

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#define TEXT "Good luck on this test"
int main () {
  char* cPtr = (char*)malloc(sizeof(TEXT));
  strncpy(cPtr,TEXT,sizeof(TEXT));
  printf("%s\n",cPtr);
  free(cPtr);
  return(EXIT_SUCCESS);
}
  1. 变量cPtr的内存?
  2. cPtr指向的地址?
  3. malloc()的代码?
  4. malloc()调用以移动 brk指针的代码 正在运行此程序?

我认为是

  1. 堆栈
  2. 数据段
  3. 共享库内存

对吗?

3 个答案:

答案 0 :(得分:3)

唯一的正确答案是,无论编译器感觉如何。那可能不是您想要的答案,所以让我们研究一下合理的编译器可能会选择什么。

  1. cPtr可能根本不会存储在内存中。它适合一个寄存器(指针几乎总是适合CPU寄存器),并且您永远不会占用其地址,因此它可能位于一个或多个寄存器中。可以将其写入堆栈,然后回读以在strncpyprintf调用中保留其值,或者可以通过其他方式保留其值。 (请注意,编译器无需在free调用后保留其值,因为您再也不会使用它了。)
  2. malloc几乎总是返回堆指针,因为在那儿分配动态内存是最容易的,即使不是唯一可能的位置。因此缓冲区将在堆中。
  3. 编译器可以在这里选择。它可能引用来自某些共享库的链接的malloc,在这种情况下,它将驻留在共享库代码中,或者可能仅内联函数的全部或一部分,在这种情况下,其一部分或全部可能驻留在您的库中。程序的代码。
  4. 这假定为POSIX环境。在我所知道的每个此类环境中,这都是通过系统调用sbrk处理的。因此,这些代码将驻留在操作系统的内核代码中。

编辑:由于有人提到了静态字符串"Good luck on this test",所以我认为讨论一个静态字符串也是值得的。该字符串出现在三个上下文中:

  • 作为宏替换(通过#define),它在编译之前由预处理器处理,因此在最终输出中根本没有任何作用;
  • 作为strncpy函数的自变量,在这种情况下,它与程序的可执行代码一起包含在只读数据中,或者包含在专门用于读取的单独部分中-仅数据;
  • 作为sizeof运算符的自变量。这是三个中最有趣的情况。从技术上讲,它应该等效于上一个;但是,许多编译器可以静态计算常量字符串的大小(毕竟非常简单),因此他们可以将sizeof(TEXT)替换为普通的23并避免完全发出字符串(对于这种情况) )。

答案 1 :(得分:3)

实际上这是一个主要问题,因为它假定所有内容都将保存在内存中。

局部变量以及没有名称的临时值仅在必要时放在堆栈中。 。有不同的原因可能需要这样做,例如:

  • 通过尽可能最低的优化级别进行编译,使编译器变得愚蠢或行事愚蠢。
  • 目标计算机的体系结构很奇怪,没有(很少)寄存器(很少)。
  • 有太多的局部变量和临时值同时存在,以至于无法将它们全部放入程序中的寄存器中,因此其中一些会“溢出”。溢出并非确切地是变量的属性,而是特定的有效范围。从某种意义上说,一个变量因此可以移动(如果它具有多个关联的有效范围,并且它们的分配方式不同),甚至可以同时在多个位置(取决于您对临时副本的计数方式,或者取决于涉及到循环展开的明确位置)。
  • 以这样一种方式来获取和使用局部变量的地址:编译器无法证明该变量不需要在内存中(可能会引起有效范围分割,因此该变量实际上仅在内存中临时存在。) / li>

上述情况很可能都不适用(最后一项绝对不适用,地址也没有使用),因此我们应该期望cPtr将整个生命周期都花在寄存器中。

在针对{64的x {3}}上进行测试,我们可能会得到如下代码:

main:                                   # @main
    push    rbx
    mov     edi, 23
    call    malloc
    mov     rbx, rax

    ; at this point, rbx roughly corresponds to cPtr
    ; it's also still in rax but rax is overwritten by the next operation

    movabs  rax, 32777976875610985 ; btw this weird number is a piece of string
    mov     qword ptr [rbx + 15], rax
    movups  xmm0, xmmword ptr [rip + .L.str]
    movups  xmmword ptr [rbx], xmm0

    ; rbx (cPtr) is copied to rdi in order to give it to puts as argument

    mov     rdi, rbx
    call    puts
    mov     rdi, rbx
    call    free
    xor     eax, eax
    pop     rbx
    ret
.L.str:
    .asciz  "Good luck on this test"

以GCC为目标的MIPS或ARM或PowerPC显示了类似的模式cPtr不在堆栈中,而是在一个寄存器(或多个寄存器,取决于您的计数方式)中,尽管代码看起来很漂亮不同。

上面代码的一个有趣的细节是,虽然整个字符串确实出现在数据段(rodata)中,但它的一部分也作为该movabs的直接操作数出现在代码段中。

答案 2 :(得分:-1)

  1. 堆栈
  2. 数据
  3. 共享库

这就是我要回答这个问题的方式。

  1. char * cPtr = NULL;在堆栈上声明一个char *并将其分配为指向NULL。在您的实例中,malloc()将其分配为指向堆内存,但是cPtr变量本身在堆栈上。

  2. Malloc分配堆内存。

  3. TEXT字符串位于数据段中,并且sizeof(“ TEXT STRING”)将作为对数据段中地址的operator处理。我认为您的问题意味着“ malloc参数的代码”

  4. 您的代码未定义函数malloc,因此由于您#include的其中一个库,它必须执行的所有操作。

我可能对其中一个或多个答案有误,但这是我的理解。如果有人可以告诉我我错了哪里,请改正答案。