# Visual Studio 2019 学习文档

## 1. 学习目标

本文档用于记录 Visual Studio 2019 的基础操作学习内容，主要围绕工程创建、工程配置和工程调试三个方面展开。

完成本文档学习后，应能够完成以下操作：

1. 熟练创建、打开、加载、卸载 Visual Studio 工程；
2. 理解解决方案、项目、文件之间的关系；
3. 熟练配置解决方案配置、平台配置、项目类型、输出路径、头文件路径、库文件路径等常用选项；
4. 掌握宏定义、运行库、调用库、安全检查等基础配置；
5. 熟练使用断点、监视窗口、内存窗口、线程窗口、调用堆栈窗口；
6. 掌握全速运行、重新运行、逐过程、逐语句等调试方式。

## 2. 文档目录结构

VisualStudio 学习文档按照基础学习要求进行存放，推荐目录结构如下：

```text
VisualStudio
|---pictures
|   |---VS2019_001_启动界面.png
|   |---VS2019_002_创建项目.png
|   |---VS2019_003_解决方案资源管理器.png
|   |---VS2019_004_项目属性页.png
|   |---VS2019_005_断点调试.png
|   +---VS2019_006_调试窗口.png
+---VisualStudio.md
```

图片统一放在 `pictures` 文件夹下，Markdown 文档中通过相对路径进行引用。

## 3. Visual Studio 基本概念

### 3.1 Visual Studio 是什么

Visual Studio 是 Microsoft 提供的集成开发环境，常用于 C、C++、C# 等程序开发。对于 C/C++ 工程来说，Visual Studio 可以完成代码编辑、工程管理、编译、链接、运行和调试等工作。

在学习 VS2019 时，不能只会写代码，还要理解工程结构、编译配置、链接配置和调试方法。

### 3.2 解决方案、项目、文件的关系

VS2019 中常见的层级关系如下：

```text
解决方案 Solution
|---项目 Project 1
|   |---头文件 Header Files
|   |---源文件 Source Files
|   +---资源文件 Resource Files
|
+---项目 Project 2
    |---头文件 Header Files
    |---源文件 Source Files
    +---资源文件 Resource Files
```

各部分含义如下：

| 名称   | 含义                                           |
| ---- | -------------------------------------------- |
| 解决方案 | 用于统一管理一个或多个项目，文件后缀通常为 `.sln`                 |
| 项目   | 一个可以独立配置、编译、生成的工程，C/C++ 项目文件后缀通常为 `.vcxproj` |
| 头文件  | 一般为 `.h` 文件，用于函数声明、宏定义、结构体声明等                |
| 源文件  | 一般为 `.c` 或 `.cpp` 文件，用于函数实现                  |
| 资源文件 | 图标、界面、资源脚本等文件，普通控制台程序中不一定使用                  |

简单理解：解决方案是一个大文件夹，项目是里面的具体工程，文件是工程中真正参与编译和管理的代码文件。

![VS2019解决方案资源管理器](pictures/VS2019_003_解决方案资源管理器.png)

## 4. 工程创建

工程创建部分主要学习 VS2019 中“解决方案、项目、文件”三者的基本管理方法。实际开发时，不能只会新建一个工程，还要会打开已有工程、加载或卸载项目、设置启动项目、向解决方案中添加项目，以及管理项目中的源文件和头文件。

本章对应要求如下：

```text
工程创建
|---工程相关
|   |---创建/打开项目
|   |---加载/卸载项目
|   +---设置启动项
|---解决方案相关
|   +---添加项目
+---文件相关
    |---创建/打开文件
    +---包括/排除文件
```

### 4.1 创建项目

#### 4.1.1 创建项目的含义

创建项目就是在 VS2019 中新建一个可以被编译、链接和调试的工程。项目中可以包含源文件、头文件、资源文件以及工程配置。对于 C/C++ 学习来说，项目通常会生成 `.exe`、`.lib` 或 `.dll` 文件。

常见项目类型如下：

| 项目类型  | 生成结果     | 适用场景                            |
| ----- | -------- | ------------------------------- |
| 控制台应用 | `.exe`   | 学习 C/C++ 基础语法、普通程序调试            |
| 空项目   | 根据后续配置决定 | 适合手动添加 `.c/.cpp/.h` 文件，便于理解工程结构 |
| 静态库   | `.lib`   | 将一部分代码封装成库，供其他项目调用              |
| 动态链接库 | `.dll`   | 运行时被其他程序加载调用                    |

学习 VS2019 基础操作时，建议优先创建 `空项目`，因为空项目不会自动生成太多文件，工程结构更清楚。

#### 4.1.2 创建空项目操作步骤

操作步骤：

1. 打开 `Visual Studio 2019`；

2. 在启动界面点击：

   ```text
   创建新项目
   ```

3. 在搜索框中输入：

   ```text
   空项目
   ```

4. 选择 C++ 分类下的：

   ```text
   空项目
   ```

5. 点击：

   ```text
   下一步
   ```

6. 设置项目信息：

   | 设置项    | 示例                          | 说明            |
   | ------ | --------------------------- | ------------- |
   | 项目名称   | `VS_Demo`                   | 当前项目的名称       |
   | 位置     | `D:\TrainCamp\VisualStudio` | 项目保存路径        |
   | 解决方案名称 | `VS_Demo`                   | 解决方案名称，可与项目同名 |

7. 点击：

   ```text
   创建
   ```

8. 创建完成后，在右侧 `解决方案资源管理器` 中可以看到解决方案和项目。

![创建空项目](pictures\VS2019_002_创建项目.png)

#### 4.1.3 创建完成后的工程结构

创建空项目后，解决方案资源管理器中通常可以看到如下结构：

```text
解决方案“VS_Demo”
+---VS_Demo
    |---引用
    |---外部依赖项
    |---头文件
    |---源文件
    +---资源文件
```

各部分说明如下：

| 名称   | 说明                                    |
| ---- | ------------------------------------- |
| 解决方案 | 用来管理一个或多个项目，后缀一般为 `.sln`              |
| 项目   | 真正参与编译和生成的工程，C/C++ 项目后缀一般为 `.vcxproj` |
| 头文件  | 用来存放 `.h` 文件                          |
| 源文件  | 用来存放 `.c` 或 `.cpp` 文件                 |
| 资源文件 | 用来存放图标、资源脚本等文件，普通控制台程序可以不使用           |

注意：刚创建的空项目没有 `main.cpp`，此时直接运行会报错，需要先手动添加源文件。

### 4.2 打开项目

#### 4.2.1 打开项目的含义

打开项目是指打开一个已经存在的 VS2019 工程。实际工作中，别人发来的工程或者从代码仓库拉取下来的工程，一般不需要重新创建，而是直接打开已有解决方案。

VS2019 中常见文件后缀如下：

| 文件后缀         | 含义               | 是否推荐直接打开           |
| ------------ | ---------------- | ------------------ |
| `.sln`       | 解决方案文件，管理一个或多个项目 | 推荐                 |
| `.vcxproj`   | C/C++ 项目文件       | 可以打开，但不如 `.sln` 完整 |
| `.cpp/.c/.h` | 普通代码文件           | 只会打开文件，不等于打开工程     |

