一、简介
在 VSCode 中配置 C/C++ 开发环境,主要有两种主流的语言服务器(LSP)方案,它们基于不同的工具链和设计理念,适用于不同的开发需求。要理解这两种方案,需先明确 LSP(语言服务器协议) 的概念:它是由微软主导制定的一套跨编辑器 / IDE 的标准化协议,旨在简化编程语言工具(如代码补全、语法检查、跳转定义等)的开发与集成,让开发者在不同编辑器中能获得一致的编程辅助体验。
1.1 微软官方 C/C++ 插件方案
由微软官方开发维护,是一套 "一站式" 的 C/C++ 开发解决方案。它集成了 IntelliSense(智能感知) 和调试功能,安装后无需额外配置太多依赖即可快速上手。具备代码补全、语法检查、跳转定义、断点调试等完整流程,适合 C/C++ 新手快速搭建开发环境,降低入门门槛。
1.2 Clangd插件+LLVM/Clangd 方案
由 LLVM 团队开发的Clangd插件,基于 Clang 工具链提供语言服务。它在代码分析的准确性、大型项目的性能表现 上更具优势,尤其适合追求编译工具链一致性(如用 Clang 编译代码)的场景。但需要注意,Clangd本身不包含调试功能,需搭配**CodeLLDB**插件才能实现 C/C++ 程序的调试。这种方案更偏向于专业开发场景,尤其是对代码质量和编译流程有深入需求的开发者。
本文将聚焦Windows 下基于 Clangd 插件 + LLVM 的 VSCode C/C++ 开发环境,详细讲解其配置流程与初步使用方法,帮助开发者利用 Clang 工具链的优势打造高效开发环境。
1.3 什么是LLVM
LLVM 命名最早源自于底层虚拟机(Low Level Virtual Machine)的缩写,由于命名带来的混乱,LLVM就是该项目的全称。LLVM 核心库提供了与编译器相关的支持,可以作为多种语言编译器的后台来使用。能够进行程序语言的编译器优化、链接优化、在线编译优化、代码生成。LLVM的项目是一个模块化和可重复使用的编译器和工具技术的集合。LLVM是伊利诺伊大学的一个研究项目,提供一个现代化的,基于SSA的编译策略能够同时支持静态和动态的任意编程语言的编译目标。自那时以来,已经成长为LLVM的主干项目,由不同的子项目组成,其中许多正在生产中使用的各种商业和开源的项目,以及被广泛用于学术研究。
总之,LLVM 是一个开源的编译器基础设施项目与框架,它以模块化、可重用的设计理念,为编译技术领域提供了一套完整的工具链和扩展能力,其核心价值可从以下维度解析:
(Chris Latter本来只是想写一个底层的虚拟机,这也是LLVM名字的由来,low level virtual machine,跟Java的JVM虚拟机一样,可是后来,llvm从来没有被用作过虚拟机,哪怕LLVM的名气已经传开了。所以人们决定仍然叫他LLVM,更多的时候只是当作"商标"一样的感觉在使用,其实它跟虚拟机没有关系 。官方描述如下:
The name "LLVM" itself is not an acronym; it is the full name of the project. "LLVM"这个名称本身不是首字母缩略词; 它是项目的全名。)
1.3.1 核心定位:编译器生态的 "通用底座"
它并非单一工具,而是一个包含编译器组件、优化库、工具链的生态系统,支持从编程语言前端(如 C/C++、Rust、Swift 等)的语法解析,到中间代码优化,再到目标平台(x86、ARM、RISCV 等)机器码生成的全流程。
简单来说,LLVM 是编译技术的 "乐高积木"------ 开发者可以基于它的模块化组件,快速构建新编程语言的编译器、静态分析工具、代码优化插件等,无需从零实现整个编译流程。
(1)传统的编译器架构

- Frontend:前端
词法分析、语法分析、语义分析、生成中间代码 - Optimizer:优化器
中间代码优化 - Backend:后端
生成机器码
(2)LLVM架构

