C++ 面试常问问题汇总:基础语法、面向对象、STL、多线程与设计模式

C++ 面试常问问题汇总:基础语法、面向对象、STL、多线程与设计模式

本文系统整理 C++ 面试中的高频问题,覆盖基础语法、面向对象、内存管理、智能指针、STL、C++11/14/17、多线程并发、编译链接、设计模式以及项目实战追问。适合算法工程师、机器人控制工程师、嵌入式 C++、后端 C++ 等方向面试复习使用。


目录


一、C++ 基础语法高频问题

问题 面试重点
C 和 C++ 的区别? C++ 支持面向对象、模板、异常、STL、RAII、泛型编程等特性
structclass 的区别? struct 默认访问权限是 publicclass 默认访问权限是 private
const 的作用? 可修饰变量、指针、函数参数、返回值、成员函数,用于限制修改
static 的作用? 可用于静态局部变量、静态成员变量、静态成员函数、文件作用域限制
inline 的作用? 建议编译器将函数内联展开,减少函数调用开销,但不是强制
extern 的作用? 声明外部变量或函数,常用于多文件共享全局变量
volatile 的作用? 告诉编译器变量可能被外部改变,避免编译器过度优化
sizeofstrlen 的区别? sizeof 求对象所占内存大小,通常编译期确定;strlen 运行期计算字符串长度
new/deletemalloc/free 的区别? new/delete 会调用构造和析构函数,malloc/free 只负责内存分配和释放
引用和指针的区别? 引用必须初始化且不能重新绑定;指针可以为空,也可以改变指向

常见追问

1. const 修饰指针有哪些情况?
cpp 复制代码
const int* p1;      // 指向常量的指针,不能通过 p1 修改值,但 p1 可以改变指向
int const* p2;      // 与 const int* 等价
int* const p3;      // 指针常量,p3 不能改变指向,但可以修改指向的值
const int* const p4;// 指向常量的指针常量,指向和值都不能改
2. newmalloc 的核心区别
cpp 复制代码
int* p1 = new int(10);              // 分配内存并初始化
int* p2 = (int*)malloc(sizeof(int)); // 只分配内存,不调用构造函数

 delete p1;
 free(p2);

二、面向对象高频问题

问题 面试重点
面向对象三大特性? 封装、继承、多态
重载、重写、隐藏的区别? 重载是同作用域函数参数不同;重写是子类覆盖父类虚函数;隐藏是子类同名函数遮蔽父类函数
虚函数是什么? 使用 virtual 修饰,通过虚函数表实现运行时多态
纯虚函数是什么? virtual void f() = 0;,含纯虚函数的类是抽象类
构造函数可以是虚函数吗? 不可以,对象构造过程中虚表尚未完整建立
析构函数为什么常设为虚函数? 防止通过父类指针删除子类对象时,只调用父类析构函数导致资源泄漏
虚函数表是什么? 存储虚函数地址的表,对象中通常含有虚表指针
多态的实现原理? 父类指针或引用 + 虚函数 + 虚函数表动态绑定
菱形继承问题? 多继承导致公共基类被重复继承,可通过虚继承解决
拷贝构造和赋值运算符区别? 拷贝构造用于初始化新对象,赋值运算符用于已有对象之间赋值

多态示例

cpp 复制代码
#include <iostream>
#include <memory>

class Controller {
public:
    virtual void computeControl() = 0;
    virtual ~Controller() = default;
};

class PIDController : public Controller {
public:
    void computeControl() override {
        std::cout << "PID control" << std::endl;
    }
};

class MPCController : public Controller {
public:
    void computeControl() override {
        std::cout << "MPC control" << std::endl;
    }
};

int main() {
    std::unique_ptr<Controller> controller = std::make_unique<MPCController>();
    controller->computeControl();
    return 0;
}

三、内存管理高频问题

问题 面试重点
C++ 内存分区有哪些? 栈区、堆区、全局/静态区、常量区、代码区
栈和堆的区别? 栈自动管理、速度快、空间较小;堆手动管理、空间较大、容易内存泄漏
什么是内存泄漏? 申请的内存没有被释放,导致长期运行时内存占用不断增加
什么是野指针? 指向无效内存地址的指针
什么是悬空指针? 指针指向已经释放的对象
如何避免内存泄漏? 使用 RAII、智能指针、容器管理资源,减少裸 new/delete
深拷贝和浅拷贝区别? 浅拷贝只复制指针地址;深拷贝会重新申请内存并复制数据内容
析构函数作用? 对象生命周期结束时释放资源
RAII 是什么? Resource Acquisition Is Initialization,用对象生命周期自动管理资源
内存对齐是什么? 按特定边界存储数据,提高 CPU 访问效率

