C语言关键字

来源:互联网 发布:济南企业软件 编辑:程序博客网 时间:2024/05/23 13:04

       C语言有32个关键字,因作为编程语言中的一类语法结构,而具有较为特殊的意义。它预先定义在符号表中,以在编译的第一个步骤词法分析中处理时,区别于一般的标识符。故而,关键字不能作为一般标识符用于命名变量、函数等。通常,关键字用来标记原始数据类型、存储级别,识别跳转结构、循环结构、分支结构等程序结构。

auto      double     int       structbreak     else       long      switchcase      enum       register  typedefchar      extern     return    unionconst     float      short     unsignedcontinue  for        signed    void default   goto       sizeof    volatiledo        if         static    while

1 总结

数据类型关键字

基本数据类型

 

void 

空类型,常用于声明函数返回值、函数参数类型、指针类型

char 

声明字符型变量

int 

声明整型变量

float 

声明单精度浮点型变量。4byte1bit符号位,8bit指数位,23bit尾数位

double 

声明双精度浮点型变量。8byte1bit符号位,11bit指数位,52bit尾数位

聚合数据类型

 

struct 

结构体声明

union 

联合体声明

enum 

枚举类型

类型修饰关键字

 

short 

限定符,修饰整型,short (int)

long 

限定符,修饰整型,long (int)。修饰双精度浮点型 long double

signed 

类型限定符,修饰char类型或整型数据,有符号数据类型

unsigned 

类型限定符,修饰char类型或整型数据,无符号数据类型

存储级别关键字

 

auto 

声明自动变量

static 

声明静态变量

register 

声明寄存器变量

extern 

声明外部变量

const 

volatile合称“cv特性,指定变量不可被当前线程/进程改变(但有可能被系统或其他线程/进程改变)

volatile 

const合称“cv特性,指定变量的值有可能会被系统或其他进程/线程改变,强制编译器每次从内存中取得该变量的值

其他

 

typedef

定义类型别名

sizeof 

计算类型或变量的所占内存空间的大小

流程控制关键字

跳转结构

 

return 

子程序返回语句(可带参数,也可不带参数)

continue 

结束当前循环,开始下一轮循环

break 

跳出当前循环或switch结构

goto 

无条件跳转语句

分支结构

 

if 

条件语句

else 

条件语句否定分支(与最近未匹配的if连用)

switch 

多路判定语句

case 

多路判定语句中的分支标记

default 

多路判定语句其他case分支都不匹配表达式,则执行该分支。可选

循环结构

 

for

for循环结构,for(表达式1;表达式2;表达式3)。表达式1为初始化部分,表达式2为条件部分,表达式3为调整部分。

do 

do while循环结构,do statement while(expression); expression循环条件部分。

while 

while循环结构,while(expression) statement; expression为循环条件

2 应用

    本节主要针对一些关键字做应用解析。

2.1 void

     用途:

     *        对函数返回的限定,用于强调函数没有返回值。如,void func(int x);

     *        对函数参数的限定,用于强调函数没有任何参数。如,int func(void);

     *        指向void的指针。如 void *vp = NULL;

     *        丢掉函数调用的返回值。如, int func(int x);  (void)func(5);

     *        (void)0,(void)1,(void *)0,(void*)0x100000

1) void

       void对象的(不存在的)值不能够以任何方式使用,也不能被显式或隐式地转换为任一非空类型。因为void表达式表示一个不存在的值,这样的表达式只可以用在不需要值的地方,例如:作为一个表达式语句或作为逗号运算符的左操作数。(《C程序设计语言》 附录A.6.7

*  不能被显式或隐式地转为任一非空类型

    int x = 12, y = 0;    y= (int)((void)x);  // 错误:'type cast' : cannot convert from 'void' to 'int'    y= (int)((void)123);//错误: 'type cast' : cannot convert from 'void' to 'int'
*  void 表达式只能用在不需要的地方

int x = 12, y = 0;(void)x;//正确:表达式语句y = ((void)x, 123);//正确:逗号运算符的左操作数
*  定义void变量
void a;//是错误的,因为定义变量时必须分配内存空间,定义void类型变量,编译器不知该分配多大的内存空间。
*  将表达式强制转换为void类型
define min(x,y) ({ \   typeof(x) _x = (x);    \   typeof(y) _y = (y);    \   (void) (&_x == &_y);  \   _x < _y ? _x : _y; })

    (void)(&_x == &_y);其中&_x == &_y是关系表达式,如果关系为真,则表达式的结果值为数值1;如果为假,则结果值为数值0

故而,(void)(&_x == &_y);等价于(void)0;(void)1;

int func(int x);void func(5);

(void)func(5);在表达式语句中一个空的强制类型转换将丢掉函数调用的返回值。

*  (void)0

标准库头文件:assert.h#define assert(test) ((test) ? (void)0 : _Assert(__FILE__“:”__STR(__LINE__)”” #test))

     (void)0的作用相当于函数返回类型为void限定,它仅是一个占位符表达式,什么都不做。

2) 指向void的指针

   指向任何类型的指针都可以转为void *类型,且不会丢失信息。如果将结果再转换为初始指针类型,则可以恢复初始指针。

    void *vp;    char *cp;    vp = cp; /*指向任何类型的指针都可以转为void *类型,且无需强制转换*/    cp = (char *)vp;

