我的变量存储在内存中的什么位置?


155

通过考虑将内存分为四个部分:数据,堆,堆栈和代码,全局变量,静态变量,常量数据类型,局部变量(在函数中定义和声明),变量(在主函数中),指针在哪里,并动态分配空间(使用malloc和calloc)存储在内存中?

我认为它们的分配如下:

  • 全局变量------->数据
  • 静态变量------->数据
  • 常量数据类型----->代码
  • 局部变量(在函数中声明和定义)-------->堆栈
  • 在主函数----->堆中声明和定义的变量
  • 指针(例如char *arrint *arr)------->堆
  • 动态分配的空间(使用malloc和calloc)-------->堆栈

我仅从C角度指这些变量。

如果我错了,请纠正我,因为我是C新手。


4
类型不存储在内存中。

5
main只是另一个功能 除非malloc与其他地方一样,否则变量会进入堆栈。
simonc

4
指针(通常)存储在堆栈中。他们指向的内存(通常通过malloc / calloc分配)通常在堆上。
2013年

3
动态分配的空间(使用malloc,calloc)-------->堆
一名机组人员

3
在主函数中声明和定义的变量----->堆栈
一名机组人员

Answers:


215

您有一些这样的权利,但是谁写的问题都至少在一个问题上欺骗了您:

  • 全局变量------->数据(正确)
  • 静态变量------->数据(正确)
  • 常量数据类型----->代码和/或数据。考虑将常量常量存储在数据段中并将其引用嵌入到代码中的情况下的字符串文字
  • 局部变量(在函数中声明和定义)-------->堆栈(正确)
  • main函数-----> 堆中声明和定义的变量也会堆栈(老师试图欺骗您)
  • 指针(ex:char *arrint *arr)-------> 数据或堆栈,具体取决于上下文。C使您可以声明全局或static指针,在这种情况下,指针本身将最终出现在数据段中。
  • 动态分配的空间(使用malloccallocrealloc)--------> 的堆

值得一提的是,“堆栈”被正式称为“自动存储类”。


6
还值得一提的是,正式称呼堆根本不算什么。分配的内存来自某个地方,标准中没有“那个地方”的名称。
史蒂夫·杰索普

6
在某些系统(即Linux和* BSD)上,也alloca有类似于的工作方式malloc,但是会进行堆栈分配。
Andreas Grapentin

方法内部声明的const变量在哪里?
Mahori 2014年

@Ravi其余常量都放在同一位置(上面的第3点)。
dasblinkenlight 2014年

