IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> C语言内存布局理解 -> 正文阅读

[C++知识库]C语言内存布局理解

编辑:就是创建和修改C程序的源代码-我们编写的程序称为源代码。
编译:就是将源代码转换为机器语言。编译器的输出结果成为目标代码,存放它们的文件称为目标文件。扩展名为.o或者.obj。
(该部分编译是指汇编器编译汇编语言或者编译器编译高级语言)
链接:链接器将源代码由编译器产生的各种模块组合起来,再从C语言提供的程序库中添加必要的代码模块,将它们组成一个可执行的文件。在windows下扩展名为.exe,Unix下无扩展名。
执行:运行程序。

1.栈区(stack) --编译器自动分配释放,主要存放函数的参数值,局部变量值等;

2.堆区(heap) --由程序员分配释放;

3.全局区或静态区 --存放全局变量和静态变量;程序结束时由系统释放,分为全局初始化区和全局未初始化区;

4.字符常量区 --常量字符串放与此,程序结束时由系统释放;

5.程序代码区--存放函数体的二进制代码

文本段(Text)
????????通常代码段和只读数据段合成为文本段(Text),包含实际要执行的代码(机器指令)和常量。它通常是共享的,多个实例之间共享文本段,文本段是不可修改的。

代码段(Code)
????????代码段由程序中执行的机器代码组成。在C语言中,程序语句进行编译后,形成机器代码。在执行程序的过程中,CPU的程序计数器指向代码段的每一条机器代码,并由处理器依次运行。

只读数据段(RO data,即常量区)
????????只读数据段是程序使用的一些不会被更改的数据,使用这些数据的方式类似查表式的操作,由于这些变量不需要更改,因此只需要放置在只读存储器中即可。

? ? ? ?通常字符串常量就是放置在这里,程序结束后由系统释放。

????????注意:这个区域的存在与否,一直是一个争议的地方,但是我们这里认同是存在的,因为我们的程序中的确出现了与其他数据段不同的一块区域,但是往往很多时候大家把只读数据段(RO data)和下面的已初始化读写数据段(RW data)合称为数据段data,但是其实这个是不合适的,因为在执行过程中,这两个区域的读写权限是不同的,顾名思义,只读数据段(RO data)是只读的,而已初始化读写数据段是可读可写的。

??

已初始化读写数据段(RW data)
? ? ? ? 已初始化数据是在程序中声明,并且具有初值的变量,这些变量需要占用存储器的空间,在程序执行时它们需要位于可读写的内存区域内,并具有初值,以供程序运行时读写。

未初始化数据段(BSS? Block Started by Symbol)
? ? ? ? 未初始化数据是在程序中声明,但是没有初始化或者初始化为0的变量,这些变量在程序运行之前不需要占用存储器的空间,BSS段的变量只有名称和大小却没有值,运行后会被初始化为0。

动&静

????????一个程序被加载到内存中,这块内存首先就存在两种属性:静态分配内存和动态分配内存。?
静态分配内存:是在程序编译和链接时就确定好的内存。?
动态分配内存:是在程序加载、调入、执行的时候分配/回收的内存。

????????代码段、只读数据段、读写数据段、未初始化数据段属于静态区域,堆、栈属于动态区域。代码段、只读数据段和初始化读写数据段在程序链接后即产生,存在于可执行文件中,但是未初始化数据段(BSS)将在程序初始化的时候开辟,而堆和栈作为动态区域在程序运行的过程中分配和释放。

转载自:https://blog.csdn.net/gatieme/article/details/43567433

#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>

#define SHW_VAR_ADR(ID, I)                    \
printf("the %20s\t is at adr:%p\n", ID, &I); //打印变量地址宏

#define SHW_POT_ADR(ID, I)                    \
printf("the %20s\t is at adr:%p\n", ID, I);  //打印指针指向地址宏

extern int func(void);
extern void afunc(void);

/*extern etext, edata, end;*/