RAII 示例

cpp 复制代码
#include <iostream>
#include <fstream>

class FileGuard {
public:
    explicit FileGuard(const std::string& file_name) {
        file_.open(file_name);
    }

    ~FileGuard() {
        if (file_.is_open()) {
            file_.close();
        }
    }

private:
    std::fstream file_;
};

RAII 的核心思想是:资源在对象构造时获取,在对象析构时自动释放。这样可以减少资源泄漏,也能提高异常安全性。


四、智能指针高频问题

问题 面试重点
unique_ptr 是什么? 独占所有权,不能拷贝,只能移动
shared_ptr 是什么? 共享所有权,通过引用计数管理对象生命周期
weak_ptr 是什么? 弱引用,不增加引用计数,常用于解决 shared_ptr 循环引用
shared_ptr 循环引用是什么? 两个对象互相持有 shared_ptr,导致引用计数无法归零
make_shared 有什么好处? 一次内存分配,提高效率,并且异常安全性更好
智能指针能完全替代裸指针吗? 不能。非拥有关系、性能敏感场景仍可使用裸指针或引用
shared_ptr 是否线程安全? 引用计数操作通常是线程安全的,但被管理对象本身不是线程安全的

unique_ptr 示例

cpp 复制代码
#include <memory>

class Planner {};

int main() {
    std::unique_ptr<Planner> planner = std::make_unique<Planner>();
    // std::unique_ptr<Planner> p2 = planner; // 错误,不能拷贝
    std::unique_ptr<Planner> p3 = std::move(planner); // 可以移动
    return 0;
}

shared_ptrweak_ptr 示例

cpp 复制代码
#include <memory>

class Node {
public:
    std::shared_ptr<Node> next;
    std::weak_ptr<Node> prev;  // 用 weak_ptr 打破循环引用
};

五、STL 高频问题

问题 面试重点
vector 底层原理? 连续内存,支持随机访问,容量不足时动态扩容
vector 扩容机制? 容量不足时重新申请更大内存,然后拷贝或移动旧元素
vectorlist 区别? vector 随机访问快,插入删除慢;list 插入删除快,随机访问慢
mapunordered_map 区别? map 底层常为红黑树,有序;unordered_map 底层为哈希表,无序
setunordered_set 区别? set 有序去重;unordered_set 哈希去重,无序
dequevector 区别? deque 分段连续,支持头尾高效插入删除
迭代器失效是什么? 容器修改后,原迭代器可能指向无效位置
push_backemplace_back 区别? emplace_back 原地构造对象,减少临时对象
STL allocator 是什么? STL 的内存分配器,用于控制容器内存申请和释放
priority_queue 底层是什么? 默认使用大根堆

push_backemplace_back 对比

cpp 复制代码
#include <vector>
#include <string>

struct Student {
    std::string name;
    int age;

    Student(const std::string& n, int a) : name(n), age(a) {}
};

int main() {
    std::vector<Student> students;

    students.push_back(Student("Tom", 18)); // 先构造临时对象,再放入 vector
    students.emplace_back("Jack", 20);      // 直接在 vector 内部构造对象

    return 0;
}

六、C++11/14/17 高频问题

问题 面试重点
auto 的作用? 自动类型推导
decltype 的作用? 推导表达式类型
nullptrNULL 区别? nullptr 是空指针类型,更类型安全
右值引用是什么? T&&,用于移动语义和完美转发
移动语义是什么? 转移资源所有权,减少深拷贝开销
std::move 做了什么? 将对象强制转换为右值引用,本身不移动数据
完美转发是什么? 保持参数的左值或右值属性进行转发
Lambda 表达式是什么? 匿名函数对象,常用于回调、排序、自定义逻辑
std::functionstd::bind 是什么? 通用函数包装器和参数绑定工具
constexpr 是什么? 用于编译期常量计算
override 作用? 显式声明重写父类虚函数,避免误写
final 作用? 禁止类被继承或虚函数继续被重写