*  #define NULL (void*)0

    定义NULL为指向内存位置为0的空指针。

   NULL表示内存位置0NULL指针并不指向任何对象。因此除非是用于赋值或比较运算,出于其他任何目的使用NULL指针都是非法的

将整型数值转为指针

   #define BUFFER_END 0x200000   void * b = (void *) BUFFER_END;   if (b == (void *) 0x100000)       b = (void *) 0xA0000;

   指向特定类型的指针可以进行算术运算(+-)和关系运算(==!=<>>=<=),空类型指针不可以。


2.2 typedef

    用途:为类型定义新名字

    *    为基本数据类型定义新名字

    *    为指针类型定义新名字

    *    为数组类型定义新名字

    *    为结构类型定义新名字

    *    为联合类型定义新名字

    *    为枚举类型定义新名字

    *    为函数类型定义新名字


1)基本数据类型

typedef int  int32_t;typedef char int8_t;

2)指针

typedef int  *iptype;typedef char *cptype;

3)数组

  类型说明符 数组名[常量表达式];

int array[5];//定义array为含有5个元素的整型数组变量typedef int array[5];//定义array为含有5个整型元素的数组类型名

4)结构

typedef struct sr_bsc{    char a;    int b;}sr_bsc_t;

5)联合

typedef union un_bsc{    char a;    int b;}un_bsc_u;

6)枚举

typedef enum en_bsc{    LOADED = 0,    UNLOADED,         }en_bsc_e;

7)函数

定义返回值为int,参数为void*,struct scatterlist *,int,int的函数类型

typedef int (vb_map_sg_t)(void *dev,struct scatterlist *sglist,int nr_pages, int direction);vb_map_sg_t *vb_unmap_sg;

定义返回值为int,参数为void*,struct scatterlist *,int,int的函数指针类型

typedef int (vb_map_sg_t*)(void *dev,struct scatterlist *sglist,int nr_pages, int direction);vb_map_sg_t vb_unmap_sg;

2.3 sizeof

        定义:sizeof的结果等于对象或者类型所占的内存字节数。

        用途:

        *        用于计算机变量所占的内存字节数(变量会被替换成类型)

        *        用于计算类型所占的内存字节数

        特点:sizeof是在编译阶段处理,且不能被编译为机器码。

        int a = 0, b = 0;        b = sizeof(a = 3);/*32位系统下,b = 4,因为a是int类型*/

        缺陷:C99标准规定,函数、不能确定类型的表达式以及位域成员不能被计算sizeof值。

        sizeof(函数)的值等价于sizeof(函数的返回值类型)        int func1()        {            return 0;        }        double func2()        {            return 0.0;        }        sizeof(func1)的值为等价于sizeof(int)        sizeof(func2)的值为等价于sizeof(double)
*   sizeof (变量)
    32位系统环境下,    int a;    sizeof(a)的值为4    sizeof(int)的值为4

*   sizeof (数组)

    char str[20] = “abcdefg”;    sizeof(str)的值为20

*   sizoef(结构体)

sizeof(结构体)涉及到字节对齐问题,字节对齐和编译器实现相关,但一般而言,需满足三个准则:1) 结构体变量的首地址能够被其最宽基本类型成员的大小所整除;2) 结构体每个成员相对于结构体首地址的偏移量(offset)都是成员大小的整数倍,如有需要编译器会在成员之间加上填充字节(internal adding);3) 结构体的总大小为结构体最宽基本类型成员大小的整数倍,如有需要编译器会在最末一个成员之后加上填充字节(trailing padding)。