一般情况下，打开工程时优先选择 `.sln` 文件。

#### 4.2.2 打开已有解决方案

操作步骤：

1. 打开 `Visual Studio 2019`；

2. 点击：

   ```text
   打开项目或解决方案
   ```

3. 找到工程目录；

4. 选择后缀为 `.sln` 的文件，例如：

   ```text
   VS_Demo.sln
   ```

5. 点击：

   ```text
   打开
   ```

6. 等待项目加载完成；

7. 在 `解决方案资源管理器` 中确认项目是否正常显示。

![打开解决方案](F:\信捷电气\训练营\第一周\学习文档\yuwenhao\VisualStudio\pictures\VS2019_002.png)

#### 4.2.3 只打开文件和打开项目的区别

很多初学者会直接双击 `.cpp` 文件，这样虽然能看到代码，但并不代表工程已经被打开。

| 操作方式          | 结果            | 是否能完整编译调试 |
| ------------- | ------------- | --------- |
| 打开 `.sln`     | 打开完整解决方案和项目配置 | 可以        |
| 打开 `.vcxproj` | 打开单个项目        | 一般可以      |
| 打开 `.cpp`     | 只打开一个代码文件     | 不一定可以     |

所以在验收或实际开发中，应优先通过 `.sln` 打开完整工程。

### 4.3 加载项目和卸载项目

#### 4.3.1 加载/卸载项目的含义

一个解决方案中可以包含多个项目。加载项目表示该项目当前被 VS2019 正常打开，可以查看、配置和生成。卸载项目表示该项目暂时不参与当前解决方案的加载和生成，但项目文件并没有被删除。

简单理解：

```text
卸载项目 = 临时让项目休息，不是删除项目
重新加载项目 = 让项目重新回到解决方案中
```

#### 4.3.2 卸载项目操作步骤

操作步骤：

1. 在左侧打开：

   ```text
   解决方案资源管理器
   ```

2. 找到需要卸载的项目；

3. 右键点击该项目名称；

4. 选择：

   ```text
   卸载项目
   ```

5. 卸载后，项目名称会变成灰色，表示当前项目已经卸载。

![卸载项目](pictures/VS2019_003_卸载项目.png)

#### 4.3.3 重新加载项目操作步骤

操作步骤：

1. 在 `解决方案资源管理器` 中找到已经卸载的项目；

2. 右键点击该项目；

3. 选择：

   ```text
   重新加载项目
   ```

   ![](pictures\重新加载项目.png)

4. 项目恢复正常显示；

5. 此时可以继续查看、修改、编译该项目。

#### 4.3.4 加载/卸载项目的常见用途

| 使用场景               | 说明               |
| ------------------ | ---------------- |
| 解决方案中项目很多          | 暂时卸载无关项目，减少干扰    |
| 某个项目暂时编译不过         | 可以先卸载该项目，只调试其他项目 |
| 查看 `.vcxproj` 文件内容 | 卸载项目后可以右键编辑项目文件  |
| 多项目联合开发            | 需要哪个项目就加载哪个项目    |

注意：卸载项目不会删除磁盘上的代码文件，也不会删除 `.vcxproj` 项目文件。

### 4.4 设置启动项

#### 4.4.1 启动项的含义

启动项就是按 `F5` 或点击 `本地 Windows 调试器` 时，VS2019 默认运行的项目。

当一个解决方案里只有一个项目时，一般不需要特别设置；当一个解决方案里有多个项目时，必须确认哪个项目是启动项。

例如一个解决方案中有两个项目：

```text
解决方案 DemoSolution
|---App_Main       生成 exe，主程序
+---Math_Lib       生成 lib，静态库
```

此时应该把 `App_Main` 设置为启动项，因为 `.lib` 静态库不能直接运行。

#### 4.4.2 设置启动项操作步骤

操作步骤：

1. 在 `解决方案资源管理器` 中找到需要运行的项目；

2. 右键点击该项目；

3. 选择：

   ```text
   设为启动项目
   ```

4. 设置成功后，该项目名称一般会加粗显示；

5. 按 `F5` 或点击：

   ```text
   本地 Windows 调试器
   ```

6. VS2019 会编译并运行当前启动项目。

![设置启动项](pictures/设置启动项.png)

#### 4.4.3 启动项设置错误的常见现象

| 现象              | 可能原因          | 解决方法                |
| --------------- | ------------- | ------------------- |
| 按 F5 运行的不是想要的项目 | 启动项选错         | 右键目标项目，设为启动项目       |
| 提示无法启动程序        | 没有生成 `.exe`   | 检查项目类型是否为应用程序       |
| 静态库项目无法运行       | `.lib` 不能直接运行 | 设置主程序 `.exe` 项目为启动项 |
| 运行按钮是灰色         | 项目未加载或配置异常    | 重新加载项目，检查配置         |

### 4.5 向解决方案添加项目

#### 4.5.1 添加项目的含义

解决方案可以管理一个项目，也可以管理多个项目。添加项目就是把新的项目加入到当前解决方案中，方便多个项目一起管理、编译和调试。

常见组合如下：

| 组合方式           | 说明                 |
| -------------- | ------------------ |
| 主程序项目 + 静态库项目  | 主程序调用自己写的 `.lib` 库 |
| 主程序项目 + 单元测试项目 | 一个项目写功能，一个项目写测试    |
| 多个模块项目         | 不同模块拆分成不同项目管理      |

#### 4.5.2 添加新项目

操作步骤：

1. 在 `解决方案资源管理器` 中，右键点击最上层的解决方案；

2. 选择：

   ```text
   添加 -> 新建项目
   ```

3. 选择项目模板，例如：

   ```text
   空项目
   ```

4. 点击：

   ```text
   下一步
   ```

5. 输入新项目名称，例如：

   ```text
   Math_Lib
   ```

6. 点击：

   ```text
   创建
   ```

7. 创建完成后，解决方案下会出现新的项目。

![添加新项目](pictures/VS2019_005.png)

#### 4.5.3 添加现有项目

如果磁盘中已经存在一个项目，可以把它添加到当前解决方案中。

操作步骤：

1. 右键点击解决方案；

2. 选择：

   ```text
   添加 -> 现有项目
   ```

3. 找到已有项目文件；

4. 选择后缀为 `.vcxproj` 的文件；

5. 点击：

   ```text
   打开
   ```

6. 该项目会被加入当前解决方案。

注意：添加现有项目不是复制代码，而是把已有项目引用到当前解决方案中。如果移动或删除原项目目录，解决方案可能会找不到该项目。

### 4.6 创建文件

#### 4.6.1 创建文件的含义

创建文件就是在项目中新增 `.c`、`.cpp`、`.h` 等代码文件。空项目刚创建完成后没有源文件，因此必须先添加源文件，项目才能正常编译生成。

常用文件类型如下：