我正在使用GCC 4.8.1,它似乎不将const变量存储在DATA段中的main本地。以下是3种此类程序的代码和内存映射:代码1:int main(void){//字符a [10] =“ HELLO”; // 1 // const char a [10] =“ HELLO”; // 2返回0; }上面的内存映射:文本数据bss dec十六进制文件名7264 1688 1040 9992 2708 a.exe内存映射2:文本数据bss dec十六进制文件名7280 1688 1040 10008 2718 a.exe内存映射3:文本数据bss dec十六进制文件名7280 1688 1040 10008 2718 a.exe
Mahori 2014年

123

对于那些可能对了解这些内存段感兴趣的未来访问者,我在C中写了有关5个内存段的要点:

一些注意:

  1. 每当执行C程序时,都会在RAM中分配一些内存以执行程序。该内存用于存储频繁执行的代码(二进制数据),程序变量等。以下内存段对此进行了讨论:
  2. 通常有三种类型的变量:
    • 局部变量(在C中也称为自动变量)
    • 全局变量
    • 静态变量
    • 您可以具有全局静态变量或局部静态变量,但以上三个是父类型。

C中的5个内存段:

1.代码段

  • 代码段,也称为文本段,是包含频繁执行的代码的内存区域。
  • 该代码段通常是只读的,以避免被诸如缓冲区溢出之类的编程错误所覆盖的风险。
  • 该代码段不包含程序变量,例如局部变量(在C中也称为自动变量),全局变量等。
  • 基于C的实现,代码段也可以包含只读字符串文字。例如,当您执行此操作时printf("Hello, world"),将在代码/文本段中创建字符串“ Hello,world”。您可以size在Linux OS中使用命令对此进行验证。
  • 进一步阅读

数据段

数据段分为以下两个部分,通常位于堆区以下或在堆栈中的某些实现中,但数据段永远不在堆区和堆栈区之间。

2.未初始化的数据段

  • 此段也称为bss
  • 这是内存的一部分,其中包含:
    1. 未初始化的全局变量 (包括指针变量)
    2. 未初始化的常量全局变量
    3. 未初始化的局部静态变量
  • 任何未初始化的全局或静态局部变量将存储在未初始化的数据段中
  • 例如:全局变量int globalVar;或静态局部变量static int localStatic;将存储在未初始化的数据段中。
  • 如果您声明一个全局变量并将其初始化为0NULL那么它仍将进入未初始化的数据段或bss。
  • 进一步阅读

3.初始化的数据段

  • 此细分存储:
    1. 初始化的全局变量 (包括指针变量)
    2. 初始化的常量全局变量
    3. 初始化的局部静态变量
  • 例如:全局变量int globalVar = 1;或静态局部变量static int localStatic = 1;将存储在初始化的数据段中。
  • 该段可以进一步分为初始化的只读区域和初始化的读写区域初始化的常量全局变量将进入初始化的只读区域,而其值可以在运行时修改的变量将进入初始化的读写区域
  • 该段的大小由程序源代码中的值的大小确定,并且在运行时不会更改
  • 进一步阅读

4.堆栈段

  • 堆栈段用于存储在函数内部创建的变量(函数可以是主函数或用户定义的函数),如
    1. 函数的局部变量(包括指针变量)
    2. 传递给函数的参数
    3. 退货地址
  • 函数执行完成后,将立即删除存储在堆栈中的变量。
  • 进一步阅读

5.堆段

  • 该段是为了支持动态内存分配。如果程序员想要分配一些内存动态然后在C中,它是使用完成malloccallocrealloc方法。
  • 例如,当int* prt = malloc(sizeof(int) * 2)在堆中分配八个字节时,该位置的内存地址将返回并存储在ptr变量中。该ptr变量将在任取决于它被声明/使用的方式堆叠或数据段。
  • 进一步阅读

不应在3中初始化而不是未初始化。
Suraj Jain

关于“存储在未初始化的数据段中”(多个实例):您是说“存储在未初始化的数据段中”吗?
Peter Mortensen

@PeterMortensen我的意思是两件事。“任何未初始化的全局或静态局部变量都将存储在未初始化的数据段中”
hagrawal

我们如何在C中具有全局静态变量?

在“一些注意”下,我发现这一点“您可以具有全局静态变量或局部静态变量,但以上三个是父类型。” 其中u称为“全局静态”。我的观点是静态变量不能是全局的。即,如果任何变量必须是全局变量,则在程序执行完成之前应该可以访问它。如果我错了,请解释并提供帮助。

11

更正了错误的句子

constant data types ----->  code //wrong

局部常量----->堆栈

初始化的全局常量变量----->数据段

未初始化的全局常量变量-----> bss

variables declared and defined in main function  ----->  heap //wrong

在主函数----->堆栈中声明和定义的变量

pointers(ex:char *arr,int *arr) ------->  heap //wrong

dynamically allocated space(using malloc,calloc) --------> stack //wrong

指针(ex:char * arr,int * arr)------->该指针变量的大小将在堆栈中。

考虑到您正在动态分配n个字节的内存(使用malloccalloc),然后使指针变量指向它。现在,n内存字节在堆中,并且指针变量要求4个字节(如果64位计算机为8字节),则该字节将在堆栈中以存储n内存块字节的起始指针。

注意:指针变量可以指向任何段的内存。

int x = 10;
void func()
{
int a = 0;
int *p = &a: //Now its pointing the memory of stack
int *p2 = &x; //Now its pointing the memory of data segment
chat *name = "ashok" //Now its pointing the constant string literal 
                     //which is actually present in text segment.
char *name2 = malloc(10); //Now its pointing memory in heap
...
}

动态分配的空间(使用malloc,calloc)-------->堆


指针可以在堆栈中,也可以在堆中(特别是:指向指针的指针)
Argentage 2013年

@airza:现在更新。恰好我只是在更新那个细节:)
rashok