/**
(1)全局变量和静态变量的存储是放在一块的,
初始化的全局变量和静态变量在一块区域(RW data),
未初始化的全局变量和未初始化的静态变量在相邻的另一块区域(BSS)。
程序结束后有系统释放
如下面(1).1   (1).2   (1).3所述
**/
// (1).1  -- 只要是静态变量,即使是局部的,也不存储在栈中,而是即存储在静态区域中,并依据其是否初始化,分别存储在BSS段和DATA段
static int /*bss_*/unini_glo_sta_var;                           //  静态未初始化全局变量,虽然系统会默认初始化为0,但仍然存放在BSS区
static int /*data_*/ini_glo_sta_var = 10;                       //  静态初始化全局变量,存放在RW DATA区

// (1).2  --  只要是全局变量,即存储在静态区域,并依据其是否初始化,分别存储在BSS段和DATA段
int /*bss_*/unini_glo_var;                                      //  未初始化全局数据存储在BSS数据区
int /*data_*/ini_glo_var = 42;                                  //  初始化全局数据存储在RW DATA数据区

// (1).3  --  全局常量,其本身是全局变量,即存储在静态区域, 同(1).2
==========原文是const int unini_glo_cnt_var;但是vs上不允许这样定义,const变量必须初始化,下面的(2).2同理
int unini_glo_cnt_var;                                        //  未初始化全局常量[不安全], 自动初始化为0, 但仍然存放在BSS区  
const int ini_glo_cnt_var = 10;                                     //  初始化全局常量,存储在常量区
// 对于常量我们需要注意的问题在于,他们并不像我们期望的那样存储在常量区(RO data),
// 常量区只用于存储初始化好的全局常量以及字符串变量本身(不是是指针)
// 局部常量作为局部量仍然存储与栈中
// 因为常量区与代码段是在一起的(在有些段分类结果中,是不存在常量区的,常量区和代码段合成为代码区)
// 而本身来说常量只是限制了其读写权限,这种读写权限的限制可以在编译阶段由编译器进行制定和限制,
// 这样在严格的编译器审查结果下,运行阶段的代码就不存在对常量的读写操作,因此就没必要将其他局部常量也存储在常量区
// 否则将造成代码段的臃肿。。。

static int unini_glo_sta_cnt_var;
static int ini_glo_sta_cnt_var = 10;

