在C语言的学习初级阶段中,我们接触最频繁的就是关键字了。然而还有很多人不知道C语言到底有多少个关键字、每个关键字的作用等,今天就整理了C语言中的这32个好汉!
序号 | 关键字 | 意义 |
---|---|---|
1 | auto | 声明自动变量,缺省时编译器一般默认为auto |
2 | int | 声明整型变量 |
3 | double | 声明双精度变量 |
4 | long | 声明长整型变量 |
5 | char | 声明字符型变量 |
6 | float | 声明浮点型变量 |
7 | short | 声明短整型变量 |
8 | signed | 声明有符号类型变量 |
9 | unsigned | 声明无符号类型变量 |
10 | struct | 声明结构体变量 |
11 | union | 声明联合数据类型 |
12 | enum | 声明枚举类型 |
13 | static | 声明静态变量 |
14 | switch | 用于开关语句 |
15 | case | 开关语句分支 |
16 | default | 开关语句中的“其他”分支 |
17 | break | 跳出当前循环 |
18 | register | 声明寄存器变量 |
19 | const | 声明只读变量 |
20 | volatile | 说明变量在程序执行中可被隐含地改变 |
21 | typedef | 用以给数据类型取别名(当然还有其他作用) |
22 | return | 子程序返回语句(可以带参数,也可不带参数) |
23 | void | 声明函数无返回值或无参数,声明空类型指针 |
24 | do | 循环语句的循环体 |
25 | while | 循环语句的循环条件 |
26 | if | 条件语句 |
27 | else | 条件语句否定分支(与if 连用) |
28 | for | 一种循环语句(可意会不可言传) |
29 | goto | 无条件跳转语句 |
30 | continue | 结束当前循环,开始下一轮循环 |
31 | extern | 声明变量是在其他文件正声明(也可以看做是引用变量) |
32 | sizeof | 计算对象所占内存空间大小 |
register:
register是最快的关键字,它尽可能把变量存在cpu内部的寄存器里面,所以“近水楼台先得月”,使用时效率也比较高。但使用的时候需要注意:register变量是一个单个的值,它的长度小于等于整型的值。由于变量可能没有存放在内存中,所以我们不可以用&来取其地址!
static:
static的功能主要是修饰函数和修饰变量,不管是修饰变量还是修饰函数,都是限定条件。在修饰函数时,函数的作用域只是本文件中。修饰变量的时候,特别需要注意修饰局部变量这个特点,只能在该函数内部使用,且每次使用完后它的值是不会销毁的,也就是说下次再运行该程序,它的值是上次运行后保存的值。
if、else:
if、else是我们经常使用的关键字,在使用if判断条件是否满足时需要注意几个问题:
1. 布尔与零值进行比较:if(flag)/if(!flag)
2. float与零值比较:设置一个精度范围ESP,if((val>=-ESP)&&(val<=ESP))
3. 指针与零值比较:if(NULL==p)/if(NULL!=p)
do、while、for
在循环中,break结束的是本层循环,而continue结束的是本次循环。
在写循环条件的时候,控制条件尽可能写成“半开半闭”。
嵌套多层循环的时候,尽力长循环在最内层,这样子循环的执行效率高。
void:
我们一般用void来函数返回参数与函数返回值,在这里我想强调的是关于void指针的用法。按照ANSI的标准,我们是不能对void指针进行算法运算的。也就是说:
void * pvoid;
pvoid++; //错误
pvoid += 1; //错误
当函数参数可以是任意类型的指针时,那么我们应该声明参数是void*。比如,典型的如内存操作函数memcpy 和memset 的函数原型分别为:
void * memcpy(void *dest, const void *src, size_t len);
void * memset ( void * buffer, int c, size_t num );
这样,任何类型的指针都可以传入memcpy 和memset 中,这也真实地体现了内存操作函数的意义,因为它操作的对象仅仅是一片内存,而不论这片内存是什么类型。
return:
return一般是用来返回函数值的,在用的过程中,一定要注意return语句不可返回指向栈内存的指针,因为内存在函数结束后会自动销毁。
const:
当函数值不可变、或者是传值时值不可变的情况下,我们一般会使用const来增加程序的健壮性。使用const的时候,需要区分const到底修饰的是谁,起到怎么样的作用。
const int *p; —->p 可变,p 指向的对象不可变
int const *p; —-> p 可变,p 指向的对象不可变
int *const p; —-> p 不可变,p 指向的对象可变
const int *const p; —->指针p 和p 指向的对象都不可变
在判断时候先忽略类型名(编译器解析的时候也是忽略类型名),看const 离哪个近,离谁近就修饰谁。前两个const修饰的是*p,所以p可变,p指向的对象不可变;第三个const修饰的是p,所以p不可变,但p指向的对象可以变;第四个是双重限定,既限制了p,也限制了*p。
struct:
关于结构体的题一般大多数都是问结构体的大小,那么空结构体的大小呢?在《C语言深度剖析》中,作者解释说:
编译器认为任何一种数据类型都有其大小,用它来定义一个变量能够分配确定大小的空间。既然如此,编译器就理所当然的认为任何一个结构体都是有大小的,哪怕这个结构体为空。那万一结构体真的为空,它的大小为什么值比较合适呢?假设结构体内只有一个char 型的数据成员,那其大小为1byte(这里先不考虑内存对齐的情况).也就是说非空结构体类型数据最少需要占一个字节的空间,而空结构体类型数据总不能比最小的非空结构体类型数据所占的空间大吧。这就麻烦了,空结构体的大小既不能为0,也不能大于1,怎么办?定义为0.5个byte?但是内存地址的最小单位是1 个byte,0.5 个byte 怎么处理?解决这个问题的最好办法就是折中,编译器理所当然的认为你构造一个结构体数据类型是用来打包一些数据成员的,而最小的数据成员需要1 个byte,编译器为每个结构体类型数据至少预留1 个byte的空间。所以,空结构体的大小就定位1 个byte。
除了空结构体,柔性数组也应该是我们应该了解的。在C99 中,结构中的最后一个元素允许是未知大小的数组,这就叫做柔性数组成员。鉴于柔性数组的特殊性,sizeof 返回的这种结构大小不包括柔性数组的内存。所以包含柔性数组成员的结构用malloc ()函数进行内存的动态分配,并且分配的内存应该大于结构的大小,以适应柔性数组的预期大小。
union:
在使用union时,有时也会问道和struct类似的问题,比如说union的大小。关于union部分,其实我们更应该关注一下大小端的问题。在存储的时候,有两种存储模式:大端模式和小端模式。
大端模式(Big_endian):字数据的高字节存储在低地址中,而字数据的低字节则存放在高地址中。
小端模式(Little_endian):字数据的高字节存储在高地址中,而字数据的低字节则存放在低地址中。
既然知道了大小端,那么如何判断当前机器是大端存储还是小端存储呢?我们可以通过如下程序来实现问题:
int checkSystem( )
{
union check
{
int i;
char ch;
} c;
c.i = 1;
return (c.ch ==1);
}
说到这里,关键字这32好汉就已经说完了。每个好汉都有自己的特点,平时写程序的时候多留心一点,才能使每个“好汉”都物尽其用!