- 不同的前端后端使用统一的中间代码LLVM Intermediate Representation (LLVM IR)
- 如果需要支持一种新的编程语言,那么只需要实现一个新的前端
- 如果需要支持一种新的硬件设备,那么只需要实现一个新的后端
- 优化阶段是一个通用的阶段,它针对的是统一的LLVM IR,不论是支持新的编程语言,还是支持新的硬件设备,都不需要对优化阶段做修改
- 相比之下,GCC的前端和后端没分得太开,前端后端耦合在了一起。所以GCC为了支持一门新的语言,或者为了支持一个新的目标平台,就变得特别困难
- LLVM现在被作为实现各种静态和运行时编译语言的通用基础结构(GCC家族、Java、.NET、Python、Ruby、Scheme、Haskell等)
1.3.2 技术架构:模块化与标准化的突破
传统编译器(如 GCC)的前端(语法分析)和后端(机器码生成)耦合度高 ,导致新增一门语言或支持新硬件时,需要大量重复开发。而 LLVM 通过统一的中间表示(LLVM IR) 实现了 "前后端解耦":
- 前端 :负责将特定编程语言(如 C++、Rust)转换为LLVM IR(一种与平台无关的中间代码)。例如,Clang 就是 LLVM 生态中针对 C/C++/Objective-C 的前端。
- 优化器:对 LLVM IR 进行通用优化(如消除冗余代码、提升执行效率),这一阶段与具体语言、硬件无关。
- 后端:将优化后的 LLVM IR 转换为目标平台的机器码。若需支持新硬件,只需开发对应的后端模块即可。
1.3.3 生态与工具:从编译到开发全链路覆盖
LLVM 生态包含众多核心工具,支撑不同场景的开发需求:
- Clang :LLVM 的 C/C++/Objective-C 前端编译器,以编译速度快、错误提示友好、模块化易集成著称,是 Xcode等 IDE 的默认 C++ 编译工具之一。
- Clangd :基于 Clang 的C/C++ 语言服务器(支持 LSP 协议),为 VSCode 等编辑器提供代码补全、跳转定义、语法检查等开发辅助功能。
- LLDB:跨平台调试器,可与 Clangd 配合实现 C++ 程序的断点调试。
- 其他衍生工具 :如用于静态分析的
Clang Static Analyzer、用于代码混淆的LLVM Obfuscator等。
1.3.4 技术价值:跨语言、跨平台的 "万能适配"
- 跨语言支持:只要能将某门语言转换为 LLVM IR,就能借助其优化器和后端生成任意平台的可执行代码。因此,Rust、Swift、D 语言等都以 LLVM 为编译基础。
- 跨平台能力:支持几乎所有主流硬件架构(x86、ARM、RISCV 等),开发者无需为不同平台重复开发编译逻辑。
- 可扩展性:基于 LLVM 的模块化设计,开发者可轻松扩展功能 ------ 比如开发自定义的代码优化 "Pass"、新增编程语言的前端,或为 IDE 打造智能提示插件(如 Clangd)。
1.3.5 行业地位:技术影响力的标杆
- 它是众多主流技术的底层支撑:苹果 Swift 语言、Rust 语言的编译器、Linux 内核的 Clang 编译方案、VSCode 的 C++ 开发环境等,都依赖 LLVM 生态。
- 曾获ACM 2012 年软件系统奖(该奖项此前授予过 Java、UNIX、万维网等里程碑式技术),足以证明其在软件领域的突破性价值。
简言之,LLVM 不仅是一套编译器工具链,更是编译技术的 "基础设施框架"------ 它通过标准化、模块化的设计,大幅降低了编译工具的开发门槛,推动了跨语言、跨平台开发的效率革命。
1.4 什么是clang
Clang 是LLVM 项目旗下的开源 C/C++/Objective-C 编译器前端,基于 LLVM 架构设计,专注于将源代码转换为 LLVM 中间表示(LLVM IR),是现代 C/C++ 开发中替代 GCC 的重要工具。其核心定位、技术特点与价值可从以下维度解析:
1.4.1 核心角色:LLVM 架构的 "前端支柱"
在 LLVM 的模块化编译流程中,Clang 承担 "前端" 职责:负责接收 C/C++/Objective-C 源代码,通过预处理、词法分析、语法分析生成抽象语法树(AST),最终转换为 LLVM IR(中间代码)。后续的代码优化、机器码生成则由 LLVM 后端完成。
简单说,Clang 是 "源代码到中间代码的转换器",是 LLVM 生态中处理 C/C++ 类语言的核心入口。
1.4.2 相比 GCC 的核心优势
作为新一代编译器前端,Clang 在性能、易用性和扩展性上显著优于传统的 GCC 前端:
- 编译速度更快:在 Debug 模式下,编译 Objective-C 代码的速度是 GCC 的 3 倍,对大型项目的开发效率提升明显。
- 内存占用更低:生成的抽象语法树(AST)内存占用仅为 GCC 的 1/5,减少了编译过程中的资源消耗。
- 模块化设计:基于库的模块化架构(如 libclang、libTooling),易于集成到 IDE(如 VSCode、Xcode)或开发自定义工具(如静态分析器)。
- 诊断信息更友好:编译错误提示包含详细元数据(如代码位置、错误原因),可读性远超 GCC 的晦涩输出,便于开发者快速定位问题。
- 易于扩展:代码结构清晰,开发者可轻松开发插件(如代码规范检查工具)或扩展语法支持,而 GCC 因架构耦合度高,扩展难度大。
1.4.3 工作流程:从源代码到 LLVM IR 的全解析
Clang 对源代码的处理遵循编译前端的标准流程,以 Objective-C 文件(main.m)为例:
- 预处理 :处理
#include、#import、宏定义等,生成预处理后的代码(可通过clang -E main.m查看)。 - 词法分析 :将代码拆分为最小语法单元(Token),如关键字(
void)、标识符(test)、运算符(+)等(可通过clang -dump-tokens查看)。 - 语法分析 :基于 Token 生成抽象语法树(AST),描述代码的语法结构(如函数定义、变量赋值),明确代码逻辑(可通过
clang -ast-dump查看)。 - 生成 LLVM IR :将 AST 转换为与平台无关的 LLVM IR(中间代码),供 LLVM 后端优化并生成机器码(可通过
clang -S -emit-llvm生成文本格式的.ll文件)。
1.4.4 应用场景:从编译到工具开发
- 日常编译:作为 C/C++/Objective-C 的编译器前端,直接用于代码编译(如 Xcode 默认使用 Clang 编译 iOS/macOS 项目)。
- IDE 集成:其模块化库(如 libclang)为 VSCode 等编辑器提供代码补全、跳转定义等功能(Clangd 插件即基于此)。
- 工具开发:基于 Clang 可开发静态分析工具(检查命名规范、内存泄漏)、代码重构工具,或通过插件扩展编译器功能。
简言之,Clang 是 LLVM 生态中为 C/C++/Objective-C 量身打造的 "高效、轻量、易扩展" 的编译器前端,凭借其性能优势和模块化设计,已成为现代 C/C++ 开发的主流工具之一。
二、安装与配置
注意:在使用Clangd插件+LLVM 方案时,需要关闭微软官方的C/C++插件,因为会产生冲突。