| 文件类型    | 后缀     | 作用               |
| ------- | ------ | ---------------- |
| C 源文件   | `.c`   | 编写 C 语言函数实现      |
| C++ 源文件 | `.cpp` | 编写 C++ 函数实现      |
| 头文件     | `.h`   | 编写函数声明、宏定义、结构体声明 |

#### 4.6.2 创建源文件

操作步骤：

1. 在 `解决方案资源管理器` 中找到当前项目；

2. 右键点击：

   ```text
   源文件
   ```

3. 选择：

   ```text
   添加 -> 新建项
   ```

4. 选择：

   ```text
   C++ 文件(.cpp)
   ```

5. 输入文件名，例如：

   ```text
   main.cpp
   ```

6. 点击：

   ```text
   添加
   ```

7. 在 `main.cpp` 中编写测试代码。

示例代码：

```cpp
#include <iostream>

int main()
{
    std::cout << "Hello Visual Studio 2019" << std::endl;
    return 0;
}
```

![创建源文件](pictures/创建源文件.png)

#### 4.6.3 创建头文件

操作步骤：

1. 在 `解决方案资源管理器` 中右键点击：

   ```text
   头文件
   ```

2. 选择：

   ```text
   添加 -> 新建项
   ```

3. 选择：

   ```text
   头文件(.h)
   ```

4. 输入文件名，例如：

   ```text
   calc.h
   ```

5. 点击：

   ```text
   添加
   ```

头文件示例：

```cpp
#ifndef CALC_H
#define CALC_H

int Add(int a, int b);

#endif
```

对应源文件示例：

```cpp
#include "calc.h"

int Add(int a, int b)
{
    return a + b;
}
```

### 4.7 打开文件

#### 4.7.1 通过解决方案资源管理器打开文件

操作步骤：

1. 在 `解决方案资源管理器` 中展开项目；

2. 找到 `源文件` 或 `头文件`；

3. 双击需要打开的文件；

4. 文件会在中间编辑区域打开；

5. 可以直接查看和修改代码。

#### 4.7.2 通过菜单打开文件

操作步骤：

1. 点击菜单栏：

   ```text
   文件 -> 打开 -> 文件
   ```

2. 选择需要打开的 `.c`、`.cpp` 或 `.h` 文件；

3. 点击：

   ```text
   打开
   ```

这种方式只是打开文件进行查看或编辑，不一定表示该文件已经加入当前项目。

#### 4.7.3 判断文件是否属于当前项目

可以通过以下方式判断文件是否已经加入项目：

| 判断方式            | 说明                        |
| --------------- | ------------------------- |
| 是否显示在解决方案资源管理器中 | 显示在项目目录下，一般表示已经加入项目       |
| 是否参与编译          | `.c/.cpp` 文件加入项目后，一般会参与编译 |
| 右键文件是否有属性       | 项目中的文件可以查看文件属性            |

注意：仅仅把文件复制到工程文件夹下，不代表它已经加入 VS2019 项目。必须通过 `添加 -> 现有项` 或 `包括在项目中` 加入项目后，才会被项目管理。

### 4.8 包括文件和排除文件

#### 4.8.1 包括/排除文件的含义

在 VS2019 中，磁盘上的文件和项目中的文件不是完全一样的概念。

```text
磁盘上存在文件 ≠ 文件已经加入项目
文件显示在项目中 ≈ 文件被项目管理
源文件被项目管理后，通常会参与编译
```

包括文件：把磁盘中的文件加入项目，让 VS2019 管理它。

排除文件：把文件从项目中移出，让它暂时不参与编译，但不删除磁盘上的真实文件。

#### 4.8.2 添加现有文件到项目

如果文件已经在磁盘上存在，可以通过以下方式加入项目。

操作步骤：

1. 在 `解决方案资源管理器` 中右键点击项目或对应筛选器，例如 `源文件`；

2. 选择：

   ```text
   添加 -> 现有项
   ```

3. 选择已有的 `.c`、`.cpp` 或 `.h` 文件；

4. 点击：

   ```text
   添加
   ```

5. 文件出现在解决方案资源管理器中，表示已经加入项目。

![添加现有文件](pictures/VS2019_007_添加现有文件.png)

#### 4.8.3 从项目中排除文件

操作步骤：

1. 在 `解决方案资源管理器` 中找到需要排除的文件；

2. 右键点击该文件；

3. 选择：

   ```text
   从项目中排除
   ```

4. 文件会从项目视图中消失；

5. 该文件不会再参与项目编译；

6. 但是磁盘上的真实文件仍然存在。

适用场景：

| 场景               | 说明            |
| ---------------- | ------------- |
| 某个 `.cpp` 文件暂时不用 | 排除后不参与编译      |
| 同名函数重复定义         | 可以先排除其中一个源文件  |
| 临时保留旧版本代码        | 文件留在磁盘，但不加入项目 |

#### 4.8.4 重新包括被排除的文件

如果文件被排除后想重新加入项目，可以使用 `显示所有文件`。

操作步骤：

1. 在 `解决方案资源管理器` 上方点击：

   ```text
   显示所有文件
   ```
   ![包括在项目中](pictures/VS2019.png)

2. 找到被排除的文件；

3. 此时文件图标一般会比较虚；

4. 右键点击该文件；

5. 选择：

   ```text
   包括在项目中
   ```

6. 文件重新加入项目，并可以参与编译。

![包括在项目中](pictures/VS2019_008_包括在项目中.png)

#### 4.8.5 排除、移除、删除的区别

这三个操作容易混淆，需要重点区分。

| 操作     | 是否还在项目中 | 是否删除磁盘文件 | 是否参与编译 | 说明                |
| ------ | ------- | -------- | ------ | ----------------- |
| 从项目中排除 | 否       | 否        | 否      | 最安全，只是不让项目管理该文件   |
| 移除     | 否       | 通常不删除    | 否      | 从项目中移除引用，文件一般还在磁盘 |
| 删除     | 否       | 可能删除     | 否      | 风险最高，可能直接删除真实文件   |

学习和验收时，如果只是想让某个文件不参与编译，优先使用 `从项目中排除`，不要直接删除文件。

### 4.9 工程创建部分验收要点

工程创建部分需要能够独立完成以下操作：

1. 新建一个 VS2019 空项目；
2. 添加 `main.cpp` 文件，并写入简单测试代码；
3. 能够通过 `.sln` 打开已有工程；
4. 能够卸载项目和重新加载项目；
5. 能够把指定项目设置为启动项；
6. 能够向解决方案中添加新项目或现有项目；
7. 能够创建 `.cpp` 源文件和 `.h` 头文件；
8. 能够打开工程中的文件；
9. 能够把已有文件添加到项目；
10. 能够将文件从项目中排除，并重新包括到项目中；
11. 能够说明“排除、移除、删除”的区别。

工程创建部分的核心不是只会点“创建”，而是要理解：

```text
解决方案负责管理多个项目；
项目负责管理代码文件和编译配置；
文件只有加入项目后，才会被项目管理和参与编译。
```

## 5. 工程配置

### 5.1 打开项目属性页

大部分工程配置都在项目属性页中完成。

打开步骤：

