嵌入式C语言编程中经验教训总结
C语言是目前主流的系统编程语言,而嵌入式C语言,是C语言在嵌入式系统中的扩展,不同于pc平台,嵌入式的cpu处理器可以说是五花八门,高中低档齐全,架构也不相同,所以,嵌入式的c程序编写往往具有鲜明的系统特征,例如会涉及到操作硬件,需要自己编写操作硬件的函数,可用的内存就是芯片上的ram空间,处理器的性能大体决定了程序的运行速度,处理器的资源配置也决定了可用资源的多少。嵌入式C程序在编译链接完成后,一般会有如下的输出:
在Program Size行中我们可以看到:
- Code,全程为Readonly code Memory,为只读代码段
- RO-data,全程为Readonly data Memary 只读数据段
- RW-data,readwrite data Memary 读写数据段
- ZI-data,Zero Initialize,没有初始化,默认初始化0的可读写变量的大小
通常地说,我们编写的代码大小,也就是最终烧写到处理器FLASH中的空间就是:Code + RO Data + RW Data,而程序运行的时候,芯片内部RAM使用的空间为: RW Data + ZI Data,在嵌入式的c语言中,我们如何确定或分配这些空间昵,这就是const、static和volatile的主要作用。
(一) 详解const、static和volatile
1、static关键字的作用
在嵌入式C语言中static有下性质:
-
(1)声明变量:
在函数体中,一个被声明为静态的变量在这一函数被调用过程中只会被分配一次内存,且整个运行期间不会重新分配;在函数体外、本源文件内,该变量只能该源文件内的所有函数访问,但不能被其他源文件的函数访问。它是一个本地的全局变量,即便extern外部声明也不可以。(2) 声明函数
在某源文件内,一个被声明为静态的函数仅仅只可以被该源文件的其它函数调用。也就是说,这个函数被限制在声明它的源文件的本地范围之内使用。
举例如下:
int fun_Test(void)
{
unsigned char test_A=0;
static unsigned char test_B=0;
test_A++;
test_B++;
}
调用上面的fut_Test()函数就会发现,每次调用该函数的时候,test_A++被执行后,其值只能是1;test_B就会一直的加下去,直到加到255,达到最大值溢出变成0,也就是说,普通的变量在执行完函数后,其内存的空间将会被释放,但是Static所修饰的变量的内存空间将不会被释放,虽然调用函数的时候,有对test_B进行了初始化,语句也会被跳过。
2、volatile关键字的性质
volatile 中文解释是易变的,作为指令关键字,确保本条指令不会因编译器的优化而省略,且要求每次直接读值。在我们的编辑器中,常常会将我们的代码优化,提高代码的执行速度,但如果有一个语句没有写操作,或者是读取操作,编辑器就自作聪明的保存了该内存空间的某一时刻的值, 用于以后的计算,虽然该内存单元中内容改变了,但是程序中用到的变量还是该时刻的保存值,多以会出现一些意想不到的错误,而Volatile的作用就是提醒编译器,这个内存单元空间中的内容是在变动的,编译器优化的时候每次都从变量的内存地址读取,而不是从寄存器或者cache缓存中读取。常常在下面几种情况下,须使用volatile:
1:多线程环境下,一个线程使用的全局变量,但是他的值会被其他线程更新的情况下。
- 变量可能被中断处理程序更新
3:对数据采集类应用,外部传感器数据映射到内存单元的寄存器
如果系统有完备的信号量、中断或消息机制,volatile并非不可或缺,但volatile关键字提供了一个简单可靠的快速响应外界变化的方法,虽然反复操作效率有点低。
3、const关键字的作用
const定义的变量,也称常量,更准确的说const变量一旦初始化完成后,是不能被修改的,始终都是不变的,一旦申明后就不能被修改了,嵌入式c语言中 ,const 全局变量存储在RO Data数据段,而const局部变量存储在栈中,代码块结束时释放。
嵌入式程序中,在存储空间分配上,变量加const修饰符,会存储在flash中而不是ram中,通常,flash空间会比ram空间大好多倍,这就有效的节省了ram空间的占用。
例如:const double PI = 3.141599265;
上面定义了const变量PI,定义后,在其他程序中,是无法对PI进行修改赋值的,这就保证了整个 系统中PI的一致性,不会因为其他有的地方重新定义PI=3.14等带来的精度误差。
4关键字的组合使用
上面的关键字也可以组合使用,例如:
static const规定变量在当前的模块中是只读的变量,无法被全局中被其他模块读取。例如对公司log图片点阵,我们当然不希望在不同模块中展示的log会出现不一致,如果我们如下定义:
static const unsignedi char LogDitArray[16384] ={0x00,0x00,0xC0,0xC0,0xC0,0x80,0x00,0x80,
。。。 。。。
0x00,0x00,0xC0,0xC0,0xC0,0x80,0x00,0x10
}
编译之后可在.map文件看到其分配到flash存储空间,且只能在本模块使用