暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

一文讲解C语言关键字

海人为记 2021-12-21
1681

C 语言中的标识符可以使用下划线、字母和数字组成,但也有一些词汇不能当作标识符,即关键字。经过多年的发展,C 语言到现在已经有了 40 多个关键字,如下图所示。

C 语言在 C90 版本,总共有 32
个标识符作为关键字,C99 增加了 5
个关键字,C11 又增加了 7
个关键字,这些关键字皆不能用于其他用途。根据关键字的作用可以将关键字进行分类,如用于表示数据类型的、用于流程控制的等等。

数据类型

C 语言有一些关键字用来表示数据类型,告知编译器声明的变量是存储整型还是字母等数据。

C语言中的数据类型都是通过上面的关键字来表示,如 int
表示缺省的整型类型,省略了 signed
修饰符,可表示的最小取值范围为 -32767~32767
,当使用 unsigned
修饰 int
时,就只能表示非负值了。

short
long
也是用于修饰 int
的关键字,修饰后所占用的存储空间会不同,如 short int
占用 2 字节,long int
占用 4 字节。

float
double
用来声明浮点数,并且使用 long
修饰 double
后,可以存储比 double
更高的精度。

C99标准新增了三个关于数据类型的关键字, _Bool
类型表示布尔值,即 true
表示 1,, false
表示 0。_Complex
_Imaginary
分别表示复数和虚数。

除此之外,还有 struct
用于声明结构体,union
声明联合,enum
来声明枚举,void
在没有可用的值时使用,typedef
可以对上述数据类型声明类型别名,sizeof
用来计算类型或类型变量所占字节数大小。

存储类型

C语言通过存储类型来指定变量存储的内存类型、生命周期、和作用域。总共提供了 5 种关键字来表示,其中有一个是在C11标准中新增的。

auto
指定为变量,在块作用域内使用,但通常省略;当想要将变量存储到寄存器中时,可以使用 register
关键字来修饰;当想要变量在整个程序执行过程中一直存在,就使用 static
extern
关键字用来指定外部变量,即在其他文件中定义,要在本文件中引用。C11又新增了 _Thread_local
关键字来修饰具有线程周期的线程局部变量,可将线程之间数据分开,避免对象在共享中被竞争。

限定类型

C语言中有四个关键字表示限定类型,C90标准加入的 const
volatile
,C99标准加入的 restrict
和 C11 标准加入的 _Atomic

const
在修饰指针时,共有三种情况,第一种情况写法是 const int *
 或 int const *

int a = 10;
const int *p = &a;
*p = 20;  // error: assignment of read-only location '*p'

*p = 20
会报错,这是因为 p
指向的值不能被改变,但可以通过 p
的引用来改变变量的值。

int a = 10;
const int *p = &a;
a = 20;

除了改变引用外,还可以改变指针本身的值。

int a = 10;
const int *p = &a;
int b = 20;
p = &b;

第二种写法是 float * const
,该写法的指针必须指向同一个地址,但所指向的值可以改变。

int a = 10;
int * const p = &a;
*p = 20;

第三种写法是 const int * const
,表明指针不能指向别处,所指向的值也不能更改。

int a = 10;
const int * const p;
int b = 20;
p = &b;  // error: assignment of read-only variable 'p'

但还是可以通过改变 a
的值来改变。

int a = 10;
const int * const p;
a = 20;

流程控制

流程控制是决定程序运行的关键,其主要有顺序控制、分支控制和循环控制,通过 C 语言提供的关键字来控制,如下图所示。

实现循环结构可以使用 for
while
do
三个关键字来实现,分支结构则使用 if
else
switch
case
default
来实现,并且在循环或分支中得到想要的结果后,想要从流程中跳转,就可以用上 break
continue
return
goto
了,当然,goto
最好少使用,它会让程序变得复杂,难以维护。

其它关键字

除以上介绍的关键字外,还有一些其他作用的关键字如下图所示。

函数调用过程包括建立调用、传递参数、跳转到函数代码并返回。使用宏定义使代码内联,可避免函数调用的开销,而 C99 标准提供的 inline
关键字可用来定义内联函数。

inline static int sum(int a, int b) {    // 内联函数
    return a + b;
}
int main() {
    ...
    sum(1020);    // 函数调用
    ...
    return 0;
}

编译器可能会用 sum
中的代码替换函数调用,并可能会执行一些优化。

_Noreturn
修饰的函数不会将控制权交还给主调函数,以免滥用,并通知编译器可优化一些代码。

_Noreturn void failed() {
    printf("failed\n");
}

int main() {

    printf("--start--\n");
    failed();
    printf("--end--\n");
    return 0;
}
/*
--start--
failed
*/


C语言本身是不支持泛型编程的,但C11标准新增 _Generic
关键字实现一个泛型表达式来实现泛型编程。

_Generic(exp. type1:exp1, type2: exp2,···, default:expn)

_Generic
关键字括号中的第 1 项是表达式,后面每个项都由 type: exp
组成。type
用于匹配类型,exp
为匹配后的值。

#define TYPE(v) _Generic((v), \
    int:"int", \
    char: "char", \
    float: "float", \
    double: "double", \
    default: "other"\
)

int main() {
    int a = 5;
    printf("%s\n", TYPE(a));    // int
    printf("%s\n", TYPE(&a));    // other
    return 0;
}

C11标准加入的 _Static_assert
关键字与断言 assert()
类似,但 assert()
是在运行时检查,而 _Static_assert
是在编译时检查,检查不通过,程序就无法通过编译。

int main() {
    _Static_assert(sizeof(int) == 2"16 bit is not supported");
    return 0;
}
/*
error: static assertion failed: "16 bit is not supported"
*/





文章转载自海人为记,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论