1. 在 `解决方案资源管理器` 中右键点击项目；

2. 选择：

   ```text
   属性
   ```

3. 打开 `项目属性页`；

4. 在顶部选择当前配置和平台；

5. 在左侧选择需要修改的配置项。

![项目属性页](pictures/VS2019_004_项目属性页.png)

属性页顶部一般有两个关键选项：

```text
配置：Debug / Release / 所有配置
平台：Win32 / x64 / 所有平台
```

修改配置前一定要确认当前修改的是哪个配置和哪个平台，否则容易出现 Debug 能用、Release 不能用，或者 x64 能用、Win32 不能用的问题。

### 5.2 解决方案配置

解决方案配置用于选择当前工程的编译模式，常见有 `Debug` 和 `Release`。

| 配置      | 特点                 | 使用场景    |
| ------- | ------------------ | ------- |
| Debug   | 包含调试信息，优化较少，便于断点调试 | 开发和调试阶段 |
| Release | 优化较多，运行效率更高，调试信息较少 | 发布和交付阶段 |

切换步骤：

1. 在 VS 顶部工具栏找到配置下拉框；

   ![](pictures/解决方案配置.png)

2. 选择：

   ```text
   Debug
   ```

   或：

   ```text
   Release
   ```

3. 重新生成项目。

也可以通过以下菜单打开配置管理器：

```text
生成 -> 配置管理器
```

![](pictures/配置管理器.png)

在配置管理器中可以查看每个项目是否参与当前解决方案生成。

### 5.3 平台配置

平台配置用于选择程序生成的平台类型，常见有 `Win32` 和 `x64`。

| 平台    | 含义        |
| ----- | --------- |
| Win32 | 生成 32 位程序 |
| x64   | 生成 64 位程序 |

切换步骤：

1. 在 VS 顶部工具栏找到平台下拉框；

2. 选择 `Win32` 或 `x64`；

3. 如果没有目标平台，打开：

   ```text
   生成 -> 配置管理器
   ```

4. 在 `活动解决方案平台` 中选择或新建平台。

注意：

* 程序、静态库、动态库的平台需要保持一致；
* x64 程序不能直接链接 Win32 的 `.lib`；
* 平台不一致时，容易出现链接错误。

![](pictures/平台配置.png)

### 5.4 项目类型配置

项目类型决定工程最终生成什么文件。

配置路径：

```text
项目属性 -> 配置属性 -> 常规 -> 配置类型
```

常见配置类型如下：

| 配置类型       | 生成文件   | 说明             |
| ---------- | ------ | -------------- |
| 应用程序(.exe) | `.exe` | 可以直接运行的程序      |
| 静态库(.lib)  | `.lib` | 编译成库文件，供其他项目链接 |
| 动态库(.dll)  | `.dll` | 运行时动态加载的库      |
| 生成文件       | 自定义    | 一般用于特殊构建流程     |

设置步骤：

1. 右键项目，选择 `属性`；

2. 进入：

   ```text
   配置属性 -> 常规
   ```

3. 找到 `配置类型`；

4. 根据需要选择 `应用程序(.exe)`、`静态库(.lib)` 或 `动态库(.dll)`；

   ![](pictures/项目类型配置.png)

5. 点击 `应用`；

6. 重新生成项目。

### 5.5 输出路径配置

输出路径用于设置编译生成文件的保存位置。

配置路径：

```text
项目属性 -> 配置属性 -> 常规
```

常见选项：

| 选项      | 说明                             |
| ------- | ------------------------------ |
| 输出目录    | `.exe`、`.lib`、`.dll` 等最终文件输出位置 |
| 中间目录    | `.obj`、临时编译文件输出位置              |
| 目标文件名   | 最终生成文件名称                       |
| 目标文件扩展名 | 例如 `.exe`、`.lib`、`.dll`        |

![](pictures/输出路径配置.png)

常用写法示例：

```text
输出目录：$(SolutionDir)bin\$(Platform)\$(Configuration)\
中间目录：$(SolutionDir)obj\$(ProjectName)\$(Platform)\$(Configuration)\
```

常用宏说明：

| 宏                  | 含义                      |
| ------------------ | ----------------------- |
| `$(SolutionDir)`   | 当前解决方案所在目录              |
| `$(ProjectDir)`    | 当前项目所在目录                |
| `$(ProjectName)`   | 当前项目名称                  |
| `$(Configuration)` | 当前配置，例如 Debug 或 Release |
| `$(Platform)`      | 当前平台，例如 Win32 或 x64     |

配置输出路径可以让生成文件结构更清晰，避免 Debug、Release、Win32、x64 文件混在一起。

### 5.6 头文件路径配置

当代码中使用 `#include` 引用其他目录下的头文件时，需要配置头文件搜索路径。

配置路径：

```text
项目属性 -> 配置属性 -> C/C++ -> 常规 -> 附加包含目录
```

示例：

```text
$(ProjectDir)include
$(SolutionDir)common\include
```

配置步骤：

1. 右键项目，选择 `属性`；

2. 进入：

   ```text
   配置属性 -> C/C++ -> 常规
   ```

3. 找到 `附加包含目录`；

4. 点击右侧下拉按钮，选择 `编辑`；

5. 添加头文件所在目录；

6. 点击 `确定`；

7. 重新生成项目。

   ![](pictures/头文件路径配置.png)

注意：

* 配置的是目录，不是具体的 `.h` 文件；
* 如果头文件在 `include` 文件夹中，一般填写 `include` 文件夹路径；
* 如果报错 `无法打开包括文件`，优先检查这里。

### 5.7 源文件路径配置

源文件一般需要加入项目后才会参与编译。仅仅把 `.c` 或 `.cpp` 文件放到工程目录中，不一定会自动参与编译。

添加源文件的常用方法：

```text
右键项目 -> 添加 -> 现有项
```

然后选择需要加入的 `.c` 或 `.cpp` 文件。

也可以通过：

```text
右键源文件 -> 添加 -> 新建项
```

创建新的源文件。

注意：

* 源文件必须包含在项目中，才会参与编译；
* 被 `从项目中排除` 的源文件不会参与编译；
* 如果函数已经写好但链接时报找不到函数，可能是对应 `.c/.cpp` 没有加入项目。

### 5.8 库文件路径配置

当项目需要调用 `.lib` 库文件时，需要配置库文件路径。

配置路径：

```text
项目属性 -> 配置属性 -> 链接器 -> 常规 -> 附加库目录
```

示例：

```text
$(SolutionDir)lib\$(Platform)\$(Configuration)
```

配置步骤：

1. 右键项目，选择 `属性`；

2. 进入：

   ```text
   配置属性 -> 链接器 -> 常规
   ```

3. 找到 `附加库目录`；

4. 添加 `.lib` 文件所在目录；

5. 点击 `应用`。

注意：这里配置的是 `.lib` 所在的文件夹路径，不是具体的 `.lib` 文件名。

### 5.9 调用库配置

调用库文件通常需要完成三件事：包含头文件、配置库目录、添加库文件名。

#### 5.9.1 包含头文件

