C语言随笔1:内存分配方式与动静态变量

首先几个基本概念(网上的各种说法都很乱;个人理解整理了一下

内存分类方法很多,四区(动态区,静态区(全局区)、文字常量区,代码区);  五段(stack heap bss data text);

存储的角度

rocode 存储再flash(或称rom)里的code

(rocode基本等同于(.text)

rodata 存储在flash(或称rom)里的data(文字常量,虽然叫rodata,但是实际存储在rom里

(该rodata可以基本可以等同于.rodata段,但是编译计算大小时不等于.rodata段的大小

rwcode 运行在ram里的code(某些需要从flashcopy到ram里运行的代码)

rwdata 存储在ram里的data(常规变量

(该rwdata 包括了(.stack)(.heap)(.bss)(.data))

flash空间包括:rodata+rocode

ram空间包括:rwdata+rwcode

但是在实际编译时,编译器统计出的rodata可能会包含一部分rwdata中的inited部分,因为data段里有要初始化的数据,会copy一份到rom中,

相当于文字常量

有了以上 清晰概念,再往下对应

从内存地址分配的角度,四区(动态区,静态区(全局区)、文字常量区,代码区)

和编译角度 五段(stack heap bss data text);的对应关系

1.动态区  也统称为堆栈段 
1.1栈区(.stack)(属于rw_data  编译器自动分配释放,存放函数的参数局部变量等。函数结束自动释放
1.2堆区(.heap)(属于rw_data  一般由程序员分配释放, 若程序员不释放,程序结束时可能由os回收。
        new或者malloc出来的对象;(不释放导致内存泄漏)

2.静态区域(即全局区 

   2.1    部分的数据段( 或称.data)(属于rw_data存储在ram中)

    具有初值的 全局变量和 静态变量 的存储 都是放在 

   2.2 (.bss)(属于rw_data存储在ram中)

       未初始化的 全局变量 和 静态变量 的存储 都是放在.bss

文字常量区(部分的.data) (属于ro_data 存储在rom中)

  3.1常量字符串 就是放在这里的。 程序结束后由系统释放。(常量不允许修改)

  3.2 const 变量 常量区在(.data)里,不占内存(rom),静态存储区在内存中

总的来说,

数据段比较特殊,包括了 .bss(未初始化 ).data(已初始化) .rodata(文字常量)

数据段中的bss data 存储在ram中,rodata存储在rom中

 4 代码区(.text)属于ro_code

 程序代码区(.text) —存放函数体的二进制代码。常量区与代码区都是 代码段(.text)的

 

再反过头来从变量的角度看分配:

先说一个概念

作用域:起作用的区域。分为

1、  代码块:一个{}包括的区域,甚至可以在一个if的{}下定义变量,但不常用。一般都是在函数开头定义。

2、 文件作用域:一个.c文件    (由static限定的全局变量在此。#include指令包含的其他文件也算同一个文件,

              但是static的变量还是要放在.c文件中,因为虽然会include进.h文件。但是在编译时

              会在.h文件中也创建一个static变量,互不可见。

              文件作用域是仅仅对一个文件起作用

3、全局可见:定义的全局变量,注意:对于全局变量的赋值必须在函数之内,除了初始化可以在外面赋值

链接属性:一共有3种:

  1. none(无)

    总是被当做单独的个体,也就是说该标识符的多个声明被当作独立不同的实体
  2. internal(内部)

    在同一个源文件中的所有声明中都指向同一个实体,但位于不同源文件的多个声明则分属不同的实体
  3. external(外部):

    不论声明多少次、位于几个源文件都表示同一个实体

局部变量: 在动态存储区,函数结束释放(在栈区stack上)
      若没有初始化,它的初始值是垃圾

      同名内层块会对把外层的变量隐藏掉,不要这样用
      作用域:代码块(既 一个{}括起来的一段代码;函数中使用一般放在最开头
静态局部变量:static 在静态存储区数据段) 函数调用结束保留 下次调用维持上次的值
       仅在创造时初始化一次,没有初始化默为0。(最好初始化,更严谨)
          释放:main函数是整个程序的声明周期,静态变量在main 的return之后释放
          (static改变了生命周期;没有改变作用域,仅仅是存储区域由栈变为数据段)

全局变量: 在静态存储区数据段 创造和初始化一次 没有初始化值默为0
      只能定义一次,可以多次声明引用

      具有全局可见性,但是引用时需要extern

静态全局变量:static 在数据段 用于解决重名问题,该变量只在本文件使用
      (所以一般不放在头文件,直接源文件)
      (static只改变作用域;限定可见性为该.C文件)

全局变量的跨文件引用

        跨文件引用,只讨论全局变量和函数

         extern 是只声明定义
        普通全局变量直接引用编译会报错,要extern才能用
        具体用法:

        变量:在a.c中定义了全局变量 struct  t,在a.h中extern struct t;

           并在b.c中#include a.h 然后就可以在b.c中使用。(这是项目 中的用法)

              在b.c中extern 也可以,规范用法还是在.h中extern

           原理上将,其实就是在b.c中extern,告诉编译器,这个变量在别的文件中定义,link阶段再检查

        函数:用法与变量相同,都是在.h文件中extern出去。并include使用

           规范用法都是在a.h中extern 并 在b.c中include

           EXTERN :宏定义:待补充
      原理上来讲:从链接属性上说,需要extern的变量在b.c中是没有定义的。t变量虽然具有全局可见性

      编译器在编译各个独立的文件时是相互不透明的,但是编译器并不知道,所以需要extern一下,告诉编译器放行。

      可以正常产生obj(二进制指令流);在link阶段,可见范围扩大到整个程序。

      (如果此时发现该变量定义多次会报错)。

      (link阶段的另一个错误:函数有定义,有调用,但全局声明区分了大小写,link阶段会报错

      编译结束生成.exe文件

      extern 将变量从none属性变为external ,多个文件对应同一个实体

      static 将变量从变为internal(内部)

      在一些复杂的项目中,
      比较习惯在所有的函数声明前添加extern修饰,
      以防止遗漏包含头文件而导致的编译错误。

.H中
#ifdef SOC_START_C
#define SOC_START_EXE
#else
#define SOC_START_EXE extern
#endif

.C中

#define SOC_START_C
#include "soc_start.h"

              当编译器处理.C文件时,它强制xxx_EXT (在相应.H文件中可以找到)为空, (因为xxx_GLOBALS
              已经定义)。所以编译器给每个全局变量分配内存空间,而当编译器处理其他.C 文件时,
              xxx_GLOBAL 没有定义,xxx_EXT 被定义为 extern,



    标识符:就是变量的名字
    关键字:变量类型 

以下为准

 

 

 



 

posted @ 2020-01-18 23:48  无头之蝇  阅读(439)  评论(0)    收藏  举报