移动语义示例

cpp 复制代码
#include <iostream>
#include <vector>

int main() {
    std::vector<int> a = {1, 2, 3, 4};
    std::vector<int> b = std::move(a);

    std::cout << "b size: " << b.size() << std::endl;
    std::cout << "a size: " << a.size() << std::endl;

    return 0;
}

注意:std::move 本身不移动数据,它只是把对象转换成右值引用,真正的资源转移发生在移动构造或移动赋值中。


七、多线程并发高频问题

问题 面试重点
进程和线程区别? 进程是资源分配单位,线程是调度执行单位
C++ 如何创建线程? 使用 std::thread
mutex 是什么? 互斥锁,用于保护共享资源
lock_guardunique_lock 区别? lock_guard 简单自动加解锁;unique_lock 更灵活,可延迟加锁、手动解锁
死锁是什么? 多个线程互相等待对方释放资源,导致程序无法继续执行
如何避免死锁? 固定加锁顺序、减少锁粒度、使用 try_lock、使用 RAII 管理锁
condition_variable 是什么? 条件变量,用于线程等待和通知
atomic 是什么? 原子变量,支持无锁原子操作
线程安全是什么意思? 多线程访问共享资源时,程序行为仍然正确
生产者消费者模型? 一个线程生产数据,另一个线程消费数据,常用队列、锁和条件变量实现

生产者消费者简化示例

cpp 复制代码
#include <iostream>
#include <queue>
#include <thread>
#include <mutex>
#include <condition_variable>

std::queue<int> data_queue;
std::mutex mtx;
std::condition_variable cv;

void producer() {
    for (int i = 0; i < 5; ++i) {
        {
            std::lock_guard<std::mutex> lock(mtx);
            data_queue.push(i);
        }
        cv.notify_one();
    }
}

void consumer() {
    for (int i = 0; i < 5; ++i) {
        std::unique_lock<std::mutex> lock(mtx);
        cv.wait(lock, [] { return !data_queue.empty(); });
        int value = data_queue.front();
        data_queue.pop();
        std::cout << value << std::endl;
    }
}

int main() {
    std::thread t1(producer);
    std::thread t2(consumer);

    t1.join();
    t2.join();

    return 0;
}

八、编译、链接、程序运行高频问题

问题 面试重点
C++ 程序从源码到可执行文件经历哪些阶段? 预处理、编译、汇编、链接
头文件和源文件作用? 头文件放声明,源文件放实现
#include ""#include <> 区别? "" 优先搜索当前目录,<> 通常搜索系统路径
静态库和动态库区别? 静态库编译进可执行文件;动态库运行时加载
编译期和运行期区别? 编译期生成程序,运行期执行程序
链接错误常见原因? 函数只有声明没有定义、库未正确链接、符号重复定义
宏和 inline 函数区别? 宏是文本替换,没有类型检查;inline 函数有类型检查
模板为什么通常写在头文件? 模板实例化需要看到完整定义

编译流程

text 复制代码
源代码 .cpp
   ↓ 预处理
预处理文件 .i
   ↓ 编译
汇编文件 .s
   ↓ 汇编
目标文件 .o/.obj
   ↓ 链接
可执行文件

九、设计模式高频问题

设计模式是 C++ 面试中非常常见的内容,尤其是项目经历中涉及控制器、规划器、传感器模块、通信模块时,面试官很容易追问架构设计。


1. 单例模式 Singleton

核心作用

保证一个类在全局只有一个实例,并提供一个全局访问点。

常见场景
场景 示例
日志系统 Logger
配置管理器 ConfigManager
线程池管理器 ThreadPool
全局资源管理 ResourceManager
C++11 线程安全单例
cpp 复制代码
class Logger {
public:
    static Logger& instance() {
        static Logger logger;
        return logger;
    }

    void log(const std::string& msg) {
        // 输出日志
    }

private:
    Logger() = default;
    ~Logger() = default;

    Logger(const Logger&) = delete;
    Logger& operator=(const Logger&) = delete;
};
面试常问
问题 回答重点
单例模式怎么实现? 构造函数私有化,提供静态访问接口
懒汉式和饿汉式区别? 懒汉式用到时创建,饿汉式程序启动时创建
单例如何保证线程安全? C++11 局部静态变量初始化是线程安全的
单例模式缺点? 全局状态较强,测试困难,可能破坏依赖关系

