1、最冤枉的关键字---sizeof
经常被人认为是函数,但sizeof是关键字而不是函数;当我们不记得它是关键字时,可以通过以下几个例子来证明:
int i=0;
A)sizeof(int); B)sizeof(i); C)sizeof i; D)sizeof int;
通过编译器调试,我们发现A、B和C的值是4,而D则显示错误;int表示i是一个整型类型,即int是一个模子,前面加sizeof固然不正确。通过这一例子我们可以知道:sizeof关键字在计算变量所占空间大小时,括号可以省略,但计算类型(模子)大小时不能省略。
再来看一段代码:
#include<stdio.h>
int main()
{
short s=20;
int a=10;
printf("%d\n",sizeof(s=a+1));
printf("%d\n",s);
return 0;
}
通过编译器调试时,我们得到以下的结果;不难发现,sizeof内部的表达式没有进行运算,而输出的是s所占空间的大小;故而我们可以得出结论:sizeof在编译期间已经进行了运算;放在sizeof内部的表达式,表达式不进行运算。
2、最易变的关键字---volatile
valatile是一种类型修饰符,用它修饰的变量表示可以被某些编译器未知的因素更改。例如操作系统、硬件或者其他线程,遇到这个关键字声明的变量,编译器对访问该变量的代码就不再进行优化,从而可以提供对特殊地址的稳定访问。
例1:
int i=10;
int j=i;//(1)
int k=j;//(2)
当编译器遇到这两句代码时,会对其进行优化,i没有被赋给左值,此时编译器认为i 的值没有发生改变,所以在(1)语句时从内存中取出i 的值赋给j 之后,这个值并没有被丢掉,而是在(2)语句时继续用这个值给k 赋值。编译器不会生成出汇编代码重新从内存里取i 的值,这样提高了效率。但要注意:(1)、(2)语句之间i 没有被用作左值才行。
例2:
volatile int i=10;
int j=i;//(3)
int k=j;//(4)
当遇到volatile关键字时,表示i是随时变化的,编译器不在对其优化;每次使用i时需从内存中将i的值拿出来,故此编译器生成的汇编代码会重新从i的地址中读取值并将其赋给k,这样一来,保证了i地址的稳定访问。
这样看来,如果i 是一个寄存器变量或者表示一个端口数据或者是多个线程的共享数据,就容易出错,所以说volatile 可以保证对特殊地址的稳定访问。
3、enum关键字
定义方式:
enum enum_type_name -----自定义的一种数据类型
(对一变量取值范围的界定)
{
ENUM_CONST_1,
ENUM_CONST_2,---------------取值范围
...........
ENUM_CONST_n,
}enum_variable_name;------枚举变量
注意:enum_type_name 是自定义的一种数据数据类型名,而enum_variable_name 为enum_type_name类型的一个变量,也就是我们平时常说的枚举变量。实际上enum_type_name类型是对一个变量取值范围的限定,而花括号内是它的取值范围,即enum_type_name 类型的变量enum_variable_name 只能取值为花括号内的任何一个值,如果赋给该类型变量的值不在列表中,则会报错或者警告。ENUM_CONST_1、ENUM_CONST_2、...、ENUM_CONST_n,这些成员都是常量,也就是我们平时所说的枚举常量(常量一般用大写)。
enum变量类型可给常量符号赋值,如果不赋值则会从被赋初值的那个常量开始依次加1,如果都没有赋值,它们的值从0依次累加1。
例:
enum color
{
GREEN=1,
RED,
BLUE,
GREEN_RED=10,
GREEN_BLUE,
}ColorVal;
各常量名代表的数为:RED=2,BLUE=3,GREEN_BLUE=11
下面再看看枚举与#define 宏的区别:
1)#define 宏常量是在预编译阶段进行简单替换。枚举常量则是在编译的时候确定其值。
2)一般在编译器里,可以调试枚举常量,但是不能调试宏常量。
3)枚举可以一次定义大量相关的常量,而#define 宏一次只能定义一个。
4、typdef关键字
给一个已经存在的数据类型(注意:是类型而不是变量)取一个别名,而非定义一个新的数据类型。
在实际项目中,为了方便,可能很多数据类型(尤其是结构体之类的自定义数据类型)需重新定义一个适用实际情况的别名。
typdef struct student
{
//code
}Stu_st,*Stu_pst;
A)Struct student stu1;和Stu_st stu1;没有区别
B)Struct student *stu2;和Stu_pst stu2;Stu_st *stu2;没有区别
上面两种情况可以解释为:将“struct student { /*code*/}”看成一个整体,typedef 就是给“struct student {/*code*/}”取了个别名叫“Stu_st”;同时给“struct student { /*code*/} *”取了个别名叫“Stu_pst”。
与const结合
const Stu_pst stu3;
Stu_pst const stu4;
分析:typdef作用是给一个已经存在的数据类型取别名,即“stu_pst”是struct student的别名,即对于编译器而言,只认为Stu_pst是一个类型名,解析时将“Stu_pst”数据类型名忽略掉,此时const修饰的是“struct student”.