2.1 安装LLVM,一般GitHub的都带了clang,但是有些渠道下载的可能没有clang,需要注意一下







2.2 vscode安装clang插件


这里选择不安装,到时候手动配置自己下载的clangd

2.3 下载安装MINGW,记得添加到环境变量
Release Release of 15.2.0-rt_v13-rev0 · niXman/mingw-builds-binaries

2.4 配置需要的功能
要配置 Clangd 的用户级(全局共享)和项目级(工程私有) YAML 配置文件,可按照以下步骤操作:
2.4.1 用户配置(全局共享设置)
用于存放所有项目通用的配置(如补全样式、提示规则等)。
1. 定位配置文件路径
根据系统类型,在对应路径创建 config.yaml 文件:


- Windows :路径:
%LocalAppData%\clangd\config.yaml(可在文件资源管理器中直接输入%LocalAppData%\clangd,新建config.yaml) - macOS :路径:
~/Library/Preferences/clangd/config.yaml(在 Finder 中按Shift+Command+G,输入路径并创建文件) - Linux :路径:
$XDG_CONFIG_HOME/clangd/config.yaml(若XDG_CONFIG_HOME未设置,默认是~/.config/clangd/config.yaml)
2. 写入通用配置内容
将以下内容复制到 config.yaml 中(可根据需求调整):
yaml
# 参考:https://clangd.llvm.org/config
---
# 补全配置:参数列表显示分隔符
Completion:
ArgumentLists: Delimiters
# 嵌入式提示:显示代码块结束、默认参数等
InlayHints:
BlockEnd: true # 显示代码块结束标记(如 `}` 旁标注函数名)
DefaultArguments: true # 显示函数默认参数提示
TypeNameLimit: 0 # 类型名提示无长度限制
# 悬停提示:显示别名(AKA)信息
Hover:
ShowAKA: true
# 全局编译选项(所有项目生效)
CompileFlags:
Add: [-Wall] # 添加 `-Wall` 开启所有警告

