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
3 文字常量区(部分的.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种:
-
none(无)
总是被当做单独的个体,也就是说该标识符的多个声明被当作独立不同的实体。 -
internal(内部)
在同一个源文件中的所有声明中都指向同一个实体,但位于不同源文件的多个声明则分属不同的实体。 -
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,
标识符:就是变量的名字
关键字:变量类型
以下为准


浙公网安备 33010602011771号