C语言关键字解析
C语言关键字有32个,如表1所示。虽然简单,但还是让一些初学者犯错,譬如sizeof,好多初学者都认为是函数,其中包括我的一些同学,他们学过有好多年了,如此看来,有时我们追求高深的内容,往往会忽略基础的简单的内容!
表1 C语言标准定义的32个关键字
关键字意义
auto声明自动变量,缺省时编译器一般默认为auto
int声明整型变量
double声明双精度变量
long声明长整型变量
char声明字符型变量
float声明浮点型变量
short声明短整型变量
signed声明有符号类型变量
unsigned声明无符号类型变量
struct声明结构体变量
union声明联合数据类型
enum声明枚举类型
static声明静态变量
switch用于开关语句
case开关语句分支
default开关语句中的“其他”分支
break跳出当前循环
register声明寄存器变量
const声明只读变量
volatile说明变量在程序执行中可被隐含地改变
typedef用以给数据类型取别名(当然还有其他作用)
extern声明变量是在其他文件正声明(也可以看
return子程序返回语句(可以带参数,也可不带
void声明函数无返回值或无参数,声明空类
continue结束当前循环,开始下一轮循环
do循环语句的循环体
while循环语句的循环条件
if条件语句
else条件语句否定分支(与if连用)
for一种循环语句(可意会不可言传)
goto无条件跳转语句
sizeof计算对象所占内存空间大小
在这里我们也不一一讲解每个关键字,只是讲讲比较容易被人忽略的或者易出错的关键字说明一下。
在讲之前,先说明一下,定义和声明的区别:
定义:编译器在创建一个对象时,为该对象申请开辟的内存空间,这个空间的的名字就是变量名或者对象名。同一个变量名在摸个区域内只能定义一次,重复定义会出现错误的。
声明:有两种作用,
1. 告诉编译器,这个变量或者函数,我已经定义了(开辟空间了),但是在别的地方,我先说明一下,免得编译器报错。当然,声明可以多次出现。
2. 告诉编译器,这个变量名或者对象名,我先预定了,其他地方不可以用了。和在饭馆吃饭(人多)要提前预约一样的道理。
定义和声明最本质的区别在于,声明没开辟空间,而定义则创建对象(变量)并开辟了空间。这是最重要的一点。
一. auto,register,extern,static,四种关键字
C语言说明变量的时,同时给出了两个方面的信息:数据类型和存储类。C语言中提供了存储说明符auto,register,extern,static说明的四种存储类别。
1. auto是变量是C语言最常用的。编译器在默认的缺省情况下,所有变量都是auto的。对于函数的形参,也是auto型的。
2. extern,语言中存在这样的一种变量,在一个函数中合法出现的同时,又可以在其他的函数中合法出现,这就是外部变量。它的生命周期是整个程序的执行周期。
3. register,寄存器变量,其值存放在寄存器中,访问速度快。有最快的关键字的称呼。虽然寄存器的速度非常快,但是使用register修饰符也有些限制的:register变量必须是能被CPU寄存器所接受的类型。意味着register变量必须是一个单个的值,并且其长度应小于或等于整型的长度。而且register变量可能不存放在内存中,所以不能用取址运算符“&”来获取register变量的地址。
4. static静态变量,两个作用。第一个作用:修饰变量,都存在内存的静态区。
静态局部变量:出现在函数体内,生命周期是整个程序的执行过程,由于被static修饰的变量总是存在内存的静态区,即使该函数生命结束,其值不会被销毁,同样要修改该变量,就要到函数内部完成,所以用起来比较安全,起到信息屏蔽的作用。
静态全局变量:出现在函数体外,作用域仅限于变量被定义的文件中,其他文件即使用extern声明也没法使用他。
第二个作用,修饰函数,函数前加static使得函数成为静态函数。但此处“static”的含义不是指存储方式,而是指对函数的作用域仅局限于本文件(所以又称内部函数)。使用内部函数的好处是:不同的人编写不同的函数时,不用担心自己定义的函数,是否会与其它文件中的函数同名。
二.基本数据类型----short、int、long、char、float、double
这些关键字都是基本的数据类型,每种类型占据的内存空间不一样,其实每种类型相当于一个模型,再通俗点就是一个模子。具体占多少个字节,可以用关键字sizeof去测一下。另外就就是存储问题。不论是float还是double在存储方式上都遵从IEEE的规范,float遵从的是IEEE R32.24 ,而double遵从的是R64.53。
#include
int main()
{
float a=999.999;
float b=0.005;
printf("%lf/n",a+b-1000.004);
return 0;
}
看看得到的结果并不是0,按数学计算本来应该是0,但由于浮点数的存储有微小的误差从而导致计算结果不是0
double m=3.3;
cout<
你输入3.3,再调试监测m的值你会发现存储的是3.2999999999999998,而不是3.3.这就是本来是3.3而经过存储却变的不是3.3.
对计算机来说,实数是不存在的东西。一般,计算机除了认识“整数”——这个整数和数学中的整数也不是一个东西。
还认识的一种数就是“浮点数”——和数学中的实数更是两码事。
三.最冤枉的关键字sizeof
经常被我们当成函数来使用,虽然造型很像函数,其实是关键字,
int i=0;
A),sizeof(int);B),sizeof(i);C),sizeof int;D),sizeof i;
以上四种写法都是正确的,结果为4.可以在VC6.0编译器watch窗口下观察,前两种写法很常见,但后面的也正确。从c和d正确可以排除sizeof是函数的说法,当然我们在使用的时候还是加上括号,披着函数皮的关键字吧!
请看下面的例子:
struct Test
{int Num;
char*Pc;
short sDtate;
char ch[2];
short S[4];
}*p;
那么这个结构体的大小多少呢?他与共同体的区别很明显,共同体所有成员变量都共用同一块内存,而结构体每个元素都占用相应地内存。
共同体的最大的成员所占内存为共同体占用内存。
int:占4个字节(32系统)。
char*pc:4个:指针相当于地址,地址就是你当前操作系统的位数。如果是指针数组即存放指针的数组,占用的空间是4*数组的个数。
如果是数组指针即指向数组的指针,指针指向的是数组的地址,占用4个字节。
short sDtate:2个
char ch[2]:2个
short S[4]:2*4=8 short型数组
所以一起:4+4+2+2+8=20字节。VC下验证是正确的,不要以为这样就没事了,其实还是有问题的:字节对齐的问题。
struct S1
{
char c;
int i;
};
S1 s1={'a',0xFFFFFFFF};
问sizeof(s1)等于多少?char占1个字节,int占4个字节,那么加起来就应该是5。这样算对吗?VC6中按默认设置得到的结果却是为8。
我们来好好琢磨一下sizeof的定义——sizeof的结果等于对象或者类型所占的内存字节数,好吧,那就让我们来看看S1的内存分配情况:
以我的VC6.0为例,s1的地址为0x0012FF74,使用memory窗口观察,其数据内容如下:
0012FF74: 61 CC CC CC FF FF FF FF
你会发现中间夹杂了3个字节的CC?看看MSDN上的说明:
When applied to a structure type or variable, sizeof returns the actual size,which may include padding bytes inserted for alignment.
原来如此,这就是传说中的字节对齐啊!那么为什么需要字节对齐?计算机组成原理教导我们这样有助于加快计算机的取数速度,否则就得多花指令周期了。各个硬件平台对存储空间的处理上有很大的不同。一些平台对某些特定类型的数据只能从某些特定地址开始存取。其他平台可能没有这种情况, 但是最常见的是如果不按照适合其平台的要求对数据存放进行对齐,会在存取效率上带来损失。这也是空间和时间的博弈。
部分内容参考了陈正冲 《C语言深度解剖》。
还有这个网站上关于字节对齐写得不错,可以看看!
http://blog.csdn.net/xuxinshao/archive/2008/04/02/2244277.aspx