2. 工厂模式 Factory

核心作用

工厂模式用于封装对象创建过程。调用者不直接 new 对象,而是通过工厂根据类型或配置创建具体对象。

适用场景
  • 多种对象具有统一接口;
  • 对象创建逻辑比较复杂;
  • 需要根据配置动态选择具体实现;
  • 希望降低调用者和具体类之间的耦合。
示例:根据配置创建不同规划器
cpp 复制代码
#include <iostream>
#include <memory>
#include <string>

class Planner {
public:
    virtual void plan() = 0;
    virtual ~Planner() = default;
};

class DWAPlanner : public Planner {
public:
    void plan() override {
        std::cout << "DWA Planner" << std::endl;
    }
};

class MPPIPlanner : public Planner {
public:
    void plan() override {
        std::cout << "MPPI Planner" << std::endl;
    }
};

class PlannerFactory {
public:
    static std::unique_ptr<Planner> createPlanner(const std::string& type) {
        if (type == "DWA") {
            return std::make_unique<DWAPlanner>();
        } else if (type == "MPPI") {
            return std::make_unique<MPPIPlanner>();
        }
        return nullptr;
    }
};

int main() {
    auto planner = PlannerFactory::createPlanner("MPPI");
    if (planner) {
        planner->plan();
    }
    return 0;
}
面试常问
问题 回答重点
工厂模式解决什么问题? 解耦对象创建和对象使用
简单工厂、工厂方法、抽象工厂区别? 创建复杂度和扩展方式不同
工厂模式优点? 隐藏创建细节,方便扩展,降低耦合
工厂模式缺点? 类数量增加,结构更复杂
什么时候适合用工厂模式? 有多个同类对象,需要根据配置或输入动态创建
在机器人项目中的典型使用
cpp 复制代码
createController("PID");
createController("MPC");
createController("MPPI");
createLocalPlanner("DWA");
createLocalPlanner("TEB");
createLocalPlanner("RPP");

3. 策略模式 Strategy

核心作用

策略模式将不同算法封装成不同策略类,使算法可以在运行时自由切换。

常见场景
场景 示例
路径跟踪控制器切换 PID / MPC / MPPI
局部规划器切换 DWA / TEB / RPP / MPPI
代价函数切换 距离代价 / 障碍物代价 / 平滑代价
排序规则切换 升序 / 降序 / 自定义优先级
轨迹评分方式切换 安全优先 / 平滑优先 / 速度优先
策略模式示例
cpp 复制代码
#include <iostream>
#include <memory>

class ControlStrategy {
public:
    virtual void compute() = 0;
    virtual ~ControlStrategy() = default;
};

class PIDStrategy : public ControlStrategy {
public:
    void compute() override {
        std::cout << "PID compute" << std::endl;
    }
};

class MPCStrategy : public ControlStrategy {
public:
    void compute() override {
        std::cout << "MPC compute" << std::endl;
    }
};

class RobotController {
public:
    void setStrategy(std::unique_ptr<ControlStrategy> strategy) {
        strategy_ = std::move(strategy);
    }

    void run() {
        if (strategy_) {
            strategy_->compute();
        }
    }

private:
    std::unique_ptr<ControlStrategy> strategy_;
};
面试常问
问题 回答重点
策略模式和工厂模式区别? 工厂负责创建对象,策略负责算法替换和行为切换
策略模式优点? 避免大量 if-else,方便扩展算法
策略模式缺点? 策略类数量增加,调用方需要了解不同策略

4. 观察者模式 Observer

核心作用

当一个对象状态发生变化时,自动通知多个依赖对象。

常见场景
场景 示例
ROS 订阅发布 Publisher / Subscriber
事件回调 键盘事件、传感器事件
GUI 系统 按钮点击通知
状态监控 机器人状态变化后通知多个模块
面试常问
问题 回答重点
观察者模式解决什么问题? 一对多通知问题
观察者模式和发布订阅模式区别? 发布订阅通常有中间消息总线,耦合更低
缺点是什么? 通知链复杂,调试困难,可能出现循环通知

5. 适配器模式 Adapter

核心作用

适配器模式用于把一个类的接口转换成客户端期望的接口。