系统禁止编译器在一个结构的起始位置跳过几个字节来满足边界对齐要求,因此所有结构的起始存储位置必须是结构中边界要求最严格的数据类型所要求的位置。

*   结构体中都是基本数据类型:typedef struct {    char ch1;    int i;}s1;sizeof(s1)的值为8。结构类型s1里的最宽基本类型成员i的大小是4byte,所以成员ch1必须存储于一个能够被4整除的地址,sizeof(s1)的大小也必须能被4整除。下一个成员i是整型,所以需要在跳过3个字节的位置存储。*   结构体中包含聚合类型:当结构体中包含聚合类型(比如结构体),在寻找最宽基本类型成员时,应当包括聚合类型成员的子成员,而不是把聚合成员看成是一个整体。但在确定聚合类型成员的偏移位置时则是将复合类型作为整体看待。typedef struct{    char ch2;    s1 s;    char ch3;}s2;sizeof(s2)的值为16。结构类型s2中包含一个结构体s,将其展开来看,s2中的最宽基本类型成员为i,其大小是4byte。所以成员ch2必须存储于一个能够被4整除的地址,sizeof(s2)的大小也必须能被4整除。下一个成员s作为一个结构体也需要满足上面的三个原则,故而其里面的成员ch1的存储地址也必须能够被4整除。所以s存放于ch2后跳过三个字节的位置。
        之所以需要字节对齐,主要是为了加快CPU的取数速度,否则就得多花指令周期。但GCC编译器中有两个属性会影响结构体的字节对齐问题,从而影响到sizeof(结构体)的取值。

    __attribute((aligned (n))),让所作用的结构成员对齐在n字节自然边界上。如果结构中有成员的长度大于n,则按照最大成员的长度来对齐。

__attribute__ ((packed)),取消结构在编译过程中的优化对齐,按照实际占用字节数进行对齐。

typedef struct {    char ch1;    int i;}__attribute__((packed)) s1;sizeof(s1)的值为5typedef struct{    char ch2;    s1 s;    char ch3;}__attribute__((packed)) s2;sizeof(s2)的值为7

2.4 goto

        用途:

        *       用于错误处理

        *       用于跳出深度嵌套的循环结构

    int func(int x, int y)    {        …        if (x > y)        {            …            goto err;        }    err:        …    }


    int func(int x, int y)    {        for (…)           for (… )              for (…)                 goto err;    err:        …}

2.5 auto

         在代码块内部声明的变量的缺省存储类型是自动的(automatic),它存储于堆栈中,称为自动变量。

    int func(void)    {        auto int a;/*a为自动变量*/        int b;     /*b缺省为自动变量类型*/        …    }

2.6 static

    用途

    *   用于修饰存储类型则为静态存储类型。

    *   用于修饰链接属性则为内部链接属性。


1)  用于代码块内部变量声明,static用于修改变量的存储类型——静态局部变量

     在函数里定义,只能在函数内使用,同一文件的其他函数不能使用。static修饰的变量存在内存的静态区,所以即使这个函数运行结束,这个静态变量的值是不会被销毁,函数下次使用时仍然能用到这个值。

int func(void){    static int a;/*a是静态变量*/    int b;/*b是自动变量*/    …}

函数的形参不能声明为static,因为实参总是在堆栈中传递给函数,用于支持递归。

2)  用于函数定义或代码块之外变量声明,static用于修改标识符的链接属性,从external改为internal

2.1)用于代码块外变量声明,则该变量为静态全局变量。

    static int a;/*静态全局变量,作用域仅限于该变量被定义的文件中,其他文件不能使用*/    int b;       /*全局变量,其他文件可通过extern引用*/    int func(void)    {        …    }

2.2)用于函数定义,static 修饰的函数只能在声明它的源文件中访问

    static int func(void)    {        …    }

2.7 register

    用于将自动变量声明为寄存器变量,提示它们应该存储于机器的硬件寄存器而不是内存中。

*    寄存器变量比存储于内存的变量访问起来效率更高,但编译器不一定要理睬register关键字,如果有太多的变量被声明为register,它只能选取几个实际存储于寄存器中,其余的按普通自动变量处理。

*   寄存器变量的创建和销毁时间和自动变量相同,但它需要一些额外的工作。在一个使用寄存器变量的函数返回之前,这些寄存器先前存储的值必须回复,确保调用者的寄存器变量未被破坏。许多机器使用运行时堆栈来完成这个任务。当函数开始执行时,它把需要使用的所有寄存器的内容保存到堆栈中,当函数返回时,这些值在复制回寄存器中。