int main(void)
{
    char *p_alloca = NULL, *b_malloc = NULL, *nb_malloc = NULL;

    // (1).4  局部静态变量,仍然是静态变量,同(1).1
    static int unini_sta_var;               //  局部未初始化静态变量,存储在BSS段
    static int ini_sta_var = 10;            //  静态初始化局部变量,存储在DATA段中

    // 局部非静态变量存储在栈中
    // (2).1  --  局部变量(不管初始化没有)存储在栈中
    int unini_var;                          //  局部未初始化变量,
    int ini_var = 10;                       //  局部初始化变量

    //  (2).2  --  局部常量(不管初始化没有)存储在栈中, 同(2).1
    int unini_cnt_var;            // 未被初始化的局部常量,不安全,存储在栈中
    const int ini_cnt_var = 10;         // 局部常量,存储在栈中

    //  (2).3  --  指针常量和常量指针,其本质还是局部变量或者局部常量,存储在栈中,同(2).1 (2).2
    const int *p_cnt_var = &ini_cnt_var;   //  指向常量的指针
    int * const cnt_p_var = &ini_var;       //  指针常量
    const int * const cnt_p_cnt_var = &unini_cnt_var;    // 指向常量的常指针

    // (3)  字符串常量,存储在常量区,在vs中字符串常量必须显式定义成const char *,如果只是char * = "ABCDE"会报等号两遍类型不同错
    const char* str_cnt = "ABCDE";      //  字符串面变量, 存储在常量区, 即(RO data)
                                            //  本代码等价于const char* str1 = "ABCDE"
    char str_array[] = "ABCDE";             //  字符数组, 相当于初始化的局部变量,存储在栈中


/*    printf("Adr etext:%8x\t Adr edata %8x\t Adr end %8x\t\n", &etext, &edata, &end);*/

    // TEXT段 -- 代码段
    printf("------------------------------------------------------\n");
    printf(".Text Location:\n");
    SHW_VAR_ADR("main", main);              //查看代码段main函数位置
    SHW_VAR_ADR("afunc", afunc);           //查看代码段afunc函数位置
    printf("------------------------------------------------------\n\n");

    // BSS段 -- 未初始化全局变量区
    printf("------------------------------------------------------\n");
    printf(".Bss Location:\n");
    SHW_VAR_ADR("unini_glo_sta_var", unini_glo_sta_var);                //  全局未初始化静态变量, 在BSS段
    SHW_VAR_ADR("unini_sta_var", unini_sta_var);                        //  未初始化静态变量,在BSS段
    SHW_VAR_ADR("unini_glo_cnt_var", unini_glo_cnt_var);                //  全局未初始化常量,在BSS段
    SHW_VAR_ADR("unini_glo_var", unini_glo_var);                        //  全局未初始化变量在, BSS段
    SHW_VAR_ADR("unini_glo_sta_cnt_var", unini_glo_sta_cnt_var);        //  全局未初始化静态常量,在BSS段
    printf("------------------------------------------------------\n\n");


    // RW DATA段 -- 可读写已初始化数据段
    printf("------------------------------------------------------\n");
    printf(".Data Location:\n");
    SHW_VAR_ADR("ini_glo_sta_var", ini_glo_sta_var);                    //  全局初始化静态变量存储在RW data区域
    SHW_VAR_ADR("ini_glo_var", ini_glo_var);                            //  全局初始化变量存储在RW data

    SHW_VAR_ADR("ini_sta_var", ini_sta_var);                            //  局部初始化静态变量存储在RW data区域

    SHW_VAR_ADR("ini_glo_sta_cnt_var", ini_glo_sta_cnt_var);            //  全局静态已初始化常量,存储在RW data区域
    printf("------------------------------------------------------\n\n");

    // RO data  --  只读数据段
    printf("------------------------------------------------------\n\n");
    printf("RW data");
    SHW_VAR_ADR("ini_glo_cnt_var", ini_glo_cnt_var);     // 初始化全局常量,同字符串面变量一样,位于文本区,即常量区
    SHW_POT_ADR("str_cnt", str_cnt);                  //  字符串面变量保存在常量区,即文本区
    SHW_VAR_ADR("str_cnt", str_cnt);             //  指针str1本身在栈中

    printf("------------------------------------------------------\n\n");

    // STACK -- 栈
    printf("------------------------------------------------------\n");
    printf("Stack Locations:\n");
    afunc();                                    //   递归调用5此afunc函数

    p_alloca = (char *)alloca(32);              //   从栈中分配空间, 用完立即释放
    if (p_alloca != NULL)
    {
        SHW_VAR_ADR("start", p_alloca);
        SHW_VAR_ADR("end", p_alloca + 31);
    }


    // 局部变量(不管初始化没有)存储在栈中
    SHW_VAR_ADR("unini_var", unini_var);
    SHW_VAR_ADR("ini_var", ini_var);

    //  局部常量(不管初始化没有)存储在栈中
    SHW_VAR_ADR("unini_cnt_var", unini_cnt_var);        // 未被初始化的局部常量,不安全,存储在栈中
    SHW_VAR_ADR("ini_cnt_var", ini_cnt_var);            // 局部常量,存储在栈中

    //  指针常量和常量指针,其本质还是局部变量或者常量,存储在栈中
    SHW_VAR_ADR("p_cnt_var", p_cnt_var);            //  该指向常量的指针,其本身其实是一个(初始化的)局部变量[同ini_var], 存储在栈中
    SHW_VAR_ADR("cnt_p_var", cnt_p_var);            //  该指针常量,其本身其实是一个初始化的局部常量[同ini_cnt_var], 存储在栈中
    SHW_VAR_ADR("cnt_p_cnt_var", cnt_p_cnt_var);    //  该指向常量的指针常量作为一个初始化的局部常量,存储在栈中

    SHW_POT_ADR("str_array", str_array);             //  字符串数组,相当于初始化的局部变量,保存在栈中
    SHW_VAR_ADR("str_array", str_array);             //  指针str2本身在栈中,其地址本身,就是字符串数组的地址

    printf("------------------------------------------------------\n\n");


    printf("------------------------------------------------------\n");
    printf("Heap Locations:\n");
    b_malloc = (char *)malloc(32 * sizeof(char));   //从堆中分配空间
    nb_malloc = (char *)malloc(16 * sizeof(char));  //从堆中分配空间
    printf("the Heap start: %p\n", b_malloc);   //堆起始位置
    printf("the Heap end:%p\n", (nb_malloc + 16 * sizeof(char)));//堆结束位置

    // 指针指向的区域在堆中,但是指针本身在栈中
    printf("\nb and nb in Stack\n");
    SHW_VAR_ADR("b_malloc", b_malloc);       //显示栈中数据b的位置
    SHW_VAR_ADR("b_malloc", nb_malloc);      //显示栈中数据nb的位置
    free(b_malloc);               //释放申请的空间,以避免内存泄漏
    b_malloc = NULL;
    free(nb_malloc);              //释放申请的空间,以避免内存泄漏
    nb_malloc = NULL;
    printf("------------------------------------------------------\n\n");

    func();

    return EXIT_SUCCESS;
}