2.4.2 项目配置(工程私有设置)
官方的配置说明文档链接
用于存放单个项目的专属配置(如编译命令路径、特定编译器等)。
1. 创建项目配置目录
在项目根目录 下,创建 .clangd 文件,使用yaml语法添加选项。
(创建 .clangd 文件夹,再在其中新建 config.yaml 文件。这种方式过时了)
2. 写入工程专属配置
这里的配置其实除了 .clangd 文件方法,一般用vscode工作区里的提前增加条件选项。
通过 "用户配置(全局通用)+ 项目配置(工程私有)" 的分离,既能保证所有项目共享统一的交互体验,又能针对单个工程做定制化编译配置,大幅提升 C/C++ 开发效率。
2.5 在VSCode中配置LLVM官方插件Clangd
2.5.1 配置工具链本体路径,在设置里搜索Clangd:Path,因为我们最开始下载的时候插件Clangd是个空壳子还要求我们是否下载,取消后这里配置我们自己下载的clangd

2.5.2 进入Clangd插件设置使用选项


建议改为工作区设置,方便后续每个工作区的定制化改变,不会影响到用户设置

一定要一条一条加进去

bash
{
"clangd.arguments": [
// 后台索引(提升大型项目的响应速度)
"--background-index",
"--background-index-priority=low", // 后台索引优先级设为低,不影响编辑
// 启用代码检查(基于clang-tidy,支持代码规范检查)
"--clang-tidy",
// 补全样式:detailed显示更详细的补全信息(如参数类型)
"--completion-style=detailed",
// 代码风格 fallback:当项目无明确风格时,默认使用Google风格
"--fallback-style=Google",
// 关闭函数参数占位符(避免补全时自动插入`${1}`等占位符)
"--function-arg-placeholders=false",
// 禁止自动插入#include(如需手动控制头文件引入)
"--header-insertion=never",
// 重命名文件时无数量限制(默认可能限制大项目重命名)
"--rename-file-limit=0",
// 启用Clangd的配置文件(即前面提到的user/project级config.yaml)
"--enable-config",
// 并行任务数量(根据CPU核心数调整,如8核设为8,加速索引)
"-j=12",
// PCH(预编译头)存储在内存中,提升解析速度
"--pch-storage=memory",
// 格式化输出日志(方便调试)
"--pretty",
// 【核心】指定compile_commands.json的路径(必须修改!)
// 该文件由CMake、Make等构建工具生成,包含项目编译信息(如头文件路径、宏定义)
// 解释:基于cmake构建管理项目时,使用该命令(--compile-commands-dir=)产生的compile_commands.json会放到指定文件夹下同时该文件帮助你找到头文件信息索引
// 格式:--compile-commands-dir=项目中compile_commands.json所在的文件夹路径
"--compile-commands-dir=${workspaceFolder}/build", // 示例:项目根目录下的build文件夹
// 【核心】指定编译器路径(解决标准库头文件找不到的问题)
// 通配符匹配编译器路径(如ARM交叉编译器、x86编译器)
// 格式:--query-driver=编译器可执行文件的路径(支持*通配符)
// "--query-driver=${userHome}/tools/gcc-arm-none-eabi-10.3-2021.10/bin/arm-none-eabi-g*" // 示例:ARM编译器路径
"--query-driver=D:/mingw64/bin"
]
}
(1)参数说明(必看)
-
--compile-commands-dir:必须指向项目中compile_commands.json文件所在的文件夹(该文件是 Clangd 理解项目结构的 "地图")。- 若用 CMake 构建,可在 cmake 命令中添加
-DCMAKE_EXPORT_COMPILE_COMMANDS=ON生成该文件(默认在 build 目录下)。 - 若项目无该文件,Clangd 可能无法识别头文件路径,导致
#include报错。
- 若用 CMake 构建,可在 cmake 命令中添加
-
--query-driver:用于告诉 Clangd 项目使用的编译器路径(如交叉编译器、本地 GCC/Clang),确保其能找到编译器自带的标准库头文件(如stdio.h)。- 路径需用通配符
*匹配编译器可执行文件(如arm-none-eabi-gcc、arm-none-eabi-g++可统一用arm-none-eabi-g*匹配)。
- 路径需用通配符
(2)验证配置是否生效
- 重启 VSCode :按下
Ctrl+Shift+P,输入 "Reload Window" 并回车,让配置生效。 - 打开项目文件 :如
main.c,观察#include语句下方是否有红色波浪线(若消失,说明头文件识别成功)。 - 查看 Clangd 日志 :按下
Ctrl+~打开终端,切换到 "Output" 面板,选择 "Clangd"。- 若日志中无
E[时间戳]开头的错误(如 "cannot find header"),则配置成功; - 若有错误,检查
--compile-commands-dir和--query-driver路径是否正确。
- 若日志中无
(3)特别提醒
- 工作区设置 vs 用户设置:若有多个 C/C++ 项目(如不同架构、不同编译器),务必用 "Workspace Settings"(仅对当前项目生效),避免配置冲突。
- 新项目需重新配置 :每个项目的
compile_commands.json路径和编译器路径可能不同,新项目需重新修改--compile-commands-dir和--query-driver。 - 确保
--query-driver的路径与项目级.clangd/config.yaml中Compiler项一致(若有),否则可能导致标准库识别混乱。
通过以上步骤,Clangd 插件就能正确解析项目代码,提供准确的代码补全、跳转定义、语法检查等功能了。
注:验证:打开一个 C/C++ 文件,观察代码补全、跳转定义等功能是否正常工作,同时可通过 VSCode 的 "输出" 面板(选择 "Clangd")查看日志,确认是否成功加载自定义的 clangd。
2.6 安装cmake

