什么是;编译时分配的内存;真的吗? 在C语言和C++语言中,人们经常提到静态和动态内存分配。我理解这个概念,但短语“所有内存都是在编译时分配(保留的)”总是让我感到困惑
据我所知,编译将高级C/C++代码转换为机器语言,并输出一个可执行文件。如何在编译文件中“分配”内存?内存不是总是和所有虚拟内存管理的东西一起分配在RAM中吗 内存分配不是一个运行时概念吗 如果我在我的C/C++代码中使用1KB的静态分配变量,这会不会增加相同数量的可执行文件的大小 这是在“静态分配”标题下使用短语的页面之一什么是;编译时分配的内存;真的吗? 在C语言和C++语言中,人们经常提到静态和动态内存分配。我理解这个概念,但短语“所有内存都是在编译时分配(保留的)”总是让我感到困惑,c++,c,memory,memory-management,terminology,C++,C,Memory,Memory Management,Terminology,据我所知,编译将高级C/C++代码转换为机器语言,并输出一个可执行文件。如何在编译文件中“分配”内存?内存不是总是和所有虚拟内存管理的东西一起分配在RAM中吗 内存分配不是一个运行时概念吗 如果我在我的C/C++代码中使用1KB的静态分配变量,这会不会增加相同数量的可执行文件的大小 这是在“静态分配”标题下使用短语的页面之一 可执行文件描述了为静态变量分配的空间。此分配由系统在运行可执行文件时完成。因此,1kB静态变量不会增加1kB可执行文件的大小: static char[1024]; 当然
可执行文件描述了为静态变量分配的空间。此分配由系统在运行可执行文件时完成。因此,1kB静态变量不会增加1kB可执行文件的大小:
static char[1024];
当然,除非指定初始值设定项:
static char[1024] = { 1, 2, 3, 4, ... };
因此,除了“机器语言”(即CPU指令)之外,可执行文件还包含对所需内存布局的描述。编译时分配的内存仅仅意味着在运行时不会有进一步的分配——不会调用
malloc
、new
或其他动态分配方法。您将有一个固定的内存使用量,即使您并不总是需要所有的内存
内存分配不是一个运行时概念吗
内存在运行时之前未被使用,但在执行开始之前,其分配由系统处理
如果我在我的C/C++代码中使用1KB的静态分配变量,这会不会增加相同数量的可执行文件的大小
简单地声明static不会使可执行文件的大小增加超过几个字节。用非零的初始值声明它(以保持该初始值)。相反,链接器只是将这1KB添加到系统加载程序在执行之前为您创建的内存需求中。编译时分配的内存意味着编译器在编译时解析进程内存映射中的某些内容
例如,考虑一个全局数组:
int array[100];
编译器在编译时知道数组的大小和int
的大小,因此它在编译时知道数组的整个大小。默认情况下,全局变量还具有静态存储持续时间:它分配在进程内存空间(.data/.bss部分)的静态内存区域中。根据这些信息,编译器在编译期间决定数组将位于该静态内存区域的哪个地址
当然,内存地址是虚拟地址。程序假定它有自己的整个内存空间(例如从0x00000000到0xFFFFFF)。这就是为什么编译器可以进行类似“好的,数组将位于地址0x00A33211”这样的假设。在运行时,MMU和操作系统将地址转换为实际/硬件地址
值初始化静态存储的事情有点不同。例如:
int array[] = { 1 , 2 , 3 , 4 };
在我们的第一个示例中,编译器只决定数组的分配位置,并将该信息存储在可执行文件中。在值初始化的情况下,编译器还将数组的初始值注入可执行文件,并添加代码,告诉程序加载器在程序启动时分配数组后,数组应填充这些值 以下是编译器生成的程序集的两个示例(带有x86目标的GCC4.8.1): C++代码:
int a[4];
int b[] = { 1 , 2 , 3 , 4 };
int main()
{}
输出组件:
a:
.zero 16
b:
.long 1
.long 2
.long 3
.long 4
main:
pushq %rbp
movq %rsp, %rbp
movl $0, %eax
popq %rbp
ret
如您所见,这些值直接注入到程序集中。在数组a
中,编译器生成一个16字节的零初始化,因为标准规定静态存储内容默认应初始化为零:
8.5.9(初始值设定人)[注]:静态存储持续时间的每个对象在 在进行任何其他初始化之前启动程序。在某些方面 在其他情况下,将在以后进行其他初始化
我总是建议人们分解他们的代码,看看编译器对C++代码的真正作用。这适用于从存储类/持续时间(如此问题)到高级编译器优化。您可以指示编译器生成程序集,但是在Internet上有一些非常好的工具可以以友好的方式完成这项工作。我最喜欢的是。编译时分配的内存意味着当您加载程序时,将立即分配部分内存,并且此分配的大小和(相对)位置在编译时确定
char a[32];
char b;
char c;
这三个变量是“在编译时分配的”,这意味着编译器在编译时计算它们的大小(大小是固定的)。变量a
将是内存中的偏移量,比如说,指向地址0,b
将指向地址33,c
指向地址34(假设没有对齐优化)。因此,分配1Kb的静态数据不会增加代码的大小,因为它只会更改其中的偏移量。实际空间将在加载时分配
真正的内存分配总是发生在运行时,因为内核需要跟踪它并更新其内部数据结构(为每个进程、页面等分配了多少内存)。不同之处在于编译器已经知道您将要使用的每个数据的大小,并且在程序执行后立即分配这些数据
还要记住,我们谈论的是相对地址。变量所在的实际地址将不同。在加载时,内核将保留一些
const char *c_string = "Here goes a thousand chars...999";//implicit \0 at end
int big_arr[1000];
for (int i=0;i<1000;++i) big_arr[i] = some_computation_func(i);
int a;
const int b[6] = {1,2,3,4,5,6};
char c[200];
const int d = 23;
int e[4] = {1,2,3,4};
int f;
int c;
global _c
section .bss
_c: resb 4
int a , b , c ;
"POINTERS AND POINTER ARITHMETIC, WAY OF THE PROGRAMMING WORLD"