void afunc(void)
{
    static int long level = 0;          // 静态数据存储在数据段中
    int      stack_var;                 // 局部变量,存储在栈区
    if (++level == 5)                   // 此函数递归调用5次
    {
        return;
    }
    printf("stack_var is at:%p\n", &stack_var);
    //      SHW_VAR_ADR("stack_var in stack section",stack_var);
    //      SHW_VAR_ADR("Level in data section",level);
    afunc();
}

//main.cpp 
 
int a = 0; 全局初始化区 XXX,初始化成0的变量存储在全局未初始化区
 
char *p1; 全局未初始化区 
 
main() 
 
{ 
 
int b;// 栈 
 
char s[] = "abc"; //"abc"在常量区,s在栈上。 
 
char *p2; //栈 
 
char *p3 = "123456"; //123456\0";在常量区,p3在栈上。 
 
static int c =0; //全局(静态)初始化区 
 
p1 = (char *)malloc(10); 
 
p2 = (char *)malloc(20); 
 
//分配得来得10和20字节的区域就在堆区。 
 
strcpy(p1, "123456"); //123456\0放在常量区,编译器可能会将它与p3所指向的"123456"优化成一个地方。 
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>

static int a;
static int b = 0;
static int c = 8;

int d;
int e = 0;
int f = 3;

int main()
{
    static int g;
    static int h = 0;
    static int i = 9;

    int j;
    int k = 0;
    int m = 28;



    printf("&a = %p &b = %p &c = %p\n", &a, &b, &c);
    printf("&d = %p &e = %p &f = %p\n", &d, &e, &f);
    printf("&g = %p &h = %p &i = %p\n", &g, &h, &i);
    printf("&j = %p &k = %p &m = %p\n", &j, &k, &m);

    return 0;
}

输出结果:
&a = 00D7431C &b = 00D74320 &c = 00D74008
&d = 00D74314 &e = 00D74318 &f = 00D74004
&g = 00D74324 &h = 00D74328 &i = 00D7400C
&j = 0038FAA0 &k = 0038FA94 &m = 0038FA88

//栈区的内容先写高地址,再写低地址

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2021-09-05 10:37:43  更:2021-09-05 10:39:17 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 20:11:40-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码