我与朋友辩论说过,在C中,内存是在编译时分配的
我说它不可能是真的,因为只有当进程加载到内存时才能分配内存
从这开始我们开始谈论堆栈分配,在阅读之后,我知道它的OS实现,
但通常堆栈以默认大小开始,1 MB(顺序)可以说是保留虚拟地址,并且当调用该函数时(无论我们在该线程的堆栈中的哪个位置),
大小为X的字节块(该函数的所有局部变量?)是从保留地址提交(分配)的。
我想了解的是,当我们进入该功能时,操作系统如何知道分配的大小有多大(从保留提交)?
它是否在函数执行期间动态发生?或者编译器知道在每个函数之前计算这个函数需要什么尺寸?
答案 0 :(得分:4)
编译器可以并且确实(在编译时)计算函数在堆栈上声明的所有局部变量的大小,从而它知道(再次,在编译时)增加堆栈需要多少 - 输入函数时的指针(当函数返回时减小它)。这个计算出的堆栈指针增加量值将在编译时直接写入可执行代码,因此在运行时调用该函数时不需要重新计算它。
上述例外情况是您的C程序使用的是C99s variable-length-arrays (VLA) feature。正如名称所暗示的那样,变长数组是大小在运行时才知道的数组,因此编译器必须为包含一个或多个VLA的任何函数发出特殊代码,这样的数量就是在运行时计算增加堆栈指针。
请注意,将虚拟堆栈地址映射到物理RAM地址(并确保分配必要的RAM)的物理操作是在运行时完成的,由操作系统而不是编译器处理。特别是,如果进程试图访问当前未映射到任何物理地址的虚拟地址(在堆栈上或其他地方),则MMU将生成page fault。在执行页面错误处理程序例程时,将临时暂停进程的执行。页面错误处理程序将评估进程尝试访问的虚拟地址的合法性;如果虚拟地址是合法的,则页面错误处理程序会将其映射到物理RAM的适当页面,然后让该进程继续执行。如果虚拟地址不是一个进程被允许访问(或者如果尝试获取物理RAM页面失败,例如因为计算机的内存已满),则映射将失败并且操作系统将停止/崩溃过程。