机器人项目中的典型场景
text 复制代码
Gazebo 接口      → 统一 RobotInterface
MuJoCo 接口      → 统一 RobotInterface
真实底盘接口     → 统一 RobotInterface
不同雷达驱动接口 → 统一 SensorInterface
面试常问
问题 回答重点
适配器模式解决什么问题? 接口不兼容问题
类适配器和对象适配器区别? 类适配器通过继承实现,对象适配器通过组合实现
适配器和代理模式区别? 适配器改变接口,代理控制访问

6. 代理模式 Proxy

核心作用

代理模式通过代理对象控制对真实对象的访问。

常见场景
场景 示例
网络代理 远程调用
权限控制 登录校验
延迟加载 大对象按需创建
日志统计 调用前后记录日志

7. 装饰器模式 Decorator

核心作用

装饰器模式可以在不修改原始类代码的情况下,动态增强对象功能。

控制器中的理解
text 复制代码
BaseController
    ↓
SafetyControllerWrapper
    ↓
FilterControllerWrapper
    ↓
LogControllerWrapper

也就是说,可以给原始控制器逐层增加功能:

  • 输出限幅;
  • 输出滤波;
  • 安全检查;
  • 日志记录;
  • 异常保护。

8. 模板方法模式 Template Method

核心作用

父类定义整体流程骨架,子类实现具体步骤。

局部规划器流程示例
text 复制代码
1. 读取地图
2. 读取机器人状态
3. 读取目标点
4. 生成候选轨迹
5. 评价候选轨迹
6. 输出控制量

不同局部规划器可以复用整体流程,只重写核心算法步骤。


十、常见设计模式对比

模式 核心作用 典型关键词 机器人/算法项目示例
单例模式 保证全局唯一实例 全局配置、日志 参数管理器、日志系统
工厂模式 封装对象创建 create、new 解耦 创建 PID/MPC/MPPI 控制器
策略模式 封装算法切换 多算法、多策略 控制算法切换、局部规划器切换
观察者模式 状态变化通知 回调、订阅发布 ROS Topic、传感器事件回调
适配器模式 接口转换 兼容旧接口 Gazebo/MuJoCo/实物接口统一
代理模式 控制访问 权限、远程、缓存 远程控制接口、通信代理
装饰器模式 动态增强功能 Wrapper、包装器 控制器加限幅、滤波、安全检查
模板方法模式 固定流程,子类实现细节 流程复用 规划器统一流程框架

十一、C++ 项目常见追问

问题 面试重点
你项目中用到了哪些 C++ 特性? 智能指针、STL、多态、模板、多线程、RAII
你怎么管理对象生命周期? RAII、智能指针、避免裸 new/delete
你的模块之间怎么解耦? 接口类、多态、工厂模式、策略模式
你的代码怎么避免内存泄漏? 智能指针、容器、析构函数释放资源
程序崩溃怎么排查? gdb、core dump、日志、断点、AddressSanitizer
程序如何提高实时性? 减少动态分配、预分配内存、降低锁竞争、减少拷贝
你用过多线程吗? 传感器读取、控制循环、日志线程、通信线程
控制周期如何保证? 定时器、实时线程、减少阻塞、监控循环耗时

十二、机器人/控制算法岗 C++ 追问

问题 面试关注点
控制器为什么要用多态? 统一接口,方便切换 PID、MPC、MPPI 等控制器
局部规划器如何设计类结构? 基类接口 + 子类实现 + 工厂创建
实物和仿真接口如何统一? 使用适配器模式,将不同接口封装为统一抽象接口
控制循环怎么保证实时性? 固定频率、预分配、避免阻塞、减少动态内存申请
ROS 回调和主控制循环如何通信? 线程安全队列、mutex、atomic、双缓冲
怎么避免控制器输出突变? 滤波、限幅、平滑约束、控制增量约束
怎么组织参数配置? YAML/JSON + Config 类 + 参数校验
C++ 中如何封装一个控制器? 初始化、状态更新、轨迹输入、控制计算、输出控制量、reset

控制器接口设计示例

cpp 复制代码
class ControllerBase {
public:
    virtual bool initialize() = 0;
    virtual void reset() = 0;
    virtual void updateState(const RobotState& state) = 0;
    virtual void setReference(const Trajectory& ref) = 0;
    virtual ControlCommand computeCommand() = 0;
    virtual ~ControllerBase() = default;
};

