加载中...
从零接触C语言(初览)-IX: 关键字

从零接触C语言(初览)-IX: 关键字

周二 8月 19 2025
3180 字 · 15 分钟

关键字

在介绍C语言变量的命名规则时, 提到过一条规则是: 禁止与关键字冲突

C语言标准中定义了许多的关键字, 这些关键字都具有特定的功能, 所以在变量命名时会明确禁止与关键字冲突, 否则编译器会无法确定该作为变量使用还是关键字使用

C语言标准中存在许多的关键字, 虽然在之前的文章中已经见过不少, 但本篇文章会总览C语言标准中定义的所有关键字

已知关键字

前面的文章中已经见到过很多关键字了

C
// 数据类型相关
void
char
short
int
long
float
double
const        // 定义不可修改的变量
unsigned     // 与数据类型一起使用, 表示无符号
signed         // 与数据类型一起使用, 表示有符号
auto        // 通常不使用, 表示定义自动管理生命周期的变量

// 条件、分支语句相关
if
else
    
switch
case
default
    
// 循环语句相关
for
while
do
continue
break
    
// 函数返回
return
// 取变量、类型等占用空间大小
sizeof
// 跳转到指定标签
goto

上面的关键字, 有三个是没怎么见过的: unsigned signed auto

其中unsignedsigned是相对的, 通常与类型一起使用, 声明数据是无符号数据还是有符号数据, 具体用法暂不过多介绍

auto, 在C语言中则是一个十分不常用的关键字, 他几乎没有什么作用, 他的唯一作用就是 在定义变量时使用, 是用来定义一个由程序自动管理生命周期的变量的, 但即使省略auto, 定义的局部变量也是由程序自动管理生命周期的

上面这些关键字, 本篇文章中不用再过多介绍

本篇文章重点见一见更重要的关键字

extern

extern在首次介绍变量的声明时, 已经简单的见过了

extern是一个非常重要的关键字, 它用于声明一个变量或函数, 并指定其具有外部链接属性

extern的作用只有声明, 他的用法非常简单, 只需要在声明变量或声明函数时, 在前面加一个extern:

C
extern int var1;
extern void func1();

声明, 并不代表这个变量或函数真实存在, 即 并不表示这个变量或函数已经被定义了

如果声明的变量或函数并不存在, 那么就无法使用这个变量, 在编译时会出现报错:

除了用来声明变量或函数之外, extern的另外一个附加功能是: 为声明的变量或函数提供外部链接属性

什么是外部链接属性?

事实上, C语言开发是不仅仅局限于单个代码文件的

C语言允许在多个文件中进行代码编写, 并进行编译

此时, 如果你在文件1中定义了一个全局变量, 但是想要在文件2中进行使用, 那么 就可以用到extern关键字

  1. 文件1: globalVar.c

    C
    int globalVar = 30;
  2. 文件2: main.c

    如果想要在main.c中直接使用globalVar.c中定义的全局变量globalVar, 就需要用到extern

    C
    #include <stdio.h>
    
    extern int globalVar;
    
    int main() {
        printf("globalVar: %d\n", globalVar);
    
        return 0;
    }

从执行结果可以看到, main.c中没有对globalVar执行赋值操作, 打印结果却为30globalVar.c中一致

此时, 这里extern int globalVar;的作用就是, 声明globalVar是一个外部变量, 链接时需要在外部进行寻找

如果, 在main.c中不使用extern对其进行声明:

C
// 尝试直接使用
#include <stdio.h>

int main() {
    printf("globalVar: %d\n", globalVar);

    return 0;
}
C
//  或不用extern 关键字
#include <stdio.h>

int globalVar;

int main() {
    printf("globalVar: %d\n", globalVar);

    return 0;
}

此时, 就会出现问题:

  1. 如果直接使用:

    编译会报错: 变量globalVar在函数第一次使用时, 未声明

  2. 如果不使用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. 限制变量或函数内部链接
  2. 修饰变量, 为变量提供静态属性

功能1: 限制变量或函数内部链接

功能1的作用, 正好与extern的功能相反

static修饰变量或函数, 可以将变量或函数的作用范围 限定到当前文件中, 这个功能与extern是相反的

extern可以将在其他文件中定义的变量或函数, 在本文件中进行声明, 并使用

static则, 限定变量或函数 只能在当前文件中使用, 并且, 无法使用extern声明

依旧是两个文件:

globalVar.c

C
static int globalVar = 30;

main.c

C
#include <stdio.h>

extern int globalVar;

int main() {
    printf("globalVar: %d\n", globalVar);

    return 0;
}

编译的结果为:

编译出现链接报错: main函数中存在未定义的引用globalVar

也就是说, 虽然使用extern int globalVar;尝试声明外部变量, 但globalVar.c文件中定义的globalVar.cstatic修饰了

staticglobalVar变量限定在了globalVar.c文件中, 不能在外部导入

即, static修饰的变量, 被限定在了所在文件中

