关键字类型 | 关键字列表 |
数据类型的关键字(12个) | char、double、enum、float、int、long、short、signed、struct、union、unsigned、void |
控制语句的关键字(12个) | break、case、continue、default、do、else、for、goto、if、return、switch、while |
存储类型的关键字(4个) | auto、register、static、extern |
其他关键字 (4个) | const、sizeof、typedef、olatile |
1. 数据类型关键字
1.1 简单数据类型的位长
由于运行C语言的平台千差万别,因此K&RC和ANSI C都没有对简单数据类型所应该占据的位长进行严格的规定。
K&RC规定long不能比short短。
ANSI C规定了各种整型数的最小位长,char>=8,short>=16, long>=32,int>=16也可以是32,不同编译器对此的约定可能不一样。
印第安序是CPU存放数据的两种不同顺序,
大端:低地址存放整数的高位字节。
小端:低地址存放整数的低位字节。
大低高,小低低->小弟弟
举例:将0x12345678存放到0x00开始的地址上。
地址 | 0x00 | 0x01 | 0x02 | 0x03 |
大端 | 0x12 | 0x34 | 0x56 | 0x78 |
小端 | 0x78 | 0x56 | 0x34 | 0x12 |
1.2 unsigned和signed关键字
每个简单的数据类型都可以是有符号或者无符号,在C语言用unsigned和signed修饰。
1. 默认情况下,都是有符号的,除了char类型;
2.char在大多数编译器约定是有符号的,ARM编译器下,是无符号数。
1.3 void关键字
1. 用来修饰函数的返回值;(说明该函数没有返回值,因为如果不写函数的返回值类型,则编译器会认为该函数默认返回值为int类型,并返回值随机,为了避免引用这个随机值,就引入void修饰函数的返回值)
2. 用来声明函数的入口函数;(说明该函数没有入口参数,如果这样声明入口函数,则会忽略传入的参数,在C++不能若声明了,则不能传递任何参数,否则编译器会报错)
3. 用来声明空类型指针;(该指针不指向任何类型的数据,仅表示一个内存地址,在需要时强制类型转换)
1.4 struct关键字
(1)位域
struct 位域结构名
{位域列表};
例如:
struct pack{
unsigned a:2;
unsigned b:8;
unsigned c:6;
}pk1, pk2;
把一个字节中的二进制位分为不同的区域,并说明每个区域的位数。一个位域不能跨两个字节。
(2) 结构体内部的成员对齐
不同编译器和处理器对齐方式不同,一般都满足以下3个条件:
1. 结构体变量的首地址能够被最宽成员的大小整除;
2. 结构体成员相对于结构体首地址的偏移量都是成员大小的整数倍,有需要会在成员间填充字节;
3. 结构体的总大小为最宽的成员的整数倍,如有需要在最后一个成员填充字节;
备注:
结构体某个成员相对于结构体首地址的偏移量可以通过宏offsetof()来获得,在 stddef.h 定义;
这里的基本数据类型指的是char、int...,对于复合类型结构体,要寻找结构体内的子成员,在确定了复合类型成员的偏移位置时则是将复合类型作为整体看待。
1.5 union关键字
union data
{
char n;
float f;
};
union data ul = {3}; //只有ul.n被初始化
联合变量的所有成员共享同一片存储区,每个时刻只能保存它的某个成员值
1.6 enum关键字
枚举是一种用于定义一组命名常量的机制,默认第一个值为0,随后的变量依次递增。
(1)当随后的变量有赋值,则下一个变量根据当前变量开始递增
enum color {
RED = 1,
GREEN,
BLUE,
WHITE = 11,
GREY,
BLACK = 15,
};
这时GREEN = 2, BLUE = 3, GREY = 12
(2)用枚举常量作为数组长度
typedef enum{
WHITE,
READ,
BLUE,
YELLOW,
BLACK,
COLOR_NUM
}COLOR;
float BallSize[COLOR_NUM];//当颜色数量发生变化时,只需要在枚举类型定义中增删颜色即可,相比#define更简洁可靠。
2. 控制语句关键字
1. break直接跳出循环,continue跳出本次循环(只能用于循环语句)
2. 不推荐使用goto
3. 存储类型关键字
1. 变量的存储类型是指存储变量值的存储器类型。
2. 变量的存储类型决定了变量何时创建、何时销毁以及其值保存多久。
3. C语言中变量可以存放在三个地方:普通内存、运行时的堆栈、CPU内部的通用寄存器。堆栈也是内存,只是往往用于暂存数据,内容变化频繁。
4. 变量的存储类型首先取决于它的声明位置。
5. 在函数外声明的变量都是全局变量,编译器在编译过程中将全局变量映射在普通内存中,在程序的整个执行期间该变量始终占用编译器为它分配的内存空间,始终保持原值,直至重新赋值或程序结束,所以也称全局变量是静态的。ARM编译器在编译过程中会生成2个全局变量段:RW(有初值)和ZI(无初值)的,链接器将所有C文件的RW和ZI段进行拼接并对其中的全局变量进行重新定位。
3.1 auto关键字
局部变量默认就是auto自动变量,这个变量存储在堆栈或CPU内部的寄存器中,所以没有实际用途。
3.2 register关键字
用于自动变量的声明。提示编译器将register修饰的自动变量存储在CPU内部的通用寄存器,而不是存储器。
注:编译器可能忽略register关键字,自己去分配存储的位置,所以一般也不会用。
3.3 static关键字
该关键字的含义取决于不同上下文。
1. 修饰函数内的局部变量时,会改变局部变量的存储类型,由自动变量变为静态变量,在编译期间分配一个静态的存储空间,不再存储在堆栈或寄存器中。函数可能变得不可重入。
2. 修饰函数时,作用域限制在此C文件中,其他文件不可访问。
3. 修饰全局变量时,作用域限制在此C文件中,其他文件不可访问。
3.4 extern关键字
默认情况下,函数和全局变量作用域只是在定义或声明的C文件内部,如果其他文件想访问必须使用extern。这是因为C编译器是以C文件为单位进行编译的,如果引用了其他文件的就会报错未定义,所以就引入了这个关键字,extern告诉编译器这个函数或变量是在其他文件定义过。两种方式:
1. 在C文件直接用extern声明变量或函数;
2. 在头文件用extern声明变量或函数,然后在其他的C文件使用;推荐!!
4. 其他类型关键字
4.1 const关键字
声明一个不可改变的变量,称为“只读”的变量
const int a;和int const a;没区别 //声明整数变量a是只读的
const int *a; //声明一个指向整数的指针变量,*a指向的整数是只读的
int *const a; //声明一个指向整数的指针变量,a指针变量是只读的
int const * const a; //声明一个指向整数的指针变量,*a和a都是只读的
1. 本质上没改变一个变量的属性;
2. 为给读代码的人传达非常有用的信息,声明一个常量;
3. 给编译器的优化器一些附加的信息,也许能产生更紧凑的代码,比如很多编译器在处理const修饰的变量时,往往会把这些变量分配到ROM的地址;
4. 合理的使用可以使编译器很自然地保护不希望被改变的参数,防止被无意的改掉,当不经意修改时,报错提醒程序员。
5. 只有在定义的时候初始化,后面就没机会了
6. 在函数声明中,const可以修饰形参,表明它是一个输入参数,在函数内不能修改
4.2 sizeof关键字
是一个关键字,不是函数,看传入的值可以看出来sizeof(int)。作用是返回一个对象或者类型所占的内存字节数,有三种使用形式:
sizeof(var); //sizeof(变量);
sizeof(type_name); //sizeof(类型);
sizeof var; //sizeof 变量;
举例:
char *ss = "0123456789";
sizeof(ss); //4 是指向字符串常量的字符指针
sizeof(*ss); //1 *ss是第一个字符
char ss[] = "0123456789";
sizeof(ss); //11 计算到'\0'位置,因此10+1=11
sizeof(*ss); //1 *ss是第一个字符
char ss[100] = "0123456789";
sizeof(ss); //100 开辟内存的大小100*1=100
strlen(ss); //10 '\0'为止之前的长度
int ss[100] = "0123456789";
sizeof(ss); //400 开辟的内存大小100*4=400
strlen(ss); //错误 strlen参数只能是char*且必须'\0'结尾
4.3 typedef关键字
通过typedef定义新的数据类型。
typedef char* ptr_to_char;
char* a;等价于 ptr_to_char a;
4.4 volatile 关键字
直接存取原始内存地址的值。优化器用到这个变量时必须每次小心地重新获取这个变量的值,而不是使用保存在寄存器里的备份。
下面是volatile变量几个例子:
1.并行设备的硬件寄存器(如状态寄存器); (因为每次读写都可能有不同的意义)
比如要初始化寄存器0xff800001,多次读取这个寄存器的值,编译器会优化成只读最后一次
2.一个中断服务子程序中会访问到的非自动变量;
非自动变量指的是全局变量或静态变量
static int i=0;
int main(int argc, char **argv)
{
while(1)
{
if(i)
do_something();
}
}
/* Interrupt service routine */
void ISR_2(void)
{
i=1;
}
3.多线程应用中被几个任务共享的变量;
一个线程读取了这个变量后放到寄存器中,当其他线程修改了这个变量,这个线程再去读取的时候会从寄存器读取,但是值已经改变了
备注:
1.一个参数既可以是const也可以是volatile,只读状态寄存器。
2.一个指针可以是volatile,例如当一个中断服务子程序修改一个指向buffer的指针时。