这种设计的好处是:

  • PID、MPC、MPPI 可以继承同一个接口;
  • 上层模块不用关心具体控制器类型;
  • 后续新增控制算法时,只需要增加子类,不需要大幅修改主流程。

十三、面试常见手写代码题

类型 高频内容
手写单例模式 C++11 静态局部变量版本
手写智能指针 简化版 shared_ptr
手写线程池 任务队列 + mutex + condition_variable
手写生产者消费者 队列 + 锁 + 条件变量
手写 LRU 缓存 list + unordered_map
手写字符串函数 strcpystrlenmemcpy
手写排序算法 快排、归并、堆排
手写二叉树遍历 前序、中序、后序、层序
手写工厂模式 基类指针 + 子类对象创建
手写观察者模式 注册、注销、通知

十四、推荐复习顺序

建议按照下面顺序准备 C++ 面试:

text 复制代码
1. C++ 基础语法
2. 面向对象:继承、多态、虚函数
3. 内存管理:new/delete、RAII、智能指针
4. STL:vector、map、unordered_map、迭代器失效
5. C++11:右值引用、move、lambda、thread
6. 多线程:mutex、condition_variable、atomic
7. 设计模式:单例、工厂、策略、观察者、适配器
8. 项目八股:模块解耦、实时性、内存泄漏、调试方法

如果时间有限,建议优先掌握下面几块:

优先级 内容
第一优先级 面向对象、虚函数、多态、析构函数、智能指针
第二优先级 STL 容器底层、迭代器失效、C++11 特性
第三优先级 多线程、锁、条件变量、原子操作
第四优先级 设计模式和项目架构设计

十五、设计模式面试总结模板

如果面试官问"你项目中用过哪些设计模式",可以这样回答:

在项目中,我主要使用了工厂模式、策略模式、适配器模式和观察者模式。工厂模式用于根据配置动态创建不同的控制器或局部规划器,例如 PID、MPC、MPPI、DWA 等;策略模式用于封装不同算法,使多个控制策略可以在统一接口下切换;适配器模式用于统一仿真环境和实物底盘接口,例如将 Gazebo、MuJoCo 和真实机器人底盘封装成统一的 RobotInterface;观察者模式则常见于传感器数据回调、事件通知以及 ROS 发布订阅机制。通过这些设计方式,可以降低模块之间的耦合度,提高代码扩展性、可维护性和复用性。


总结

C++ 面试并不是只考语法细节,更重要的是考察对工程代码的理解能力。对于机器人、自动驾驶、控制算法、嵌入式等方向,除了掌握 C++ 基础,还需要重点理解以下内容:

  • 如何使用多态设计统一接口;
  • 如何用智能指针管理对象生命周期;
  • 如何用 STL 提高开发效率;
  • 如何避免内存泄漏和野指针;
  • 如何设计线程安全的数据传递方式;
  • 如何用工厂模式、策略模式、适配器模式降低模块耦合;
  • 如何结合项目经历讲清楚 C++ 在工程中的实际作用。

如果能够把"语法知识"和"项目架构"结合起来回答,面试效果会更好。

相关推荐
wear工程师1 小时前
腾讯天美一面:从 HashMap 到 Spring 事务,面试官这套连环问到底在考什么
java·面试
盐焗鹌鹑蛋1 小时前
【C++】二叉搜索树
c++
哎呦,帅小伙哦1 小时前
C++工程实战: 预处理宏、宏重定义避坑、LOG_TAG业务实践与__COUNTER__深度解析
c++
触底反弹2 小时前
面试被问 RAG 只能说出六个字?这篇用 100 行 Node.js 代码帮你彻底搞懂
javascript·人工智能·面试
IVVi0jToe2 小时前
高效C++线程池设计与实现
java·c++·安全
用户852495071842 小时前
Agent 智能体到底是什么:一文讲透 ReAct 循环
面试
^yi2 小时前
【C++】内存管理
开发语言·c++
云栖梦泽2 小时前
从 IMX415 摄像头驱动理解 Media Controller 框架
linux·开发语言·c++·嵌入式硬件
C++ 老炮儿的技术栈2 小时前
MFC 自定义纯色居中文字进度条控件
c语言·数据库·c++·windows·算法·c·visual studio