TIP如果你从未接触过 C 语言, 那么我建议你先阅读前面的文章:
关键字
在介绍 C 语言变量的命名规则 时, 提到过一条规则是: 禁止与关键字冲突
C 语言标准中定义了许多的关键字, 这些关键字都具有特定的功能, 所以在变量命名时会明确禁止与关键字冲突, 否则编译器会无法确定该作为变量使用还是关键字使用
C 语言标准中存在许多的关键字, 虽然在之前的文章中已经见过不少, 但本篇文章会总览 C 语言标准中定义的所有关键字
已知关键字
前面的文章中已经见到过很多关键字了
// 数据类型相关voidcharshortintlongfloatdoubleconst // 定义不可修改的变量unsigned // 与数据类型一起使用, 表示无符号signed // 与数据类型一起使用, 表示有符号auto // 通常不使用, 表示定义自动管理生命周期的变量
// 条件、分支语句相关ifelse
switchcasedefault
// 循环语句相关forwhiledocontinuebreak
// 函数返回return// 取变量、类型等占用空间大小sizeof// 跳转到指定标签goto上面的关键字, 有三个是没怎么见过的: unsigned signed auto
其中unsigned和signed是相对的, 通常与类型一起使用, 声明数据是无符号数据还是有符号数据, 具体用法暂不过多介绍
而auto, 在 C 语言中则是一个十分不常用的关键字, 他几乎没有什么作用, 他的唯一作用就是 在定义变量时使用, 是用来定义一个由程序自动管理生命周期的变量的, 但即使省略auto, 定义的局部变量也是由程序自动管理生命周期的
上面这些关键字, 本篇文章中不用再过多介绍
本篇文章重点见一见更重要的关键字
extern
extern在首次介绍 变量的声明 时, 已经简单的见过了
extern是一个非常重要的关键字, 它用于声明一个变量或函数, 并指定其具有外部链接属性
extern的作用只有声明, 他的用法非常简单, 只需要在声明变量或声明函数时, 在前面加一个extern:
extern int var1;extern void func1();声明, 并不代表这个变量或函数真实存在, 即 并不表示这个变量或函数已经被定义了
如果声明的变量或函数并不存在, 那么就无法使用这个变量, 在编译时会出现报错:

除了用来声明变量或函数之外, extern的另外一个附加功能是: 为声明的变量或函数提供外部链接属性
什么是外部链接属性?
事实上, C 语言开发是不仅仅局限于单个代码文件的
C 语言允许在多个文件中进行代码编写, 并进行编译
此时, 如果你在文件 1 中定义了一个全局变量, 但是想要在文件 2 中进行使用, 那么 就可以用到extern关键字
-
文件 1:
globalVar.cint globalVar = 30; -
文件 2:
main.c如果想要在
main.c中直接使用globalVar.c中定义的全局变量globalVar, 就需要用到extern#include <stdio.h>extern int globalVar;int main() {printf("globalVar: %d\n", globalVar);return 0;}

从执行结果可以看到, main.c中没有对globalVar执行赋值操作, 打印结果却为30与globalVar.c中一致
此时, 这里extern int globalVar;的作用就是, 声明globalVar是一个外部变量, 链接时需要在外部进行寻找
如果, 在main.c中不使用extern对其进行声明:
// 尝试直接使用#include <stdio.h>
int main() { printf("globalVar: %d\n", globalVar);
return 0;}// 或不用extern 关键字#include <stdio.h>
int globalVar;
int main() { printf("globalVar: %d\n", globalVar);
return 0;}此时, 就会出现问题:
-
如果直接使用:

编译会报错: 变量
globalVar在函数第一次使用时, 未声明 -
如果不使用
extern关键字:
编译时, 会出现另外一个问题: 链接出现错误, 变量
globalVar多次定义, 无法确定链接即,
globalVar变量定义了两次, 这是不允许的
函数也是相似的, 函数也可以在其他文件中定义, 但是在使用之前, 一定要先声明函数
事实上, 无论是函数还是变量, 在使用之前都需要先进行声明, 只不过一般来说定义时声明动作也被同时完成了
但 如果你使用变量或函数的地方, 在变量或函数定义之前, 其实使用的变量或函数可以看作未声明
函数声明通常直接写函数名就可以:
int add(int num1, int num2);其实完整的写法应该是
extern int add(int num1, int num2);, 只是extern可以被省略
static**
C 语言中还有一个非常重要的关键字: static, 非常非常非常重要
static可以用来修饰变量或函数, 在变量或函数声明时, 用static修饰的变量或函数 被称为静态变量或静态函数
C 语言中static具体的功能有两个:
- 限制变量或函数内部链接
- 修饰变量, 为变量提供静态属性
功能 1: 限制变量或函数内部链接
功能 1 的作用, 正好与extern的功能相反
static修饰变量或函数, 可以将变量或函数的作用范围 限定到当前文件中, 这个功能与extern是相反的
extern可以将在其他文件中定义的变量或函数, 在本文件中进行声明, 并使用
而static则, 限定变量或函数 只能在当前文件中使用, 并且, 无法使用extern声明
依旧是两个文件:
globalVar.c
static int globalVar = 30;main.c
#include <stdio.h>
extern int globalVar;
int main() { printf("globalVar: %d\n", globalVar);
return 0;}编译的结果为:

编译出现链接报错: main函数中存在未定义的引用globalVar
也就是说, 虽然使用extern int globalVar;尝试声明外部变量, 但globalVar.c文件中定义的globalVar.c被static修饰了
static将globalVar变量限定在了globalVar.c文件中, 不能在外部导入
即, 被static修饰的变量, 被限定在了所在文件中
那么, 如果在两个文件中, 用static修饰并定义同名的变量, 会出现什么情况?
用下面的代码测试一下:
staticTest1.c:
#include <stdio.h>
extern void printGlobalVar();
static int globalVar = 1;
int main() { printf("file: Test1 globalVar %d, %p\n", globalVar, &globalVar); printGlobalVar();
return 0;}staticTest2.c:
#include <stdio.h>static int globalVar = 2;
void printGlobalVar() { printf("file: Test2 globalVar %d, %p\n", globalVar, &globalVar);}这段代码, 编译运行的结果为:

我们分别在两个文件中定义了同名的static变量globalVar, 并分别赋值1和2, 并在两个文件中分别打印globalVar的值和地址
从结果来看, 两个变量相互独立 互不影响, 值不同, 在内存中的地址不同
static也可以修饰函数, 将函数限制在当前文件中
可以在额外进行显示函数声明时使用, 也可以在只进行函数定义时使用, 也可以同时使用:
// 显示函数声明 √static int add(int num1, int num2);int add(int num1, int num2) { return num1 + num2;}
// 只进行函数定义 √static int add(int num1, int num2) { return num1 + num2;}
// 同时 √static int add(int num1, int num2);static int add(int num1, int num2) { return num1 + num2;}但是, 不能再额外声明非静态函数之后, 再在函数定义时使用:
// 显式声明非静态函数, 但定义静态函数 ×int add(int num1, int num2);static int add(int num1, int num2) { return num1 + num2;}
为什么呢?
因为, 显示函数声明实际上是完整地 给编译器说明了 一个函数的返回值类型、函数名以及参数列表, 相当于 给这个函数定制好了”契约”
编译器会按照此声明的契约, 去寻找函数的定义实现, 然后进行编译链接
如果你在声明时, 没有说明这是一个静态函数, 但在定义实现尝试将其实现为静态函数, 编译器就会报错: 函数静态声明位于非静态声明之后
这是 C 语言标准中禁止的行为, 这说明: C 语言, 不能将已经声明为外部链接的标识符改为内部链接
有人可能存在疑问:
// 显示函数声明 √static int add(int num1, int num2);int add(int num1, int num2) {return num1 + num2;}为什么这样就可以呢? 这样声明和定义实现也是不一致的啊?
这是因为:
C 语言标准中, 如果一个函数先被声明为
static, 后续的定义即使没有static, 该函数仍然保持内部链接这是因为第一次显式声明决定了函数的链接属性
WARNING关键字
static和extern不能一起使用
功能 2: 修饰变量, 提供静态属性
static还有非常重要的第二个功能: 修饰变量时, 创建静态变量
什么是静态变量?
无论是尝试定义全局变量还是临时变量, 只要被static进行修饰定义, 此变量就被称为静态变量, 不过会区分为 静态全局变量和静态局部变量
静态变量具有的属性, 非常的重要:
-
被
static修饰的变量, 生命周期为 程序运行期间即使尝试定义临时变量, 但只要使用
static进行修饰, 此变量的生命周期就为 整个程序运行期间即, 无论是静态全局变量, 还是静态局部变量, 生命周期 均为整个程序的运行期间
-
被
static修饰的变量, 作用域为 其所在代码块内作用域方面, 静态全局变量和静态局部变量存在一定的不同
在介绍功能 1 时, 已经了解过, 静态全局变量的作用域是 其所在的文件内
而静态局部变量不同, 静态局部变量的作用域是 定义此变量时, 其所在的代码块内
可以用代码测试一下:
#include <stdio.h>int main() {{static int globalVar = 1;printf("file: Test1 globalVar %d, %p\n", globalVar, &globalVar);}printf("file: Test1 globalVar %d, %p\n", globalVar, &globalVar);return 0;}
从编译结果来看, 第
8行的printf("file: Test1 globalVar %d, %p\n", globalVar, &globalVar);提示globalVar未定义而第
6行的相同语句, 并没有报错 -
被
static修饰的变量, 在整个程序运行期间, 只会被初始化一次从字面来看, 这个属性好像并不容易理解
但可以结合代码理解:
#include <stdio.h>int main() {int count = 5;while (count--) {int staticVal = 10;printf("staticVal: %d\n", staticVal++);}return 0;}
#include <stdio.h>int main() {int count = 5;while (count--) {int val = 10;printf("val: %d\n", val++);}return 0;}
对比这两段代码 和 其运行结果
从结果中可以看到
-
被
static修饰的临时变量staticVal初始化语句写在
while()循环内, 但即使多次循环, 也只初始化了一次在循环体内的
staticVal++操作, 会对此静态变量随着循环执行累加操作 -
没有被
static修饰的普通临时变量val初始化语句同样写在
while()循环内, 但每次循环, 都会初始化一次循环体内的
val++操作,val总是从10开始
-
typedef
C 语言中, typedef的功能只有一个: 可以为数据类型重新起一个新名字
用法也非常简单:
typedef 数据类型 新名字;使用typedef起的新名字, 可以直接使用
#include <stdio.h>
typedef unsigned int uint;
int main() { uint val = 10; printf("val: %u\n", val);
return 0;}
可以看到, uint在 C 语言标准中是不存在的类型
但使用typedef对unsigned int起一个新名字uint, 在之后就可以使用uint定义无符号整型数据
typedef可以在一定程度上提升代码的简洁度, 毕竟uint比unsigned int要短的多
C 语言还有一些其他的关键字, 各有作用, 但限于篇幅有限, 在之后的文章中再进行介绍