在代码中包含库对应的头文件：

```cpp
#include "xxx.h"
```

同时在 `附加包含目录` 中添加头文件所在路径。

#### 5.9.2 配置库目录

配置路径：

```text
项目属性 -> 配置属性 -> 链接器 -> 常规 -> 附加库目录
```

添加 `.lib` 文件所在目录。

#### 5.9.3 添加库文件名

配置路径：

```text
项目属性 -> 配置属性 -> 链接器 -> 输入 -> 附加依赖项
```

示例：

```text
my_math.lib
user_driver.lib
```

完整流程如下：

```text
头文件路径 -> 附加包含目录
库文件路径 -> 附加库目录
库文件名称 -> 附加依赖项
```

如果是动态库 `.dll`，除了配置 `.lib` 参与链接外，还要保证运行时能找到 `.dll` 文件。常见做法是把 `.dll` 放到 `.exe` 同级目录下。

### 5.10 宏定义

宏定义常用于控制编译开关、区分 Debug/Release、打开或关闭某些功能。

配置路径：

```text
项目属性 -> 配置属性 -> C/C++ -> 预处理器 -> 预处理器定义
```

示例：

```text
DEBUG
USE_CAN
ENABLE_LOG
_CRT_SECURE_NO_WARNINGS
```

![](pictures/宏定义.png)

代码中可以这样使用：

```cpp
#ifdef ENABLE_LOG
printf("log enable\n");
#endif
```

常见用途：

| 宏定义                       | 作用                |
| ------------------------- | ----------------- |
| `DEBUG`                   | 表示当前为调试版本         |
| `NDEBUG`                  | 表示关闭 assert 等调试功能 |
| `_CRT_SECURE_NO_WARNINGS` | 关闭部分 C 标准库安全警告    |
| `USE_XXX`                 | 控制某个模块是否参与编译      |

注意：

* 多个宏之间用分号分隔；
* 不同配置下宏定义可能不同；
* 如果 Debug 正常、Release 异常，要检查两个配置下的宏定义是否一致。

### 5.11 运行库配置

运行库决定程序使用哪种 C/C++ 运行时库。

配置路径：

```text
项目属性 -> 配置属性 -> C/C++ -> 代码生成 -> 运行库
```

常见选项如下：

| 选项               | 含义                 | 常见场景        |
| ---------------- | ------------------ | ----------- |
| 多线程 DLL `/MD`    | 使用动态运行库，Release 常用 | 发布版本        |
| 多线程调试 DLL `/MDd` | 使用动态调试运行库，Debug 常用 | 调试版本        |
| 多线程 `/MT`        | 使用静态运行库，Release 可用 | 不想依赖运行库 DLL |
| 多线程调试 `/MTd`     | 使用静态调试运行库，Debug 可用 | 调试版本        |

![](pictures/运行库配置.png)

配置建议：

1. Debug 配置一般使用 `/MDd` 或 `/MTd`；
2. Release 配置一般使用 `/MD` 或 `/MT`；
3. 主程序和静态库项目的运行库配置尽量保持一致；
4. 如果运行库不一致，可能出现链接冲突或运行异常。

### 5.12 安全检查

安全检查用于增强程序运行时的安全性，例如检测部分缓冲区溢出问题。

常见配置位置：

```text
项目属性 -> 配置属性 -> C/C++ -> 代码生成 -> 安全检查
```

![](pictures/安全检查.png)

常见选项：

| 选项            | 说明           |
| ------------- | ------------ |
| 启用安全检查 `/GS`  | 编译器加入栈安全检查代码 |
| 禁用安全检查 `/GS-` | 不加入该检查代码     |

一般情况下建议保持默认启用，不建议随意关闭。只有在明确知道关闭原因，并且项目要求关闭时，才进行修改。

## 6. 工程生成和运行

### 6.1 生成解决方案

生成解决方案会编译当前解决方案中参与生成的项目。

操作方法：

```text
生成 -> 生成解决方案
```

快捷键：

```text
Ctrl + Shift + B
```

### 6.2 重新生成解决方案

重新生成会先清理旧的中间文件，再重新编译整个解决方案。

操作路径：

```text
生成 -> 重新生成解决方案
```

适合场景：

* 修改了大量工程配置；
* 编译结果异常；
* 怀疑中间文件没有更新；
* 从别的电脑拷贝工程后首次编译。

### 6.3 清理解决方案

清理解决方案会删除编译生成的中间文件和输出文件。

操作路径：

```text
生成 -> 清理解决方案
```

常用于重新编译前清空旧文件。

### 6.4 运行程序

常用运行方式如下：

| 操作      | 快捷键                 | 说明             |
| ------- | ------------------- | -------------- |
| 开始调试    | `F5`                | 运行程序并进入调试状态    |
| 开始执行不调试 | `Ctrl + F5`         | 直接运行程序，不进入断点调试 |
| 停止调试    | `Shift + F5`        | 停止当前调试         |
| 重新启动    | `Ctrl + Shift + F5` | 重新运行当前调试程序     |

调试阶段常用 `F5`，普通运行查看输出时可以使用 `Ctrl + F5`。

## 7. 工程调试

### 7.1 断点创建

断点用于让程序运行到指定代码行时暂停，方便查看变量、内存和函数调用关系。

创建断点的方法：

方法一：

1. 打开源文件；
2. 将光标放到需要暂停的代码行；
3. 按快捷键：

   ```text
   F9
   ```

方法二：

1. 在代码行最左侧灰色区域点击鼠标；
2. 出现红色圆点，表示断点创建成功。

![断点调试](pictures/VS2019_005_断点调试.png)

### 7.2 断点删除

删除单个断点：

1. 找到有红色圆点的代码行；
2. 再次点击红色圆点；
3. 红点消失，断点删除。

也可以选中断点所在行，按：

```text
F9
```

删除所有断点：

![](pictures/删除所有断点.png)

```text
调试 -> 删除所有断点
```

### 7.3 断点禁用

禁用断点表示暂时不让断点生效，但保留断点位置。

操作步骤：

1. 右键点击断点红点；

2. 选择：

   ![](pictures/禁用断点.png)

   ```text
   禁用断点
   ```

3. 断点图标会发生变化，表示该断点当前不生效。

   ![](pictures/空心断点.png)

适合场景：

* 当前暂时不想停在该位置；
* 后面可能还要继续使用该断点；
* 不想删除后再重新找位置。

### 7.4 断点启用

启用断点表示让被禁用的断点重新生效。

操作步骤：

1. 右键点击被禁用的断点；

2. 选择：

   ```text
   启用断点
   ```

3. 程序再次运行到该行时会暂停。


### 7.5 条件断点

条件断点表示只有满足指定条件时，程序才会暂停。普通断点是程序运行到该行就暂停，而条件断点是在普通断点的基础上增加判断条件，只有条件成立时才会真正停下来。

![条件断点设置界面](pictures/右击条件断点.png)

条件断点适合循环次数很多，但只想观察某一次特殊情况的场景。例如循环执行 1000 次，但只想在 `i == 500` 时暂停，就可以使用条件断点。

