C语言中的32个关键字及部分讲解

说起c语言中的关键字,大家印象最深的可能就是int,double等定义一个数据变量时所使用的关键字了。但是除了这些数据类型的关键字还有哪些呢?for?while?没错,这些确实都是,但是并没有一个系统的总结。本文笔者就将就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                        条件语句的否定分支

for                         循环语句

goto                      无条件跳转语句  

sizeof                    计算对象所占内存空间大小


篇幅问题,无法进行挨个讲解,同时也不一定会根据上述顺序进行分析,请见谅。


1.auto

    该关键字通常情况下都可以无视,而且平常它并不是特别常见。(说是不常见,只是我们看不到而已,并非是不常用的意思。)

    C语言作为一门面向过程的语言必然会出现许多的函数块。在每个函数模块中声明的变量即局部变量,也有另一个名字,就叫做:自动变量。  

     例:(在一个函数中)int a = 10;

                等价于

                auto int a = 10;

auto的出现也就意味着当前变量是一个局部变量,会在内存栈上分配,从而更高效的利用内存。


2.register

    被register修饰的变量叫做寄存器变量,顾名思义,该变量是直接存储在CPU的内部寄存器中的,访问速度和内存中的数据必不可同日而语。具体应用场景比如有一个很大的循环,其中有几个需要频繁操作的变量,这时使用register进行修饰将大大提高效率。

    但是即使强如register也是有着自己的限制的,必须是寄存器所接受的类型才能被修饰,即register变量必须是一个单个的值,而且他的长度也要小于等于整形的长度。register变量虽说并不是每时每刻都在寄存器中,最后也是要写入内存的,但是我们也并不能保证某个时刻它就在内存里,所以我们也不能对register变量取地址。


3.static

    静态关键字static在c语言中主要有两个作用,在C++中获得了扩展(第三个作用),不过这不在本文的讨论范围内。


作用一:修饰变量

变量分为局部变量和全局变量,修饰后也就对应两种静态变量。他们都存储在内存的静态区。

    静态局部变量:

    在函数中定义的静态局部变量则只有该函数可以使用,其他函数不得使用。同时由于它和静态全局变量都存在于内存的静态区中而非栈中,所以函数结束之后值也不会销毁,函数下次运行时会继续使用这个值,这是非常重要的特性。

    静态全局变量:

    该种变量的作用域仅限于被定义的文件中(从定义之处开始到当前文件结尾),而且在其他文件中即使使用extern关键字也不能使用。在当前文件中,静态全局变量定义之处之前的代码行也同样不能直接使用它,需要使用extern关键字才能使用。所以一般我们都将静态全局变量定义在文件的顶端。


作用二:修饰函数

在函数前面加上static使得函数成为静态函数。这时该函数的作用域则仅限于本文件中,所以又成为内部函数。这样做的好处是不同的人编写不同的函数时这样不用担心自己的函数与他人编写的重名。


4.基本数据类型-----short int long char float double

需要注意的是在32位系统上,short占2个字节,int和long都是4个字节,char是1个字节,只有double和longlong是8字节。当然在不同系统上也有差异,需要自己用sizeof进行测试。


5.sizeof

首先有一个问题:sizeof是函数还是关键字呢?

有人可能看见sizeof后面加上了一对括号,认为它是函数。其实sizeof(i) 和 sizeof i (竟然可以这样用?)效果是一样的,结果也是一样没有任何问题。那么sizeof(int) 和 sizeof int也是一样的吗?答案是否定的,因为sizeof int 根本就无法通过编译。从前面一个例子我们了解了没有括号也能完成功能说明它并不是一个函数!毫无疑问它是一个关键字,那么sizeof int是什么意思?在int数据类型前加一个关键字,类比下来就好像double int类型的变量一样,这是个什么变量?

所以一般来说,sizeof在计算变量所占空间大小的时候可以去掉括号,但计算类型大小的时候不能省略。在此处省略也只是wile让大家明白它不是一个函数,平常我们使用的时候还是乖乖的把括号写上吧:P


6.signed和unsigned关键字

相信大家平常也没少见这两个关键字,但大多数人对其不甚了解。

他们的中文名字是有符号类型和无符号类型。涉及到变量的正负问题。我们都知道一个变量的最高位是符号位,unsigned类型则是将这个最高位也用于表示数据了,所以unsigned类型都是从0开始到某个正数的范围。signed类型和auto类似,也是平常我们看不到但是默认缺省情况都认为一个变量是signed类型的。


7.goto关键字

建议禁用。虽然它可以灵活跳转,但它明显破坏了结构化的设计风格。


8.void关键字

首先问大家一个问题:在c语言中如果定义一个函数,不给返回类型,例如:

add (int a, int b)

{

    return a+b;

}

这么做的结果是什么呢?返回值是不是void类型?还是说根本无法通过编译呢?

答案是,编译可以通过,但返回值类型并不是你所想的void而是int。


还有另一个问题:定义一个void类型的变量的话,编译器会给他分配多大的内存呢?

