目录
最近在学 STM32 单片机,再次回顾C语言的一些知识,好记性不如烂笔头。
一、嵌入式C语言的结构特点
1、程序总从 main 函数开始执行,语句以分号“ ; ” 结束,采用 /*...*/ 或 // 做注释。
2、函数是C语言的基本结构,每个C语言程序均由一个或多个功能函数组成。
3、函数由两部分组成:说明部分和函数体。
函数名(参数)
{
[说明部分];
函数体;
}
4、一个C语言程序包含若干个源程序文件( .c 文件)和头文件( .h 文件),其中 .h 头文件主要由预处理命令(包括文件、宏定义、条件编译等)和数据声明(全局变量、函数等声明)组成; .c 源文件主要是功能函数的实现文件。
5、采用外设功能模块化设计方法,一个外设功能模块包括一个源文件( .c 文件 )和一个头文件( .h 文件),.c 文件用于具体外设功能模块函数的实现,.h 头文件用于对于该外设功能模块参数及功能函数的声明。
嵌入式系统开发多采用模块化、层次化的设计思想,系统层次架构清晰,便于协同开发。
图1 嵌入式系统的软件基本结构框架
二、数据类型
数据是嵌入式C语言的基本操作对象,数据类型是指数据在计算机内存中的存储方式,如基本数据类型中的整型(存放整数)、浮点型(存放实数)、字符型(存放字符)、指针(存放地址)以及派生出的复合数据类型(如数组、结构体、共用体、枚举类型)。嵌入式C语言的数据类型如图2所示:
图2 嵌入式C语言的数据类型
三、const 关键字
const 关键字用于只读的变量,其值在编译时不能被改变,注意,const 关键字定义的是变量而不是常量。使用关键字是为了在编译时防止变量的值被误修改,同时提高程序的安全性和可靠性,一般放在头文件中或者文件的开始部分。
在C99标准中, const 关键字定义的变量是全局变量,const 关键字与 #define 关键字存在区别,#define 关键字知识简单的文本替换,而 const 关键字定义的变量是存储在静态存储器中的。使用#define 关键字定义的常量的形式为
#define PI 3.14159
使用该方式定义后,无论在何处使用PI,都会被预处理器以3.14159替代,编译器不对PI 进行类型检查,若使用不慎,则很可能由预处理引入错误,且这类错误很难发现。用 const 声明变量的方式收入虽然增加了分配空间,但可以很好地消除预处理引入的错误,并提供了良好的类型检查形式,保证安全性。
利用 const 关键字进行编程时需要注意以下三点.
1)、使用const 关键字声明的变量,只能读取,不能被赋值。如:
const uint8_t sum = 3.14;
uint8_t abs = 0;
...
sum = abs; //非法,将导致编译错误,因为sum 只能被读取,不能被赋值
abs = sum; // 合法
2)、const 关键字修饰的变量在声明时必须初始化,上述语句表示 sum 值时 3.14,且 sum 值在编译时不能修改,若在编译过程中直接修改 sum 值,编译器会提示出错。
3)、函数的形参声明为 const , 则意味着所传递的指针指向的内容只能读,而不能被修改,如C 语言的标准函数库中用于统计字符串长度的函数 int strlen(const char *str)。
四、static 关键字
在嵌入式C语言中,static 关键字可以用来修饰变量,使用static 关键字修饰的变量称为静态变量。
静态变量的存储方式与全局变量一样,都是静态存储方式。全局变量的作用范围是整个源程序,当一个源程序由多个源文件组成时,全局变量在各个源文件中都是有效的,即全局变量定义某个源文件中,若想在另一个源文件中使用该全局变量,则只需在该源文件中通过 extern 关键字声明该全局变量就可以使用了。若在该全局变量前加上该关键字 static ,则该全局变量被定义成一个静态全局变量,其作用范围只在定义该变量的源文件内有效,其他源文件不能音乐该全局变量,注意就避免了在其他源文件中印引起相同名字的变量而引发错误,有利于模块化程序设计。
利用 static 关键字进行编程时需要注意以下要点。
1)、static 关键字不仅可以用来修饰变量,而且可以用来修饰函数。模块化程序设计中,若用 static 声明一个函数,则该函数只能被该模块内的其他函数调用,这样有效避免了因其他模块的文件定义了同名函数而引发的错误,充分体现了程序的模块化设计思想。
2)、static 处理用于定义静态全局变量,还用于定义静态局部变量,保证静态局部变量在调用过程中不被重新初始化。
五、volatile 关键字
嵌入式来发展,常用到volatile 关键字,它是一个类型修饰符,含义为“易变的”,使用方式如下:
volatile char i;
这里使用 volatile 关键字定义了一个字符型的变量i,指出 i 是随时可能发生变化的,每次使用改变了是都必须从 i 的地址中读取。
用于内存的读/写速度远不及 CPU 中寄存器的读/写速度,为了提高数据信息的存取速度,一方面在硬件上引入高速缓存 Cache 另一方面在软件上使用编译器对程序进行优化,将变量的值提前从内存读取到 CPU 的寄存器中,以后用到该变量是,直接从速度较快的寄存器中读取,这样有利于提高运算速度,但同时也可能存在风险,如该变量在内存中的值有可能被程序的其他部分(如其他线程)修改或覆盖,而寄存器中存放的仍是之前的值,这就导致应用程序读取的值和实际变量值不一致;也有可能是寄存器中的值发生了改变,而内存中该变量的值没有被修改,同样也会导致不一致的情况发生。因此,为防止由于编译器对程序进行优化导致读取错误数据,使用 volatile 关键词进行定义。
简单来说,使用 volatile 关键字就是不让编译器进行优化,即每次读取或者修改值时都必须重新从内存中读取或者修改,而不是使用保存在寄存器的备份。
const 关键字的含义为“只读” ,volatile 关键字的含义为“易变的”,但 volatile 关键字解释为“直接存取原始内存地址”更为合适,使用 volatile 关键字定义变量后,该变量不会因外因而发生变化了。一般来说,volatile 关键字常用于在以下场合:
1)中断服务程序中修改的。供其他程序检测的变量需要使用 volatile 关键字。
2)多任务环境下各任务间共享的标志应添加 volatile 关键字。
3)外设寄存器地址映射的硬件寄存器通常要用 volatile 关键字进行声明。
六、extern 关键字
extern 关键字用于指明次函数或变量定义在其他文件中,提示编译器遇到次函数或变量时到其他模块中寻找其定义。这样,extern 关键字声明的函数或者变量就可以在本模块或其他模块中使用,因此,使用 extern 关键字是一个声明而不是重新定义。使用方法如下:
extern int a;
extern int funA();
解析:第一条语句仅仅是变量a 声明,而不是定义变量 a ,并未为a 分配内存空间,变量 a 作为全局变量只能被定义一次。第二条语句声明函数 funA(), 此函数已在其他文件中定义。
嵌入式开发一般采用模块化设计思想,因此,为保证全局变量和功能函数的使用,extern 关键字一般也在 .h 头文件中对某个模块提供给其他模块调用的外部函数及变量进行声明,实际编程中只需要将该 .h 头文件包含进该模块对应的 .c 文件中,即在该模块的 .c 文件中加入代码 #include "xxx.h" 。 实例如下: