在内存的哪里我的变量存储在C?

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

我认为他们的分配如下:

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

我仅从C的角度引用这些变量。

如果我错了,请指正,因为我是新来C的。

297261 次浏览

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

不,它们可以在堆栈上,也可以在数据段中。他们可以指向任何地方。

有些问题你答对了,但写问题的人至少在一个问题上骗了你:

  • 全局变量------->数据(正确)
  • 静态变量------->数据(正确)
  • 常量数据类型----->代码和/或数据。当一个常量本身存储在数据段中,并且对它的引用将嵌入到代码中时,请考虑字符串字面量
  • 局部变量(在函数中声明和定义)-------->堆栈(正确)
  • main函数中声明和定义的变量-----> 也可以堆叠(老师试图欺骗你)
  • 指针(例如:char *arrint *arr) -------> 数据或堆栈,取决于上下文。C允许您声明全局指针或static指针,在这种情况下,指针本身将在数据段中结束。
  • 动态分配空间(使用malloccallocrealloc) --------> 堆栈

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

纠正错误的句子

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

本地常量变量----->堆栈

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

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

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

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

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


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

指针(例如: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) -------->堆

一个流行的桌面架构将进程的虚拟内存划分为几个:

  • 文本段:包含可执行代码。指令指针接受这个范围内的值。

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

  • 堆栈段:包含程序的动态内存,即自由存储(“堆”)和所有线程的本地堆栈帧。传统上,C堆栈和C堆从相反的一端成长为堆栈段,但我相信这种做法已经被放弃了,因为它太不安全了。

C程序通常将具有静态存储持续时间的对象放入数据段中,在空闲存储区中动态分配对象,在其所在线程的调用堆栈中自动分配对象。

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

我仅从C的角度引用这些变量。

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

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

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

一些提示:

  1. 每当C程序执行时,就会在RAM中分配一些内存用于程序执行。这个内存用于存储频繁执行的代码(二进制数据)、程序变量等。下面的内存段讲的是同样的:
  2. 通常有三种类型的变量:
    • 局部变量(在C语言中也称为自动变量)
    • 全局变量
    • 静态变量
    • 你可以有全局静态变量或局部静态变量,但上面三个是父类型。
    • 李< / ul > < / >

5 C中的内存段:

1. 代码段

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

数据段

数据段被分为下面两部分,通常位于堆区域的下面,或者在某些实现中位于堆栈之上,但数据段从不位于堆和堆栈区域之间。

2. 未初始化的数据段

  • 这个段也被称为bss
  • 这部分内存包含:
    1. 未初始化的全局变量 (包括指针变量)
    2. 未初始化的常量全局变量
    3. 未初始化的局部静态变量
    4. 李< / ol > < / >
    5. 任何未初始化的全局或静态局部变量都将存储在未初始化的数据段中
    6. 例如:全局变量int globalVar;或静态局部变量static int localStatic;将存储在未初始化的数据段中。
    7. 如果你声明了一个全局变量并将其初始化为0NULL,那么它仍然会进入未初始化的数据段或bss。
    8. 进一步阅读 .

3.初始化的数据段

  • 该段存储:
    1. 初始化的全局变量 (包括指针变量)
    2. 初始化的常量全局变量
    3. 初始化局部静态变量
    4. 李< / ol > < / >
    5. 例如:全局变量int globalVar = 1;或静态局部变量static int localStatic = 1;将存储在初始化的数据段中。
    6. 这个段可以是进一步分为初始化只读区和初始化读写区初始化的常量全局变量将位于初始化的只读区域,而在运行时可以修改值的变量将位于初始化的读写区域
    7. 此段的大小由程序源代码中的值的大小决定,并且在运行时不改变
    8. 进一步阅读 .

4. 堆栈段

  • 堆栈段用于存储在函数(函数可以是主函数或用户定义函数)内部创建的变量,如变量
    1. 函数(包括指针变量)局部变量
    2. 传递给函数的参数
    3. 返回地址
    4. 李< / ol > < / >
    5. 一旦函数执行完成,存储在堆栈中的变量将被删除。
    6. 进一步阅读 .

5. 堆段

  • 这个段支持动态内存分配。如果程序员想动态分配一些内存,那么在C语言中可以使用malloccallocrealloc方法。
  • 例如,当int* prt = malloc(sizeof(int) * 2)时,将在堆中分配8个字节,该位置的内存地址将返回并存储在ptr变量中。ptr变量将在堆栈或数据段上,这取决于它的声明/使用方式。
  • 进一步阅读 .

关于存储需要记住的一件事是似< / em > < em >规则。编译器不需要把一个变量放在特定的位置——相反,它可以把它放在任何它喜欢的地方,只要编译后的程序按照抽象C机器的规则在抽象C机器中运行。这适用于所有存储持续时间。例如:

  • 一个没有被访问的变量可以被完全消除——它没有存储空间……任何地方。例子 -看到在生成的程序集代码中有42,但没有404的迹象。
  • 具有自动存储持续时间的变量如果没有获取其地址,则根本不需要存储在内存中。循环变量就是一个例子。
  • const或实际上是const的变量不需要在内存中。例子——编译器可以证明foo是有效的const,并将其使用内联到代码中。bar具有外部链接,编译器不能证明它不会在当前模块之外被更改,因此它不是内联的。
  • malloc分配的对象不需要驻留在从堆分配的内存中!例子——注意代码没有调用malloc, 42也没有存储在内存中,而是保存在寄存器中!
  • 因此,由malloc分配的对象和引用将丢失,而不会释放带有free 不需要泄漏内存的对象…
  • malloc分配的对象不需要在堆下面中,即Unixen上的程序中断(sbrk(0))…

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 section

char *char c[]

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

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


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

将非常大的字符串字面值也放在堆栈?还是.data ?或者编译失败?

函数参数

void f(int i, int j);

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

然后,如价值优化是什么?意思是在gdb?所示,-O0将所有内容吸进堆栈,而-O3则尝试尽可能多地使用寄存器。

然而,如果函数内联,它们就像普通的局部变量一样被对待。

const

我相信这没什么区别,因为你可以把它类型转换掉。

相反,如果编译器能够确定某些数据从未被写入,理论上它可以将其放置在.rodata中,即使不是const。

做分析。

指针

它们是变量(包含地址,地址是数字),所以和其他所有变量一样:-)

malloc

这个问题对于malloc没有太大意义,因为malloc是一个函数,并且在:

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

*i是一个包含地址的变量,所以它属于上述情况。

至于malloc在内部是如何工作的,当你调用它时,Linux内核在其内部数据结构上将某些地址标记为可写的,当程序最初接触这些地址时,就会发生错误,内核启用页表,这样就可以在没有segaul: x86分页是如何工作的?的情况下进行访问

然而,请注意,这基本上就是当你试图运行可执行文件时,exec系统调用在底层所做的事情:它标记它想要加载到的页面,并在那里编写程序,另见:内核如何得到一个可执行的二进制文件在linux下运行?除了exec对加载到哪里有一些额外的限制(例如,是代码不能重新定位吗)。

在现代2020实现中,用于malloc的确切系统调用是mmap,而在过去使用的是brk: malloc()是否使用brk()或mmap()?

动态库

基本上是将mmaped放入内存:https://unix.stackexchange.com/questions/226524/what-system-call-is-used-to-load-libraries-in-linux/462710#462710

环境变量和mainargv

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