C语言基础教程:Clang的安装与环境变量配置

去年两次参加软考高级架构师考试,都是在某个老牌顶级名校,在他们的书店里发现很多教材用的还是VC98之类的古董。这玩意儿在我们当年学的时候就已经显得陈旧了。

前些年就开始教授基础的编程入门课程,既教Python,也教C语言基础知识。我教学时一般使用苏小红老师的教材,其中有一些章节是孙志岗老师编写的,他们采用的是Code Blocks,这个工具也还不错,可以安装集成GCC的版本,网上资料也很丰富。

但从今年开始,我考虑换一个思路。今年准备采用Clang,Clang是LLVM项目提供的现代化C/C++/Objective-C编译器,以编译快、内存省、报错清晰著称。Clang可以搭配Void/Trae/Qoder之类的现代编辑器,以及之前我们介绍过的OpenCode和Ollama,让同学们能够直接使用当前最前沿的工具链。

下面就简单介绍一下如何安装Clang以及配置环境变量。

Windows 系统下使用 Winget 安装

如果你使用的是Windows系统,建议尽量多用Winget,而不要去网站下载安装包,这样既能提高安全性,也能省去很多麻烦。

如何使用Winget呢?很简单,按下快捷键Win+X,然后点击"终端(管理员)",如下图所示:

这样就能进入PowerShell,注意一定要是PowerShell,不能是cmd,因为命令不一样。

winget install LLVM.LLVM

然后默认安装位置在

C:\Program Files\LLVM\bin

Windows 系统下环境变量设置

刚安装完成时,你直接运行clang肯定会出错,会提示类似下面的错误信息:

clang
clang : The term 'clang' is not recognized as the name of a cmdlet, function, script file, or
operable program. Check the spelling of the name, or if a path was included, verify that the path is
correct and try again.
At line:1 char:1
+ clang
+ ~~~~~
    + CategoryInfo          : ObjectNotFound: (clang:String) [], CommandNotFoundException
    + FullyQualifiedErrorId : CommandNotFoundException

这是因为刚刚安装好的clang目录没有被添加到系统的环境变量里,所以系统找不到它,这时候就需要手动将上述目录添加到环境变量中。

这也很简单,还是按Win+X,选择"终端(管理员)",进入PowerShell,注意一定要是PowerShell,不能是cmd,因为命令不一样。 然后运行下面这段命令,注意仔细复制粘贴,不要遗漏任何字符:

[Environment]::SetEnvironmentVariable("Path", $([Environment]::GetEnvironmentVariable("Path","Machine")) + ";C:\Program Files\LLVM\bin", "Machine")

上面的命令执行成功后,需要新建一个终端窗口(注意,一定要是新建的),才能加载新设置的环境变量,然后再运行验证命令。

clang --version

会得到

clang version 21.1.8
Target: x86_64-pc-windows-msvc
Thread model: posix
InstalledDir: C:\Program Files\LLVM\bin

Ubuntu 系统下的安装和使用

如果使用的是Ubuntu系统,那就更方便了,直接使用apt安装即可:

# 首先更新软件包列表
sudo apt update
# 然后安装Clang本体
sudo apt install clang
# 接着安装一些依赖库
sudo apt install libc++-dev libc++abi-dev
# 然后就可以直接使用了
clang --version

就会得到下面的输出:

Ubuntu clang version 18.1.3 (1ubuntu1)
Target: x86_64-pc-linux-gnu
Thread model: posix
InstalledDir: /usr/bin

Clang 的最基础用法

假设当前目录下有一份名为hello.c的C语言代码文件,内容如下:

#include <stdio.h>      // 标准输入输出(printf, scanf)
#include <stdlib.h>     // 标准库(malloc, free, EXIT_SUCCESS)
#include <string.h>     // 字符串操作(strcpy, strlen)
#include <math.h>       // 数学函数(sqrt, pow)
#include <stdbool.h>    // 布尔类型(bool, true, false)

// 函数声明(可选,因定义在 main 之后)
double compute_hypotenuse(double a, double b);