如果你接触过OOP语言(例如java c#等)一定对抽象类这个概念不陌生。抽象类能定义一个实例吗?明显不能,因为它强调的是一个“抽象”的概念,那必然是不能存在的,void也是同理,是不允许定义void类型的变量的。


9.const

这个关键字非常重要,有许多人对它的理解都有错误。先来看一句话。

                        被const修饰的变量叫做常量。

这句话对吗?加上const确实不能直接修改它的值了,这不是和常量一样吗?

这么说是不准确的。该值在编译的时候不能被使用,因为这时编译器并不知道它存储的内容。因此和常量还是有小小的区别的,也许叫做只读变量比较好。

具体验证:请看如下代码。

const int MAX = 100;

int Array[MAX];

这串代码在.cpp文件中可以顺利运行,但是在.c文件中不可行。证明了这个MAX它其实还是一个变量,绝非常量。由于c++对const进行了扩展使得在.cpp文件中这些代码成为可行。

前面我们说到它叫做只读变量是因为编译期间它的值不被编译器所知道,这是怎么回事呢?

编译器通常不给只读变量分配存储空间,而是直接将他们保存在符号表里。它就成为了一个编译期间的值,没有了存储和读内存的操作,效率变得很高。

关于const修饰对象的问题

先看下面这些代码:

const int *p;                //p的值可以改变,但是指针p指向的对象不能改变。

int const *p;                //同上

int *const p;                //p的值不可以改变,但是指针p指向的对象可以改变。

const int* const p;      //p的值和p指向的对象都不可改变。

分辨这些情况,只需要首先将数据类型屏蔽(把int不看,编译器解析的时候也是这么做的),我们看const离哪个近他就修饰谁。


10.volatile


和const类似,它也是一种类型修饰符。不过我们可能觉得它很陌生,因为它并不那么常见(其实在linux和c++中还是很常见的)。该关键字表示他所修饰的变量是一个极易被未知因素所改变的,编译器则对访问该代码不再进行优化,从而提供对特殊地址的稳定访问。具体例子如下:

int i = 10;  

int j = i;          //(1)    

int k = i;         //(2)

通常情况编译器会对代码进行优化:在(1)(2)这两条语句中,i并未做左值。此时编译器就认为i的值没有改变,所以在语句(1)将i从内存中取出赋给j之后这个值并未被丢掉,而是直接继续给k赋值了。编译器不会再生成从内存中再次取出i的值的代码,这样就提高了效率,也就是所谓的优化。

接下来再看下面的代码:

volatile i = 10;

int j = i;            //(3)

int k = i;           //(4)

这样的话,(4)语句则会再次从内存中i的地址中读取一次值赋给k。省去了优化的步骤,实现了稳定的访问。


11.extern

extern可以置于变量和函数之前,来表示这个函数或者变量有可能是来自别的文件的,在这里进行了引用(当然有一种特殊情况,就是我们在上面讲过的本文件中在静态全局变量前的代码行若想使用这个变量,也得使用extern来引用)


12.struct

struct即是我们所说的结构体。他将一些相关联的数据打包成一个整体,方便使用。其实它的概念通俗的来说,就像OOP语言中的类一样,只不过我们的结构体里没有函数,只有各种“属性”或者“字段”。:P

(PS:C语言中只能包含数据,C++中可以包含成员函数,和class更加相似。)


说到结构体,最折磨人的就是它的大小问题。首先我们先解决空结构体的大小问题。普通结构体的大小涉及到内存对齐,我将在我的另一篇博客中进行整理。

大小是0吗?

很遗憾,大小是1。如果定义一个空结构体,把它看成一个模子,模子当然不能为0。编译器认为任何一种数据类型都有其大小,同样它也认为任何一个结构体都是有大小的,哪怕它是空的。


13.union关键字

union,即联合体。它和struct非常相像,但是含义却有较大的差别。

我们知道,struct结构体的大小是考虑到其中每个成员后,经过内存对齐而产生的。而union则只是维护足够的空间(成员中大小最大的数据类型)来放置多个数据成员的一种,而并非给每个成员都配置空间。所有的成员都有相同的起始地址。

一个形象的比喻:struct就像是一个大别墅,每个成员都有自己的房子,大家都在里面住也没任何问题;而union就比较可怜了,只有一个房间,大小够成员中最胖的那个住进去,但是同时只能有一个人在里面住。

union主要的用处就是压缩空间。如果一些数据不可能在同一时间被用到,则他们可以写进同一个union里面。


14.enum 枚举类型

一般它的定义如下:

enum enum_type_name

{

    ENUM_CONST_1,

    ENUM_CONST_2,

    ......

    ENUM_CONST_n

}enum_variable_name;

其中里面的例如ENUM_CONST_1就是我们常说的枚举常量。

需要注意的一点是,enum中的常量可以赋值,但是如果你不给它赋值则会从被赋了初值的那个常量开始依次加1.如果没有给赋任何值,则从第一个值开始自动赋0,往后每个变量都自增1.

至于enum的大小,你在使用它的时候它的类型就已经确定了,这时大小你自己心里也就有数了。

enum可以一次定义大量相关的常量,而且使用方便。


15.typedef关键字

有许多人对这个关键字的认知是:定义一个*新的*数据类型。因为type是类型,def是define定义的意思,连在一起不就是定义类型的意思吗?

又是典型的望文生义惨案,看来不只是中国成语,英文单词也有这样的问题啊www

它的真正作用是:给一个已经存在的数据类型(注意,不是变量)取一个别名.它的主要用处就是,取了别名之后我们可以更清楚的一眼看懂这个数据类型的含义。常见的使用场合就是结构体定义的时候。

typedef struct Student

{

    //code..

}stu,*stu_p;

此时结构体Student 就有了别名stu,即

struct Student stu1 == stu stu1;

struct Student* stu2 == stu_p stu2 == stu * stu2;

现在我们顺便回顾一下以前的知识:

const stu_p stu3;

stu_p const stu4;

这两句的const分别修饰的是谁呢?

答案是stu3和stu4.因为我们说了typedef的含义是,给*数据类型*起一个别名。所以stu_p不单单是一个指针,而是一个数据类型。const在判断时会自动忽略数据类型,所以自然它也就被当作不存在啦。



猜你喜欢

转载自blog.csdn.net/czc1997/article/details/80766138