设置步骤：

1. 先在需要暂停的代码行创建普通断点；

2. 右键点击断点红点；

3. 在右键菜单中选择：

   ```text
   条件
   ```

4. 在弹出的条件断点设置窗口中输入条件表达式，例如：

   ```cpp
   i == 10
   ```

   或：

   ```cpp
   count > 100
   ```

5. 设置完成后继续运行程序；

   ![](pictures/条件设置.png)

6. 程序运行到该断点位置时，只有条件满足才会暂停。

示例代码：

```cpp
for (int i = 0; i < 1000; i++)
{
    sum += i;
}
```

如果只想在 `i == 500` 时暂停，可以在循环内部设置条件断点：

```cpp
i == 500
```

这样程序不会在每一次循环时都暂停，而是只会在 `i` 等于 500 时暂停，方便观察特定情况下变量的值。

### 7.6 监视窗口

监视窗口用于查看变量、表达式、数组元素、指针指向的值。程序运行到断点暂停后，可以通过监视窗口观察变量当前的数值变化。

![监视窗口查看变量](pictures/监视窗口2.png)

打开路径：

```text
调试 -> 窗口 -> 监视 -> 监视 1
```

![监视窗口查看变量](pictures/监视窗口.png)

使用步骤：

1. 程序运行到断点后暂停；
2. 打开监视窗口；
3. 在监视窗口中输入变量名或表达式；
4. 查看变量当前值；
5. 单步执行程序，观察变量值是否发生变化。

常见输入内容如下：

```text
a
a + b
arr[0]
*p
&buffer[0]
```

说明：

| 输入内容         | 含义               |
| ------------ | ---------------- |
| `a`          | 查看变量 `a` 的值      |
| `a + b`      | 查看表达式 `a + b` 的值 |
| `arr[0]`     | 查看数组第 0 个元素      |
| `*p`         | 查看指针 `p` 指向的内容   |
| `&buffer[0]` | 查看数组首元素地址        |

监视窗口是调试时最常用的窗口之一，适合用来观察变量值是否符合预期。

### 7.7 内存窗口

内存窗口用于查看某个地址开始的一段内存内容。相比监视窗口，内存窗口更偏底层，可以直接观察内存地址中的数据。

![内存窗口查看数据](pictures/内存窗口查看数据.png)

打开路径：

```text
调试 -> 窗口 -> 内存 -> 内存 1
```

使用步骤：

1. 程序运行到断点后暂停；

2. 打开内存窗口；

3. 在内存窗口地址栏中输入变量地址，例如：

   ```cpp
   &p
   ```

   或：

   ```cpp
   &buffer[0]
   ```

4. 查看该地址开始的一段内存数据。

内存窗口常用于查看数组、结构体、指针、通信缓冲区等内容。

例如有如下数组：

```cpp
unsigned char buffer[4] = {0x11, 0x22, 0x33, 0x44};
```

可以在内存窗口中输入：

```cpp
&buffer[0]
```

然后观察数组在内存中的实际存放情况。

注意：内存窗口查看的是地址中的原始数据，适合排查指针、数组越界、通信数据缓存等问题。

### 7.8 线程窗口

线程窗口用于查看当前程序中的线程信息。当程序是多线程程序时，可以通过线程窗口查看每个线程的执行位置和状态。

![线程窗口内容](pictures/线程窗口内容.png)

打开路径：

```text
调试 -> 窗口 -> 线程
```

线程窗口可以查看以下内容：

| 内容    | 说明              |
| ----- | --------------- |
| 线程 ID | 当前线程编号          |
| 线程名称  | 线程名称或系统显示名称     |
| 当前位置  | 当前线程执行到的函数或代码位置 |
| 状态    | 线程当前运行或暂停状态     |

当程序运行到断点暂停时，如果程序中存在多个线程，可以在线程窗口中切换不同线程，查看不同线程当前执行到的位置。

线程窗口常用于分析：

1. 当前程序有几个线程；
2. 哪个线程正在执行目标代码；
3. 多线程程序是否出现线程卡死；
4. 多线程调试时切换不同线程观察执行状态。

### 7.9 调用堆栈窗口

调用堆栈窗口用于查看函数调用关系。程序运行到断点暂停时，调用堆栈可以显示当前函数是被哪些函数一级一级调用进来的。

![调用堆栈窗口](pictures/调用堆栈窗口.png)

打开路径：

```text
调试 -> 窗口 -> 调用堆栈
```

例如程序调用关系如下：

```text
main -> Motor_Init -> Driver_Init -> GPIO_Init
```

当程序停在 `GPIO_Init` 函数中时，调用堆栈窗口可以看到它是从 `main` 函数开始，经过 `Motor_Init`、`Driver_Init`，最后进入 `GPIO_Init` 的。

调用堆栈常用于分析：

1. 当前函数是从哪里被调用的；
2. 程序为什么会进入某个函数；
3. 程序异常时崩溃前的调用路径；
4. 多层函数嵌套时的执行关系。

调用堆栈对排查程序异常非常重要。比如程序进入了错误处理函数，可以通过调用堆栈向上查找，是哪个函数调用导致的错误。

### 7.10 单步调试

单步调试用于逐行分析程序执行过程。程序运行到断点暂停后，可以通过单步调试观察程序每一行代码的执行情况。

![单步调试按钮](pictures/单步调试按钮.png)

常用调试操作如下：

| 操作   | 快捷键                 | 说明                        |
| ---- | ------------------- | ------------------------- |
| 全速运行 | `F5`                | 从当前位置继续运行，直到遇到下一个断点或程序结束  |
| 重新运行 | `Ctrl + Shift + F5` | 停止当前调试并重新启动               |
| 逐过程  | `F10`               | 执行当前行，如果当前行是函数调用，则不进入函数内部 |
| 逐语句  | `F11`               | 执行当前行，如果当前行是函数调用，则进入函数内部  |
| 跳出   | `Shift + F11`       | 从当前函数执行完并返回上一层函数          |

逐过程和逐语句区别如下：

| 操作        | 是否进入函数内部 | 使用场景         |
| --------- | -------- | ------------ |
| 逐过程 `F10` | 不进入      | 只关心函数调用结果    |
| 逐语句 `F11` | 进入       | 需要查看函数内部执行过程 |

示例代码：

```cpp
int add(int a, int b)
{
    return a + b;
}

int main(void)
{
    int ret = add(1, 2);
    return 0;
}
```

当程序停在下面这一行时：

```cpp
int ret = add(1, 2);
```

如果按 `F10`，程序会直接执行完 `add` 函数，不进入函数内部。

如果按 `F11`，程序会进入 `add` 函数内部，可以继续观察：

```cpp
return a + b;
```

简单理解：

```text
F10：不进函数，只看结果
F11：进入函数，看内部细节
```

## 8. 常见问题和排查方法总结

在 VS2019 使用过程中，常见问题主要集中在编译错误、链接错误、配置不一致和文件未加入项目等方面。排查问题时，应先判断错误发生在编译阶段还是链接阶段。

