• 基础C语言知识串串香6☞内存大话题


    6217760-7a1d63bdb0bc5b7a.jpg

    文章参考微信公众号[嵌入式ARM]

    一、内存大话题

    1.0、内存就是程序的立足之地,体现内存重要性。

    1.1、内存理解:

    内存物理看是有很多个Bank(就是行列阵式的存储芯片),每一个Bank的列就是位宽,每一行就是Words,则存储单元数量=行数(words)×列数(位宽)×Bank的数量;通常也用M×W的方式来表示芯片的容量(或者说是芯片的规格/组织结构)。

    M是以位宽为单位的总容量,单位是兆,W代表位宽,单位是bit。计算出来的芯片容量也是以bit为单位,但用户可以采用除以8的方法换算为字节(Byte)。比如8M×8,这是一个8bit位宽芯片,有8M个存储单元,总容量是64Mbit(8MB)。

    1.2、c语言中其实没有bool类型:以0表示假,非0表示真,则在内存存储是以int型存放的。如果想要表示真假,可以用int/char型做替换,在c++中就有boolx=true/false;

    1.3、内存对齐:内存对齐(提高访问效率速度,编译器一般默认是4字节对齐)

    1.4、char/int/short/long/float/double型:放在内存的长度和解析作用。(int*)0,使0地址指向一个int型。又比如0000111010101可以解析成int型也可以解析成float型。

    1.5、Linux内核是面向对象的,而c语言是面向过程的,但可以用结构体内嵌指针变成面向对象。

    structstudent{
    intage;//变量
    intlenth;//将相当于一个类,有变量有函数
    char*name;
    void(*eat)(void);//函数指针
    }
    

    1.6、栈的理解:

    (1)运行时自动分配&自动回收:栈是自动管理的,程序员不需要手工干预。方便简单。(表现在汇编代码,编译时,会自动编译成汇编码实现函数调用完立即改变栈顶)

    (2)反复使用:栈内存在程序中其实就是那一块空间,程序反复使用这一块空间。(硬件上有个寄存器,用来存放栈的栈顶地址,栈是有大小的空间)

    (3)脏内存:栈内存由于反复使用,每次使用后程序不会去清理,因此分配到时保留原来的值。

    (4)临时性:(函数不能返回栈变量的指针,因为这个空间是临时的)

    (5)栈会溢出:因为操作系统事先给定了栈的大小,如果在函数中无穷尽的分配栈内存总能用完。栈的操作(怎么出栈怎么入栈)是由具体硬件来干预,程序员只要明白原理就可以了,但是要给相应的栈寄存器赋值。当调用函数时,变量会自动放在栈中(入栈)当函数调用完后,栈会自动出栈.

    (6)栈的"发展"有四种情况,满增栈,满减栈,空增栈,空减栈,至于是那种要根据编译器决定,而s5pv21是满减栈。

    1.7、堆的理解:

    (1)操作系统堆管理器管理:堆管理器是操作系统的一个模块,堆管理内存分配灵活,按需分配。

    (2)大块内存:堆内存管理者总量很大的操作系统内存块,各进程可以按需申请使用,使用完释放。

    (3)脏内存:堆内存也是反复使用的,而且使用者用完释放前不会清除,因此也是脏的。

    (4)临时性:堆内存只在malloc和free之间属于我这个进程,而可以访问。在malloc之前和free之后都不能再访问,否则会有不可预料的后果。

    (5)程序手动申请&释放:手工意思是需要写代码去申请malloc和释放free。(记住:不要把申请的地址给搞丢了,不然自己用不了,也释放不了)

    申请一段内存,可以是:

    malloc( 10*sizeof(int) );
    

    原型:

    void* malloc( size_t size );
    //指针函数size_t是宏定义int都是便于可移植性,返回一个内存地址,void*可以看出,希望申请的内存用来存放什么就强制类型什么。
    

    也可以是:

    calloc( 10, sizeof(int) );
    

    原型:

    void* calloc( size_t nmemb, size_tsize);//nmemb个单元,每个单元size字节
    

    也可以是:

    void* realloc( void* ptr, size_t size );//改变原来申请的空间的大小的ptr是原来申请内存的指针,size是想要重新申请内存的大小使用就是*(p+1)=12;*(P+3)=110;
    

    申请失败返回NULL,申请成功返回一个地址,申请之后一定要检验(NULL!=p)用完一定要free(p);释放后不是不能用,是不应该使用了。可以给它“洗盘子‘,p=NULL;

    其实申请的内存并不能真正改变大小,原理是先重新申请一段内存,然后把原来申请的内存上的内容复制到新的内存上,然后释放掉原来的内存,返回新的指针。

    (6)在申请内存时,malloc(0)其实也是成功的,因为系统规定少于一定数目的大小,都申请规定的大小,如在win32系统下申请少于32字节的地址,最后申请到的空间是32字节。

    1.8、内存里的数据

    (1)代码段:存放代码二进制、常量(char*p="linux",则”linux“存放在代码段,是不可更改的)

    (2)数据段:存放非0全局变量、静态局部变量(局部只属于函数的,不是整个程序的)

    (3)bss:存放为0的全局变量/为0的静态局部变量、存放未初始化全局变量/静态局部变量

    注意:constinta=9;有两种存放方式:第一种确实存放在代码段,让a不能修改,第二种是仍然存放在数据段中,让编译器来判断,如果有改变的代码就会报错。至于那种,是不确定的,像单片机就属于第一种。

    1.9、程序在内存中的分布
    《1》一个源文件实际上是以段为单位编译成连接成可执行文件(a.out);这个可执行文件总的说是分为数据段,代码段,自定义段。数据段还可以细分成.bbs段。而杂段会在执行的时候拿掉。所以a.out分为杂段,数据段(存放的是非0全局变量).bbs段,代码段。

    《2》内存实际上被划分了两大区域,一个是系统区域,另一个是用户区域,而每一个区域又被划分成了几个小区域,有堆,栈,代码区,.bbs区,数据区(存放的是非0全局变量)。

    《3》对于有操作系统而言,当我们在执行a.out可执行文件时,执行这个文件的那套程序会帮我们把杂段清掉,然后把相应的段加载到内存对应的段。对于裸机程序而言,我们是使用一套工具将a.elf的可执行程序给清掉了所有段的符号信息,把纯净的二进制做成.bin格式的烧录文件。所以我们加载到内存的程序是连续的,也就是说代码段和数据段、.bbs段都是连续的。当然,栈空间是我们自己设置的。而且在裸机中我们不能使用malloc函数,因为我们使用的只是编译器、连接器工具没有集成库函数,没有定义堆空间区。

    《4》多程序运行情况:在Linux系统中运行cdw1.out时,运行这个文件的那套程序会帮我们把相应的段加载到内存对应的段。然后操作系统会把下载到内存的具体物理地址与每条命令(32位)的链接地址映射到TTB中(一段内存空间),当我们又运行cdw2.out时,同样也像cdw1.out一样加载进去,并映射到TTB表中。而且这两个.out文件默认都是链接0地址(逻辑),当cpu发出一个虚拟地址(Linux中程序逻辑地址)通过TTB查找的物理地址是不一样的。所以对于每一个程序而言,它独占4G的内存空间,看不到其他程序。


    往期热文:
    基础C语言知识串串香(1)

    基础C语言知识串串香(2)

    基础C语言知识串串香(3)

    基础C语言知识串串香(4)

    基础C语言知识串串香(5)


    ===========我是华丽的分割线===========


    更多知识:
    点击关注专题:嵌入式Linux&ARM

    或浏览器打开:https://www.jianshu.com/c/42d33cadb1c1

    或扫描二维码:

    6217760-e6bba06e005d8fe7.jpg

  • 相关阅读:
    LED事件
    CSR8670按钮事件
    编译提示:warning: ISO C89 forbids mixed declarations and code
    C#使用Smtp,通过qqmail发送邮件
    编写VS的Snippet
    WPF的Clipboard.SetText()有问题
    HttpListenerRequest.ContentEncoding
    jQuery的Deferred对象教程
    visual studio如何修改c++项目的.net framework框架版本
    如何让Sqlite支持Entity Framework Code-First
  • 原文地址:https://www.cnblogs.com/leon1124/p/14039757.html
Copyright © 2020-2023  润新知