一、关键字
很多的C语言教材都是这针对面向计算机编程,因此很多时候忽略了一些不常用的关键字的讲解,而在嵌入式中往往会看到很多不常用的关键字,值得我们去进一步理解。
1、register和auto
在计算机中CPU的运算速度最快,现在都达到3GHZ左右,而相对应的存储器速度却相对慢很多,访问速度最快的寄存器和缓存,由于其体积又大,不适合大容量的使用,所以只能二者相接合的方式来提高效率。程序代码保存在内存中,当使用数据时,将其送到寄存器,让CPU来访问,使用完毕,送回内存保存。
register修饰符暗示编译程序相应的变量为寄存器变量将被频繁地使用,如果可能的话,应将其保存在CPU的寄存器中,以加快其存储速度。在使用寄存器变量时,请注意:
- 待声明为寄存器变量类型应该是CPU寄存器所能接受的类型,意味着寄存器变量是单个变量,变量长度应该小于等于寄存器长度(整形长度)
- 不能对寄存器变量使用取地址符“&”,因为该变量没有内存地址(不在内存上)
- 只有局部变量和形参可以作为寄存器变量,全局变量不行
- 静态变量不可以被定义为寄存器变量
auto的出现意味着,当前变量的作用域为当前函数或代码段的局部变量,意味着当前变量会在内存栈上进行分配。(一般情况下编译器都忽略auto)
2、continue、break和return
**continue:**结束当前循环,开始下一轮循环
**break:**跳出当前循环
**return:**子程序返回语句,可返回值或不返回值
初学者往往容易犯的错误是搞不清continue和break的区别,continue只是结束当前一轮的循环然后进行下一轮循环(如果有)而break是直接退出整个循环。
3、extern与static
static可用于修饰变量和函数,修饰的变量又可以分为局部变量和全局变量,他们都存在于内存的静态区
- 静态局部变量:存储在静态变量区,生命周期为整个程序的生命周期,作用域只在对应的函数 内部,因此只有在对应的函数中才能够修改,相对比较安全。
- 静态全局变量:存储在静态变量区,生命周期为整个程序的生命周期,作用域旨在对应的文件中,在其他文件中即使使用extern进行声明也无法调用它。
- 静态函数:限定了函数的作用域仅为当前文件,好处是可以避免不同文件中的同名函数(一旦被定义为静态函数,就只能被同一文件内的函数所调用)
extern:声明外部定义,表示被定义的变量或函数为一个已经在其他文件中定义的函数或变量,不再需要在该页文件中重新定义。
4、volatile与const
volatile:易变变量声明,与register作用正好相反,它的作用是放置编译器将该变量定义为一个寄存器变量,定义为寄存器变量有时会带来副作用即减少对内存的的读写,举个例子
在选择外部存储芯片时我们 常需要一个引脚作为片选信号CS,假如该引脚的定义没有加volatile被编译器错误的认为可以变成寄存器变量就会发生以下的错误
int *Memory_CS=0x40ff000u; //定义Memory_CS指向外接存储器的片选引脚的输出寄存器
*Memory_CS=0; //低电平选中
.....
*Memory_CS=1; //重新拉高引脚
由于Memory_CS被编译器错误的编译为了一个寄存器变量在两次对0x40ff000地址写的过程中并没有进行读取,编译器就会认为第一次的写入是无效的从而忽略第一次的片选选择。
const:只读变量,一旦出现直接修改只读变量,编译器就会提示出错。const的作用对象有其特有的规则“从左往右 忽略数据类型 看const修饰的是那个对象”,举个例子
char * const P="abcd"; //定义一个字符指针P指向字符串abcd的起始地址 P为常量
P[1]='1'; //修改P[1](*P)的内容允许,编译器可以通过
P="abcd"; //重新改写P所指向地址不允许,编译器报错
const char *P ="abcd"; //定义一个字符指针P指向字符串abcd的起始地址 *P为常量
P[1]='1'; //修改P[1](*P)的内容不允许,编译器报错
P="abcd"; //重新改写P所指向地址允许,编译器通过
5、typedef
typedef:常用于为复杂的声明定义的别名,格式为 typedef 标准声明 别名; 例 typedef unsigned long uint32;
值得注意的是typedef常用语为结构体例
typedef struct GpioMemMap
{
uint32_t PDDR; //Gpio 模块中具体的寄存器分布
.......
uint32_t PDOR;
} volatile * Gpio_MeMMapPtr; //定义了一个Gpio模块寄存器分布结构体,并为其起一个volatile类型的 别名指针变量
(Gpio_MeMMapPtr)0x400ff000u 相当于(volatile * GpioMemMap { …})0x400ff000u;
6、struct,enum与union
struct:结构体类型,在实际问题中常需要分配一整块特定长度的内存空间,当该空间为同一种数据类型时我们常使用数组类型来进行管理,但若该空间内部需要按不同的长度进行分配(如上面例子中Gpio模块内部寄存器地址的分配),最好的方法就是结构体。格式如下
struct 结构体名
{
结构体成员列表
} 结构体变量;
或
typedef struct 结构体名
{
结构体成员列表
} 该结构体类型别名;
**enum:**枚举名,计算机只能进行数据的计算,而日常生活中我们常遇到的问题往往与数字不是直接联系的,需要我们在他们之间建立起一种联系,这种联系我们就可以称为枚举,列
enum week
{
monday = 0, //在monday 和 0之间建立联系 在my_week类型变量中 monday 与0等价 ,赋值不是必须的 默认从0开始赋值
tuesday=1, //赋值不是必须的 , 默认为前一个成员加一值
.....
sunday =6,
} my_week; //枚举变量
my_week = monday;
同样的如果你觉得反复如此定义枚举变量,也可以使用typedef起一个别名。
union:联合体,定义和使用方式与结构体相类似,不同之处在于其内部成员变量共用同一块内存空间。例
typedef union
{
unsigned long DW;
unsigned short W[2];
unsigned char B[4];
}Dtype;
int main ()
{
Dtype a;
a.DW=0x100000000;
printf("a.B[3]=0x%x\n",a.B[3]);
return 0;
}
结果为 a.B[3]=0x10 从而判断改平台为小端模式(高字节位于高地址)
同理访问a.W也可以读写相同内存空间的内容