### 8.1 找不到头文件

常见报错：

```text
无法打开包括文件: “xxx.h”: No such file or directory
```

该问题一般属于编译阶段错误，说明编译器在当前搜索路径中没有找到对应的头文件。

常见原因如下：

1. 头文件本身不存在；
2. 头文件路径写错；
3. 头文件没有放在工程目录中；
4. `附加包含目录` 没有配置；
5. 当前 Debug/Release 或 Win32/x64 平台下没有配置该路径。

排查思路：

1. 先确认磁盘中确实存在该 `.h` 文件；
2. 检查 `#include` 中的文件名是否拼写正确；
3. 检查项目属性中的头文件路径配置：

   ```text
   项目属性 -> 配置属性 -> C/C++ -> 常规 -> 附加包含目录
   ```

4. 确认当前修改的是正确的配置和平台；
5. 重新生成解决方案。

总结：

```text
找不到 .h 文件，优先检查 附加包含目录。
```

### 8.2 找不到库文件

常见报错：

```text
LINK : fatal error LNK1104: 无法打开文件 “xxx.lib”
```

该问题一般属于链接阶段错误，说明链接器没有找到需要链接的 `.lib` 文件。

常见原因如下：

1. `.lib` 文件不存在；
2. `.lib` 文件还没有生成；
3. `附加库目录` 配置错误；
4. `附加依赖项` 中库文件名写错；
5. Debug/Release 或 Win32/x64 平台不一致。

排查思路：

1. 确认 `.lib` 文件真实存在；
2. 检查库文件所在目录是否配置正确：

   ```text
   项目属性 -> 配置属性 -> 链接器 -> 常规 -> 附加库目录
   ```

3. 检查库文件名称是否填写正确：

   ```text
   项目属性 -> 配置属性 -> 链接器 -> 输入 -> 附加依赖项
   ```

4. 确认主程序和库文件的平台一致；
5. 如果是自建库，应先生成库项目，再生成主程序项目。

总结：

```text
找不到 .lib 文件，优先检查 附加库目录 和 附加依赖项。
```

### 8.3 无法解析的外部符号

常见报错：

```text
error LNK2019: 无法解析的外部符号
```

该问题属于链接阶段错误，通常说明函数已经被声明或调用，但是链接器没有找到对应的函数实现。

常见原因如下：

1. 函数只有声明，没有实现；
2. 实现函数的 `.c` 或 `.cpp` 文件没有加入项目；
3. 调用了库函数，但没有链接对应的 `.lib` 文件；
4. C 和 C++ 混合编译时，函数名修饰不一致；
5. Debug/Release 或 Win32/x64 链接了错误版本的库。

排查思路：

1. 检查函数是否真的有实现；
2. 检查实现函数的源文件是否已经加入项目；
3. 检查库文件路径和库文件名是否配置正确；
4. 如果 C 函数被 C++ 调用，可以检查是否需要：

   ```cpp
   extern "C"
   {
   #include "xxx.h"
   }
   ```

5. 清理后重新生成解决方案。

总结：

```text
LNK2019 通常不是语法问题，而是链接阶段找不到函数实现。
```

### 8.4 Debug 正常但 Release 异常

Debug 正常但 Release 异常，通常说明两个配置下的工程属性不一致，或者 Release 优化后暴露了代码中的隐藏问题。

常见原因如下：

1. Debug 和 Release 的宏定义不同；
2. Debug 和 Release 的运行库配置不同；
3. Release 开启优化后，未初始化变量等问题暴露；
4. Debug 和 Release 的输出路径或库路径不同；
5. Release 链接的库版本不正确。

排查思路：

1. 对比 Debug 和 Release 的项目属性；
2. 检查 `预处理器定义`；
3. 检查 `运行库`；
4. 检查 `附加包含目录`、`附加库目录`、`附加依赖项`；
5. 清理后重新生成。

总结：

```text
Debug 正常但 Release 异常，优先对比两种配置下的项目属性。
```

### 8.5 x64 正常但 Win32 异常

x64 正常但 Win32 异常，通常说明不同平台下的配置没有保持一致，或者链接了错误平台的库文件。

常见原因如下：

1. 只配置了 x64，没有配置 Win32；
2. 使用了 x64 版本的 `.lib` 去链接 Win32 程序；
3. 输出路径中平台目录混乱；
4. 第三方库没有提供 Win32 版本；
5. 当前活动平台选择错误。

排查思路：

1. 打开项目属性页，把平台切换到 Win32；
2. 检查 Win32 下的所有路径配置；
3. 确认 Win32 使用的是 Win32 版本库；
4. 检查输出目录和中间目录是否区分平台；
5. 重新生成解决方案。

总结：

```text
x64 和 Win32 是不同平台，库文件和工程配置需要保持一致。
```

### 8.6 常见问题对应关系总结

| 问题现象                  | 所属阶段     | 优先检查内容                             |
| ------------------------- | ------------ | ---------------------------------------- |
| 找不到 `.h` 文件          | 编译阶段     | 附加包含目录、头文件路径                 |
| 找不到 `.lib` 文件        | 链接阶段     | 附加库目录、附加依赖项                   |
| 无法解析外部符号          | 链接阶段     | 函数实现、源文件是否加入项目、库是否链接 |
| Debug 正常但 Release 异常 | 配置问题     | Debug/Release 配置差异                   |
| x64 正常但 Win32 异常     | 平台问题     | Win32/x64 配置和库平台是否一致           |
| 写了源文件但不编译        | 工程管理问题 | 文件是否被包括在项目中                   |
| 按 F5 运行错误项目        | 启动项问题   | 是否设置正确启动项                       |

## 9. 学习练习总结

本章节用于总结 VS2019 基础学习过程中需要完成的练习内容。通过练习，应能够掌握项目创建、文件添加、程序生成、程序运行和基本调试操作。

### 9.1 练习一：创建控制台项目

练习目标：

1. 熟悉 VS2019 创建项目流程；
2. 理解解决方案和项目之间的关系；
3. 能够创建一个可以运行的控制台程序；
4. 掌握生成和运行程序的方法。

练习要求：

1. 创建一个 VS2019 控制台项目；
2. 添加 `main.cpp` 文件；
3. 编写简单打印程序；
4. 使用 `Ctrl + Shift + B` 生成解决方案；
5. 使用 `Ctrl + F5` 运行程序。

示例代码：

```cpp
#include <stdio.h>

int main(void)
{
    printf("Hello Visual Studio 2019!\n");
    return 0;
}
```

运行后控制台应输出：

```text
Hello Visual Studio 2019!
```

### 9.2 练习二：添加头文件和源文件

练习目标：

1. 掌握头文件和源文件的创建方法；
2. 理解函数声明和函数实现的区别；
3. 能够在主程序中调用自己编写的函数；
4. 理解源文件必须加入项目后才会参与编译。

练习要求：

1. 新建 `math_user.h`；
2. 新建 `math_user.cpp`；
3. 在头文件中声明函数；
4. 在源文件中实现函数；
5. 在 `main.cpp` 中调用函数；
6. 生成并运行程序。