int main(void) {
    // === 基本数据类型 ===
    int          age = 25;                     // 整型
    short        count = 100;                  // 短整型
    long         big_num = 123456789L;         // 长整型
    long long    huge_num = 9876543210LL;      // 更长整型

    float        price = 19.99f;               // 单精度浮点
    double       pi = 3.1415926535;            // 双精度浮点

    char         grade = 'A';                  // 字符(ASCII)
    unsigned char byte_val = 255;              // 无符号字符

    bool         is_valid = true;              // 布尔值(C99起)

    // === 构造类型:数组、结构体、枚举 ===
    int scores[3] = {90, 85, 95};              // 数组

    struct Student {                           // 结构体
        char name[20];
        int age;
        double gpa;
    } stu = {"Alice", 20, 3.8};

    enum Color { RED, GREEN, BLUE };           // 枚举
    enum Color bg = GREEN;

    // === 指针与动态内存 ===
    int *p = &age;                             // 指针(指向 age)
    int *arr = malloc(5 * sizeof(int));        // 动态分配数组
    if (arr == NULL) return EXIT_FAILURE;
    for (int i = 0; i < 5; i++) arr[i] = i * 2;

    // === 使用标准库函数 ===
    printf("Hello, %s! Age: %d\n", stu.name, stu.age);
    printf("Pi is about %.6f, sqrt(2) is about %.4f\n", pi, sqrt(2.0));
    printf("Hypotenuse of (3,4): %.2f\n", compute_hypotenuse(3.0, 4.0));

    // === 控制结构 ===
    if (is_valid) {
        printf("Status: Valid\n");
    }

    for (int i = 0; i < 3; i++) {
        printf("Score[%d] = %d\n", i, scores[i]);
    }

    // === 释放动态内存 ===
    free(arr);

    return EXIT_SUCCESS;  // 程序正常退出
}

// === 自定义函数 ===
double compute_hypotenuse(double a, double b) {
    return sqrt(a * a + b * b);  // 调用 math.h 中的 sqrt
}

上面的代码内容可能比较多,其实涵盖了C语言的基础知识点,大家现在不用太在意细节。

# 编译 C 程序
clang hello.c -o hello.exe
./hello.exe

然后就会得到类似下面的输出:

Hello, Alice! Age: 20
Pi is about 3.141593, sqrt(2) is about 1.4142
Hypotenuse of (3,4): 5.00
Status: Valid
Score[0] = 90
Score[1] = 85
Score[2] = 95

上面使用了.exe扩展名,这是针对Windows用户的。 在Unix-like系统(如Linux或macOS)中,可执行文件通常不带.exe后缀;.exe是Windows的惯用命名方式。因此在这些系统中建议输出为hello而非hello.exe,如下所示:

# 编译 C 程序
clang hello.c -o hello
./hello

Clang 的进阶使用

其实前面的编译过程还可以添加很多选项。另外,严格来说这里实际上完成了编译、链接等多个步骤,只是在此处简称为编译。

选项 作用
-std=c11 指定使用C11标准(推荐,因代码使用了bool等C99+特性)
-O2 启用推荐的优化等级,适用于发布版本
-g 生成调试信息,便于使用GDB或LLDB调试
-Wall 开启常见警告,帮助发现潜在问题
-Wextra 启用更多额外警告(可选但推荐)
-lm 在Linux或macOS下链接数学库(因代码调用了sqrtpow

重要:在Linux或macOS系统下,链接数学库时,-lm选项必须放在源文件之后,否则可能导致链接失败:

clang -std=c11 -O2 -Wall -g hello.c -o hello.exe   # ✅ Windows
clang -std=c11 -O2 -Wall -g hello.c -lm -o hello   # ✅ Linux或macOS
clang -lm hello.c -o hello                         # ❌ 可能报错

完整推荐命令:

clang -std=c11 -O2 -Wall -Wextra -g hello.c -o hello.exe   # ✅ Windows
clang -std=c11 -O2 -Wall -Wextra -g hello.c -lm -o hello   # ✅ Linux或macOS

Clang 的简单静态分析

Clang内置静态分析器,可以在不运行程序的情况下发现空指针解引用、未初始化变量等问题:

clang --analyze hello.c

就会提示

hello.c:38:10: warning: Value stored to 'p' during its initialization is never read
      [deadcode.DeadStores]
   38 |     int *p = &age;                             // 指针(指向 age)
      |          ^   ~~~~
1 warning generated.

若代码中存在如malloc失败未处理等风险,分析器会给出相应提示。

如果你想要了解编译器如何理解你的代码:

clang -S -emit-llvm hello.c -o hello.ll

生成的hello.ll是人类可读的LLVM IR,可用于学习编译原理或进行手动优化。

分类: C