在下面的内存映射中,您能否指出堆栈和堆在哪里?我不确定这是否正确,因为堆栈和内存可能仅在运行时适用。内存映射:“文本数据bss dec十六进制文件名7280 1688 1040 10008 2718 a.exe”
Mahori 2014年

7

流行的桌面体系结构将进程的虚拟内存划分为以下几个部分

  • 文本段:包含可执行代码。指令指针采用此范围内的值。

  • 数据段:包含全局变量(即具有静态链接的对象)。细分为只读数据(例如字符串常量)和未初始化的数据(“ BSS”)。

  • 堆栈段:包含程序的动态内存,即所有线程的免费存储(“堆”)和本地堆栈帧。传统上,C堆栈和C堆曾经从相反的两端扩展到堆栈段,但是我认为实践已被放弃,因为它太不安全了。

AC程序通常将具有静态存储持续时间的对象放入数据段,将免费存储中的动态分配对象放入它所在的线程的调用堆栈中,并将自动对象放入该对象中。

在其他平台(例如旧的x86实模式)或嵌入式设备上,情况显然可能完全不同。


“我相信实践已经被放弃,因为它太不安全了”,并且无法实现线程,因为那样的话每个程序需要一个以上的堆栈,并且它们不能全部结束:-)
Steve Jessop

@SteveJessop:是的,我也在想。但是线程已经存在很长时间了-我不知道所有线程堆栈是否也向后增长,或者它们是否像堆一样长大……无论如何,如今一切都朝着同一方向发展,并且有保护措施页面。
Kerrek SB 2013年

6

我仅从C角度指这些变量。

C语言的角度来看,最重要的是范围,范围,链接和访问。具体如何将项目映射到不同的内存段取决于具体的实现方式,并且会有所不同。语言标准不谈论内存段可言。大多数现代建筑的行为方式大致相同。块范围变量和函数参数将从堆栈中分配,文件范围和静态变量将从数据或代码段中分配,动态内存将从堆中分配,一些常量数据将存储在只读段中等


3

关于存储,需要牢记的一件事是-if -if规则。不需要编译器将变量放在特定的位置-而是可以将其放置在任意位置,只要编译后的程序的行为就像是根据抽象C机的规则在抽象C机中运行一样即可。这适用于所有存储期限。例如:

  • 可以完全消除所有无法访问的变量-它在任何地方都没有存储。示例 -看看42生成的汇编代码中有什么,但没有的迹象404
  • 具有自动存储持续时间且未使用其地址的变量根本不需要存储在内存中。一个示例是循环变量。
  • 一个已经存在const或实际上const不需要存储在内存中的变量。示例 -编译器可以证明foo是有效的,const并将其内联到代码中。bar具有外部链接,并且编译器无法证明不会在当前模块外部对其进行更改,因此未进行内联。
  • 分配的对象malloc不必驻留在从堆分配的内存中!例子 -注意代码是如何没有调用的,malloc并且值42也不存储在内存中,而是保存在寄存器中!
  • 因此,已分配malloc并引用的对象会丢失,而无需取消分配该对象而free 无需泄漏内存...
  • 分配的对象malloc不必位于Unixen上程序break()下面sbrk(0)堆中...

1

指针(例如:char * arr,int * arr)------->堆

不,它们可以位于堆栈中或位于数据段中。他们可以指向任何地方。


关于main和动态分配的变量的语句也是错误的
simonc

不仅在堆栈或数据段上。想想一个指向指针数组的指针。在这种情况下,数组中的指针存储在堆中。
Sebi2020