`math_user.h`：

```cpp
#ifndef MATH_USER_H
#define MATH_USER_H

int add(int a, int b);

#endif
```

`math_user.cpp`：

```cpp
#include "math_user.h"

int add(int a, int b)
{
    return a + b;
}
```

`main.cpp`：

```cpp
#include <stdio.h>
#include "math_user.h"

int main(void)
{
    int ret = add(1, 2);
    printf("ret = %d\n", ret);
    return 0;
}
```

运行后应输出：

```text
ret = 3
```

### 9.3 练习三：断点调试

练习目标：

1. 掌握断点创建方法；
2. 能够使用 `F5` 开始调试；
3. 能够使用 `F10` 和 `F11` 进行单步调试；
4. 能够通过监视窗口查看变量值；
5. 能够通过调用堆栈查看函数调用关系。

练习要求：

1. 在 `ret = add(1, 2);` 这一行设置断点；
2. 按 `F5` 开始调试；
3. 使用 `F10` 逐过程调试；
4. 使用 `F11` 逐语句进入函数；
5. 打开监视窗口查看 `ret` 的值；
6. 打开调用堆栈窗口查看函数调用关系。

调试时重点观察：

```text
F10 不进入函数内部；
F11 会进入函数内部。
```

### 9.4 练习四：条件断点

练习目标：

1. 掌握条件断点的设置方法；
2. 理解普通断点和条件断点的区别；
3. 能够让程序只在满足指定条件时暂停；
4. 能够配合监视窗口观察变量变化。

练习要求：

1. 编写一个 `for` 循环；
2. 在循环内部设置断点；
3. 设置条件断点 `i == 5`；
4. 运行程序，观察程序是否只在 `i` 等于 5 时暂停。

示例代码：

```cpp
#include <stdio.h>

int main(void)
{
    int sum = 0;

    for (int i = 0; i < 10; i++)
    {
        sum += i;
    }

    printf("sum = %d\n", sum);
    return 0;
}
```

条件表达式：

```cpp
i == 5
```

运行到条件断点时，可以在监视窗口中查看 `i` 和 `sum` 的值。

## 10. 验收重点总结

VS2019 基础操作验收时，应重点掌握工程创建、工程配置、工程调试和常见错误排查方法。验收时不仅要会操作菜单，还要理解每个配置项的作用。

### 10.1 工程创建验收重点

| 分类          | 必须掌握内容                         |
| ------------- | ------------------------------------ |
| 创建项目      | 能够创建 VS2019 空项目或控制台项目   |
| 打开项目      | 能够通过 `.sln` 打开完整工程         |
| 加载/卸载项目 | 能够卸载项目并重新加载项目           |
| 设置启动项    | 能够设置正确的启动项目               |
| 添加项目      | 能够向解决方案中添加新项目或现有项目 |
| 文件管理      | 能够创建、打开、包括和排除文件       |

### 10.2 工程配置验收重点

| 分类         | 必须掌握内容                             |
| ------------ | ---------------------------------------- |
| 解决方案配置 | 理解 Debug 和 Release 的区别             |
| 平台配置     | 理解 Win32 和 x64 的区别                 |
| 项目类型     | 理解 `.exe`、`.lib`、`.dll` 的区别       |
| 输出路径     | 能够配置输出目录和中间目录               |
| 头文件路径   | 能够配置附加包含目录                     |
| 库文件路径   | 能够配置附加库目录                       |
| 调用库       | 能够配置附加依赖项                       |
| 宏定义       | 能够配置预处理器定义                     |
| 运行库       | 理解 `/MD`、`/MDd`、`/MT`、`/MTd` 的含义 |
| 安全检查     | 了解安全检查配置的作用                   |

### 10.3 工程调试验收重点

| 分类     | 必须掌握内容                          |
| -------- | ------------------------------------- |
| 断点     | 创建、删除、启用、禁用断点            |
| 条件断点 | 设置满足条件才暂停的断点              |
| 监视窗口 | 查看变量、表达式、数组和指针          |
| 内存窗口 | 查看指定地址的内存内容                |
| 线程窗口 | 查看多线程程序中的线程状态            |
| 调用堆栈 | 查看函数调用关系                      |
| 单步调试 | 掌握 F5、F10、F11、Shift + F11 等操作 |

### 10.4 常见错误排查重点

| 问题             | 优先检查内容                       |
| ---------------- | ---------------------------------- |
| 找不到 `.h`      | 附加包含目录                       |
| 找不到 `.lib`    | 附加库目录、附加依赖项             |
| 函数无法解析     | 源文件是否加入项目、库是否正确链接 |
| 程序运行不对     | 启动项、Debug/Release 配置         |
| 平台链接错误     | Win32/x64 是否一致                 |
| 文件写了但不编译 | 是否被包括在项目中                 |

## 11. 快捷键汇总

VS2019 中常用快捷键可以提高工程生成、运行和调试效率。学习时应重点记住生成、运行和单步调试相关快捷键。

| 功能           | 快捷键              |
| -------------- | ------------------- |
| 生成解决方案   | `Ctrl + Shift + B`  |
| 开始调试       | `F5`                |
| 开始执行不调试 | `Ctrl + F5`         |
| 停止调试       | `Shift + F5`        |
| 重新启动调试   | `Ctrl + Shift + F5` |
| 设置/取消断点  | `F9`                |
| 逐过程         | `F10`               |
| 逐语句         | `F11`               |
| 跳出函数       | `Shift + F11`       |
| 查找           | `Ctrl + F`          |
| 替换           | `Ctrl + H`          |
| 保存当前文件   | `Ctrl + S`          |
| 全部保存       | `Ctrl + Shift + S`  |

常用调试快捷键可以简单记为：

```text
F5：继续运行
F9：设置或取消断点
F10：逐过程，不进入函数
F11：逐语句，进入函数
Shift + F11：跳出当前函数
```

## 12. 学习结论

通过本次学习，掌握了 Visual Studio 2019 的基本工程创建、文件管理、工程配置和调试方法。VS2019 不只是代码编辑器，更重要的是它通过解决方案和项目来管理编译、链接和调试流程。

在实际使用中，需要重点关注以下几点：

1. 修改配置前先确认当前是 Debug 还是 Release，是 Win32 还是 x64；
2. 解决方案用于管理项目，项目用于管理文件和编译配置；
3. 源文件必须加入项目后才会参与编译；
4. 头文件路径、库文件路径、库文件名是三个不同配置，不能混淆；
5. 多项目工程中要正确设置启动项；
6. 主程序和库文件的平台配置需要保持一致；
7. 调试时要熟练使用断点、监视窗口、内存窗口和调用堆栈窗口；
8. 遇到编译或链接错误时，先判断错误属于编译阶段还是链接阶段。

总结来说，VS2019 的学习重点不只是会点击菜单，而是要理解：

```text
工程如何创建；
文件如何加入项目；
配置如何影响编译和链接；
调试工具如何帮助定位问题。
```

掌握这些基础操作后，后续学习静态库生成与使用、单元测试等内容会更加容易。