我这里选择的二进制包,你可以下载.msi来安装,这种就是很直观但会增加一些注册表,但是,如果你直接拿源码是不行的,需要自己去编译得到可执行工具链。要下载二进制包,然后将bin文件夹工具链路径加入系统环境变量。这是个人习惯问题。



2.7 安装VSCode的CMake插件


2.8 如果后续需要调试,就需要安装这个插件,提示:这个插件会下载很久建议找到VSIX的外部http地址进行下载


会出现

三、初步使用
**说明:**自己可以尝试当一个大型项目找对应的函数实现,会发现微软那个c/c++插件查找索引会比较慢以及占用内存,相对之下Clangd的效果就好的多。
目前搭配方案是:MINGW的编译器+Clangd的代码补全以及其他功能。你也可以使用clang++代替c++
3.1 未使用CMake版本
这里可以写个小代码进行测试,clangd是否运行正常,一般头文件出现下划线,以及传值前面会有函数形参变量名提示,以及能够正常跳转,就表示是正常的


右击鼠标,能够正常跳转

这个配置文件就再指定了一下编译器路径。

3.2 使用CMake版本
参考:
【Embedded System】【CMake】Windows下CMake+VSCode的开发环境搭建以及初步认识_如何利用vscode和cmake 搭建c++开发环境-CSDN博客
错误现象(原因是因为使用clangd+cmake,你需要在cmake顶层文件里开启一个产生特殊文件的选项来让clangd识别到各个头文件):