1
  • 变量/自动变量--->堆栈部分
  • 动态分配的变量--->堆部分
  • 初始化的全局变量->数据部分
  • 未初始化的全局变量->数据部分(BSS)
  • 静态变量->数据部分
  • 字符串常量->文本部分/代码部分
  • 功能->文本部分/代码部分
  • 文字代码->文字部分/代码部分
  • 寄存器-> CPU寄存器
  • 命令行输入->环境/命令行部分
  • 环境变量->环境/命令行部分

什么是环境/命令行部分?它们是否存在于Linux中?
郝浩歌

-1

带有反汇编分析的Linux最小可运行示例

由于这是标准未指定的实现细节,因此让我们看一下编译器在特定实现上的工作。

在此答案中,我将链接到进行分析的特定答案,或者在此处直接提供分析,并在此处总结所有结果。

所有这些版本都在Ubuntu / GCC的各种版本中,结果在各个版本中可能都相当稳定,但是如果发现任何变化,我们可以指定更精确的版本。

函数内部的局部变量

是它main还是任何其他功能:

void f(void) {
    int my_local_var;
}

如以下所示:<值优化输出>在gdb中是什么意思?

  • -O0:堆栈
  • -O3:如果它们不溢出,则注册,否则堆叠

有关为什么存在堆栈的动机,请参见:x86汇编器中的寄存器上使用的push / pop指令的功能是什么?

全局变量和static函数变量

/* BSS */
int my_global_implicit;
int my_global_implicit_explicit_0 = 0;

/* DATA */
int my_global_implicit_explicit_1 = 1;

void f(void) {
    /* BSS */
    static int my_static_local_var_implicit;
    static int my_static_local_var_explicit_0 = 0;

    /* DATA */
    static int my_static_local_var_explicit_1 = 1;
}
  • 如果已初始化0或未初始化(并因此隐式初始化为0):.bss部分,另请参见:为什么需要.bss段?
  • 否则:.data部分

char *char c[]

如图所示:C和C ++中的静态变量存储在哪里?

void f(void) {
    /* RODATA / TEXT */
    char *a = "abc";

    /* Stack. */
    char b[] = "abc";
    char c[] = {'a', 'b', 'c', '\0'};
}

TODO还会将非常大的字符串文字放入堆栈吗?还是.data?还是编译失败?

函数参数

void f(int i, int j);

必须遵循相关的调用约定,例如:用于X86的https://en.wikipedia.org/wiki/X86_calling_conventions,该规范为每个变量指定特定的寄存器或堆栈位置。

然后,如<值优化输出>在gdb中意味着什么所示-O0然后将所有内容都放入堆栈中,同时-O3尝试尽可能多地使用寄存器。

但是,如果函数内联,则将它们像普通本地人一样对待。

const

我相信这没有什么不同,因为您可以将其打字掉。

相反,如果编译器能够确定某些数据从未写入过,则理论上.rodata即使没有const 也可以将其放入。

TODO分析。

指针

它们是变量(包含地址,即数字),与其余所有变量相同:-)

分配

该问题对于而言并没有多大意义malloc,因为它malloc是一个函数,并且在:

int *i = malloc(sizeof(int));

*i 是包含地址的变量,因此属于上述情况。

至于malloc在内部的工作方式,当您调用它时,Linux内核将某些地址标记为可写在其内部数据结构上,并且当程序最初对其进行触摸时,就会发生错误,内核会启用页表,从而允许访问页发生而没有segfaul:x86分页如何工作?

但是请注意,这基本上就是exec您尝试运行可执行文件时syscall 在后台执行的操作:它标记要加载的页面,并在其中写入程序,另请参见:内核如何使可执行二进制文件在以下环境下运行Linux的?除此之外,exec对加载到的位置有一些额外的限制(例如,代码不可重定位)。

确切的syscall mallocmmap在2020年的现代实现中使用的,并且在过去brk使用过:malloc()使用brk()还是mmap()吗?

动态库

基本上可以mmap进入内存:https : //unix.stackexchange.com/questions/226524/what-system-call-is-used-to-load-libraries-in-linux/462710#462710

envinroment变量mainargv

在初始堆栈上方:https: //unix.stackexchange.com/questions/75939/where-is-the-environment-string-actual-stored TODO为什么不在.data中?

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.