*   寄存器变量必须是能被CPU寄存器所接受的类型。这意味着register变量必须是一个单个的值,并且其长度应小于或等于整型的长度。

*   寄存器变量可能不存放在内存中,所以不能用取址运算符&来获取register变量的地址。

register声明只适用于自动变量以及函数的形式参数。

void func(void){    register unsigned char st;    …    register int lcount;    register char *lptr;    …    for(lcount=virtual_dma_count, lptr=virtual_dma_addr; lcount; lcount--, lptr++)    {       …    }}


func(register unsigned m, register long n){    register int i;    ...}

2.8 extern

        外部链接属性。

*      用于修饰变量

*      用于修饰函数

       当用于修饰变量或函数时,表明后面即将用到的变量或函数都是在别的文件定义,提示编译器在其他模块中寻找定义。

    a.c文件                                        b.c文件    int i = 10;                                   extern int Ii    void fun(void)                                extern void fun(void);    {        …    }

*  extern "C"

      extern "C"是C++的特性,主要是为了解决C++在调用C函数库时,直接使用函数名,而不是使用一个经过处理的函数名。

#ifdef __cplusplus             /*__cplusplus是在CPP文件自定义的宏,extern "C"提示C++编译器将其{}包含的函数使用C语言的方式进行编译*/extern "C"{#endifextern int func(int a, int b);#ifdef __cplusplus}#endif

    G++编译时生成函数名称的方式与GCC不同。G++在编译C++语言时,为了解决函数的多态问题,不会直接使用代码中的函数名称,而是经过“名称的特殊处理”生成一个全局唯一的函数名称。GCC在编译C语言时,则直接使用代码中的函数名称。如此,当在C++中调用由C写成的函数库时,此时C函数库的函数名称没变,而C++却用变换过的函数名称来调用,则编译过程中就会发生编译器找不到C函数的问题,从而导致链接失败。为解决这种情况,就引用了extern “C”的方法。extern “C”的代码在处理函数名称时,直接使用函数的名称,不采用特别的方法生成一个中间函数名称。所以C++函数在使用C函数时,加上extern “C”才能正确的找到指定的函数

2.9 const

   volatile合称“cv特性,指定变量不可被当前线程/进程改变(但有可能被系统或其他线程/进程改变)

1)常变量

const int a  //正确,变量a的值不能改变:int const a; //正确,变量a的值不能改变

由于a的无法被修改,所以一旦声明完就无法再赋值。那该如何让a一开始就拥有一个值:

方法1:在声明时初始化

const int a = 10;

方法2:在函数中声明为const的形参在函数被调用时会得到实参的值。

int func(const int &a){    a = 12;//错误    ...}func(10); 形参a被赋值为10,且不能在改变

2)指针

int *pi;  //pi是一个普通的指向整型的指针int const *pci; //pci是一个指向整型常量的指针。可以修改指针的值,但不能修改指针所指向的值int *const cpi; //cpi是一个指向整型的常量指针。可以修改指向指向的整型值,但不能修改指针的值int const *  const cpci; //cpci是一个指向整型常量的常量指针。指针的值和指针所指向的值都不能修改。

2.10 volatile

   const合称“cv特性,指定变量的值有可能会被系统、硬件或其他进程/线程改变,强制编译器每次从内存中取得该变量的值

          当编译器遇到这个关键字声明的变量时,对访问该变量的代码不再优化,每次都从这个这个变量的内存中获取其值。

*

int i=10;int j = i;//(1)语句int k = i;//(2)语句

    这时候编译器对代码进行优化,因为在(1)、(2)两条语句中,i没有被用作左值。这时候编译器认为i的值没有发生改变,所以在(1)语句时从内存中取出i的值赋给j之后,这个值并没有被丢掉,而是在(2)语句时继续用这个值给k赋值。编译器不会生成出汇编代码重新从内存里取i的值,这样提高了效率。但要注意:(1)、(2)语句之间i没有被用作左值才行。

*

volatile int i=10;int j = i;//(3)语句int k = i;//(4)语句

    volatile关键字告诉编译器i是随时可能发生变化的,每次使用它的时候必须从内存中取出i的值,而编译器生成的汇编代码会重新从i的地址处读取数据放在k中。





1 0
原创粉丝点击