这里简单描述一下怎么使用cmake,需要将加法函数c/h文件放入一个子文件夹,可以将这个子文件夹编译为单独一个静态库提供给根目录下的main文件使用,根目录和add子文件夹分别需要cmake文件,根目录下的cmake文件(顶层cmake)为主
3.2.1 代码
(1)add
add.h
#ifndef __ADD_H__
#define __ADD_H__
int add(int a, int b);
#endif // !__ADD_H__
add.c
#include "add.h"
int add(int a, int b)
{
return a+b;
}
CMakeLists.txt
cmake_minimum_required(VERSION 3.10)
add_library(add STATIC add.cpp)
target_include_directories(add PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
(2)根目录
.clangd
CompileFlags:
Add: []
Remove: []
Compiler: D:/mingw64/bin/c++*
main.cpp
#include <iostream>
#include <string>
#include "add.h"
int main() {
std::string name;
std::cout << "Enter your name: ";
std::getline(std::cin, name);
std::cout << "Hello, " << name << "!" << std::endl;
int a = 1;
if (a == 1) {
std::cout << "a is 1" << std::endl;
}
std::cout << "add: " << add(1, 1) << "!" << std::endl;
return 0;
}
E9_clang_test.code-workspace
{
"folders": [
{
"path": "."
}
],
"settings": {
"clangd.arguments": [
"--background-index",
"--background-index-priority=low",
"--clang-tidy",
"--completion-style=detailed",
"--fallback-style=Google",
"--function-arg-placeholders=false",
"--header-insertion=never",
"--header-insertion-decorators",
"--import-insertions",
"--rename-file-limit=0",
"--enable-config",
"-j=18",
"--pch-storage=memory",
"--pretty",
"--compile-commands-dir=${workspaceFolder}/build",
"--query-driver=D:/mingw64/bin/c++*"
]
}
}
CMakeLists.txt
# 主目录 CMakeLists.txt
cmake_minimum_required(VERSION 3.18)
project(E9_clang_test) # 项目名可保持不变
# 开启编译数据库生成(关键!clangd依赖这个文件)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
# 关键:添加子目录(编译add模块)
add_subdirectory(add)
# 定义可执行程序目标(目标名:E9_clang_test)
add_executable(E9_clang_test main.cpp)
# 链接add库(目标名必须和上面的add_executable一致)
target_link_libraries(E9_clang_test add)
注意:
开启编译数据库生成(关键!clangd依赖这个文件)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
3.2.2 代码编译执行
(1)cmake配置,选择对应的编译器,然后就会先配置一些东西,此时就会产生compile_commands.json,然后可以clangd: Restart Language Server 重启服务


(2)在 VS Code 中按 Ctrl+Shift+P,输入 clangd: Restart Language Server 重启服务,clangd 会重新读取 compile_commands.json,就会发现头文件就不会报红了

(3)在 VS Code 中按 Ctrl+Shift+P,输入 cmake build


(4)执行可执行文件

(5)尝试调试,点击调试目标设置,通过cmake插件工具进行配置项目的调试方式文件launch.json,或者直接从CodeLLDB的调试按钮开始也可以




或者CodeLLDB

(6)查看launch

(7)现在开始CodeLLDB,左上角直接点击出现错误,要找到可执行文件,"program": "${workspaceRoot}/build/E9_clang_test.exe",

(8)这里发现我使用的:vscode的 G++编译器+Clangd代码补全+lldb(CodeLLDB) 不能调试(不知道为什么打了断点后,点击使用lldb调试后就马上全部执行并退出,断点灰色且空心),原因可能是:G++编译出来的调试版本的代码的调试符号与lldb不适配,可能需要更换为clang编译出来的文件进行调试。这里我把C/C++ Extension Pack再打开,换为cppdbg调试,可能会跳出一个智能补全类型的功能提示与clang冲突了,点击disable关掉,然后就可以正常进行单步调试。