那么, 如果在两个文件中, 用static修饰并定义同名的变量, 会出现什么情况?

用下面的代码测试一下:

staticTest1.c:

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:

C
#include <stdio.h>
static int globalVar = 2;

void printGlobalVar() {
    printf("file: Test2 globalVar %d, %p\n", globalVar, &globalVar);
}

这段代码, 编译运行的结果为:

我们分别在两个文件中定义了同名的static变量globalVar, 并分别赋值12, 并在两个文件中分别打印globalVar的值和地址

从结果来看, 两个变量相互独立 互不影响, 值不同, 在内存中的地址不同


static也可以修饰函数, 将函数限制在当前文件中

可以在额外进行显示函数声明时使用, 也可以在只进行函数定义时使用, 也可以同时使用:

C
// 显示函数声明 √
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;
}

但是, 不能再额外声明非静态函数之后, 再在函数定义时使用:

C
// 显式声明非静态函数, 但定义静态函数 ×
int add(int num1, int num2);
static int add(int num1, int num2) {
    return num1 + num2;
}

为什么呢?

因为, 显示函数声明实际上是完整地 给编译器说明了 一个函数的返回值类型、函数名以及参数列表, 相当于 给这个函数定制好了”契约”

编译器会按照此声明的契约, 去寻找函数的定义实现, 然后进行编译链接

如果你在声明时, 没有说明这是一个静态函数, 但在定义实现尝试将其实现为静态函数, 编译器就会报错: 函数静态声明位于非静态声明之后

这是C语言标准中禁止的行为, 这说明: C语言, 不能将已经声明为外部链接的标识符改为内部链接

有人可能存在疑问:

C
// 显示函数声明 √
static int add(int num1, int num2);
int add(int num1, int num2) {
 return num1 + num2;
}

为什么这样就可以呢? 这样声明和定义实现也是不一致的啊?

这是因为:

C语言标准中, 如果一个函数先被声明为static, 后续的定义即使没有static, 该函数仍然保持内部链接

这是因为第一次显式声明决定了函数的链接属性

功能2: 修饰变量, 提供静态属性

static还有非常重要的第二个功能: 修饰变量时, 创建静态变量

什么是静态变量?

无论是尝试定义全局变量还是临时变量, 只要被static进行修饰定义, 此变量就被称为静态变量, 不过会区分为 静态全局变量和静态局部变量

静态变量具有的属性, 非常的重要:

  1. static修饰的变量, 生命周期为 程序运行期间

    即使尝试定义临时变量, 但只要使用static进行修饰, 此变量的生命周期就为 整个程序运行期间

    即, 无论是静态全局变量, 还是静态局部变量, 生命周期 均为整个程序的运行期间

  2. static修饰的变量, 作用域为 其所在代码块内

    作用域方面, 静态全局变量和静态局部变量存在一定的不同

    在介绍功能1时, 已经了解过, 静态全局变量的作用域是 其所在的文件内

    而静态局部变量不同, 静态局部变量的作用域是 定义此变量时, 其所在的代码块内

    可以用代码测试一下:

    C
    #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行的相同语句, 并没有报错

  3. static修饰的变量, 在整个程序运行期间, 只会被初始化一次

    从字面来看, 这个属性好像并不容易理解

    但可以结合代码理解:

    C
    #include <stdio.h>
    
    int main() {
        int count = 5;
        while (count--) {
            int staticVal = 10;
            printf("staticVal: %d\n", staticVal++);
        }
    
        return 0;
    }

    C
    #include <stdio.h>
    
    int main() {
        int count = 5;
        while (count--) {
            int val = 10;
            printf("val: %d\n", val++);
        }
    
        return 0;
    }

    对比这两段代码 和 其运行结果

    从结果中可以看到

    1. static修饰的临时变量staticVal

      初始化语句写在while()循环内, 但即使多次循环, 也只初始化了一次

      在循环体内的staticVal++操作, 会对此静态变量随着循环执行累加操作

    2. 没有被static修饰的普通临时变量val

      初始化语句同样写在while()循环内, 但每次循环, 都会初始化一次

      循环体内的val++操作, val总是从10开始

typedef

C语言中, typedef的功能只有一个: 可以为数据类型重新起一个新名字

用法也非常简单:

C
typedef 数据类型 新名字;

使用typedef起的新名字, 可以直接使用

C
#include <stdio.h>

typedef unsigned int uint;

int main() {
    uint val = 10;
    printf("val: %u\n", val);

    return 0;
}

可以看到, uint在C语言标准中是不存在的类型

但使用typedefunsigned int起一个新名字uint, 在之后就可以使用uint定义无符号整型数据

typedef可以在一定程度上提升代码的简洁度, 毕竟uintunsigned int要短的多


C语言还有一些其他的关键字, 各有作用, 但限于篇幅有限, 在之后的文章中再进行介绍


Thanks for reading!

从零接触C语言(初览)-IX: 关键字

周二 8月 19 2025
3180 字 · 15 分钟