-- 整体流程
C++ 源文件 (.cpp)
↓ 预处理(展开头文件、宏替换等)
预处理后的代码 (.i)
↓ 编译(编译器)
汇编代码 (.s)
↓ 汇编(汇编器)
目标文件 (.o / .obj)
↓ 链接(连接器)
最终可执行文件(如 a.out / exe)

(一)预处理

C++ 的 预处理阶段(Preprocessing) 是整个编译过程的第一步,它在真正编译代码前处理以 # 开头的指令,生成一个中间文件(通常扩展名为 .i),供后续编译器编译。简单理解:预处理阶段就像在编译之前对源码进行“文本替换和展开”的处理器。


预处理器主要做了什么?

1. 头文件展开:#include

#include <iostream>
#include "myutils.h"

被替换为头文件的全部内容(递归展开)。


2. 宏替换:#define

#define PI 3.14
std::cout << PI; // → std::cout << 3.14;

所有出现 PI 的地方都被替换为 3.14


3. 条件编译:

#ifdef / #ifndef / #if / #else / #endif

#ifdef DEBUG
std::cout << "Debugging" << std::endl;
#endif

如果定义了 DEBUG,这段代码会被保留;否则会被忽略。


4. 删除注释

///\* \*/

预处理阶段会移除所有注释,不再传给编译器。


5. 宏函数展开:

#define SQUARE(x) ((x)*(x))
SQUARE(3 + 1) // → ((3 + 1)*(3 + 1)) → 16

注意宏替换是纯文本替换,没有类型检查。


6. 错误指令处理:#error

#ifndef PLATFORM
#error "PLATFORM not defined!"
#endif

如果没有定义 PLATFORM,预处理器报错并停止编译。


如何查看预处理结果?

使用 g++ 命令:

g++ -E main.cpp -o main.i
  • main.i 文件就是预处理之后的纯文本 C++ 代码;
  • 可用于查看头文件展开、宏替换等效果。

例子:

源码:

// main.cpp
#include <iostream>
#define PI 3.14 int main() {
std::cout << PI << std::endl;
return 0;
}

预处理后:

// 展开为 iostream 的实际内容...
int main() {
std::cout << 3.14 << std::endl;
return 0;
}

(二)编译-汇编


1. 编译(Compile):C++ → 汇编代码(.s

编译器的任务:

  • 分析源代码:词法分析、语法分析、语义分析;
  • 中间表示(IR)生成:构建抽象语法树(AST)和 LLVM IR 等中间代码;
  • 优化:常量折叠、循环展开、函数内联、死代码消除等;
  • 生成汇编代码:将优化后的中间代码生成目标 CPU 的汇编语言。

示例命令:

g++ -S main.cpp -o main.s

输出示例(x86 汇编):

main:
push rbp
mov edi, OFFSET FLAT:.LC0
call puts
pop rbp
ret

2. 汇编(Assemble):汇编代码 → 目标文件(.o

汇编器的任务:

  • 将汇编语言转成二进制机器码(目标代码);
  • 构建符号表、指令地址映射等;
  • 输出 .o 文件(或 .obj)。
as main.s -o main.o   # 或由 g++ 自动完成

这个 .o 文件:

  • 是一段不能单独运行的机器代码
  • 包含未解析的符号(如对 printf 的引用);
  • 需要链接阶段才能成为可执行程序。

3. 编译器 vs 汇编器对比

步骤 输入 输出 工具 作用
编译 .cpp / .i .s(汇编) 编译器(如 g++ -S 将 C++ 源码转换为汇编语言
汇编 .s .o(目标文件) 汇编器(如 as 将汇编语言转换为机器码

4. 例子:代码到机器

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

经过编译 → 生成汇编:

add:
mov eax, edi
add eax, esi
ret

再经过汇编器 → 生成 .o 文件(二进制形式):

b8 01 00 00 00    ; mov eax, 1
01 f0 ; add eax, esi
c3 ; ret
  • .s 是汇编语言(人类可读)
  • .o 是机器语言(二进制,CPU 可执行,但不能独立运行)
  • 最后再由链接器 ld 把多个 .o 文件合成完整程序

5. 编译单元

在 C++ 中,编译单元(Translation Unit) 是编译器处理的最小单位,理解它对于掌握 C++ 的编译过程、头文件组织、链接等都非常重要。


编译单元是什么?

一个编译单元就是一个源文件(.cpp)加上它所包含的所有头文件,经过预处理后的完整代码集合。

也就是说:

编译单元 = 源文件 + 源文件 `#include` 的头文件(递归展开后)

然后,编译器会单独对每个编译单元生成一个 .o.obj 目标文件。


例子:

假设我们有以下文件:

// math_utils.h
#pragma once
int add(int a, int b); // math_utils.cpp
#include "math_utils.h"
int add(int a, int b) {
return a + b;
} // main.cpp
#include "math_utils.h"
#include <iostream>
int main() {
std::cout << add(3, 4) << std::endl;
return 0;
}

这个程序两个编译单元:

  1. math_utils.cpp(+ 它包含的 math_utils.h) → 编译单元 A
  2. main.cpp(+ 它包含的 math_utils.h<iostream>) → 编译单元 B

每个编译单元独立编译生成 .o 文件:

g++ -c math_utils.cpp -o math_utils.o   # 编译单元 A
g++ -c main.cpp -o main.o # 编译单元 B

最后再链接:

g++ main.o math_utils.o -o program
问题 关系到编译单元的理解
为什么函数定义不能写在头文件中? 因为头文件会被多个 .cpp 包含,会重复定义,导致链接错误
为什么加 inline 可以解决重复定义? 编译器会允许多份相同定义,只要完全一致
静态变量/函数的作用域? static 限定在当前编译单元可见
多文件项目如何组织? 每个 .cpp 独立编译,头文件共享声明

6. 防止头文件被重复包含

如果一个头文件在同一个编译单元中被重复包含(即被多个地方 #include,或者被间接多次 #include),但没有使用头文件保护机制(如 #pragma once#ifndef/#define),将会导致 编译错误或潜在的奇怪行为


重复包含会发生什么后果?

头文件中包含了函数定义变量定义结构体定义等。

1. 函数重复定义

// mymath.h
int add(int a, int b) { return a + b; } // 这是一个定义,不只是声明
// main.cpp
#include "mymath.h"
#include "mymath.h" // 重复包含 int main() {
return add(1, 2);
}

编译错误:

error: redefinition of 'int add(int, int)'

因为预处理后,add 函数体出现了两次。


2. 结构体/类重复定义

// point.h
struct Point {
int x, y;
};
#include "point.h"
#include "point.h"

会导致:

error: redefinition of 'struct Point'

3. 全局变量重复定义

// globals.h
int global_value = 42;

多个 .cpp 文件都 #include "globals.h",就会有多个 global_value,导致链接错误:

multiple definition of `global_value`

注意:声明不会导致重复定义!

头文件中如果只写函数声明或类前向声明,不会出错:

// safe.h
int add(int a, int b); // 只是声明,不是定义

7. 如何避免重复包含?

在 C++ 中,为了防止 头文件被重复包含(导致编译错误或冗余),我们通常使用以下两种方式实现“头文件保护”


方法一:#pragma once(推荐)

// math_utils.h
#pragma once int add(int a, int b);

原理:

  • #pragma once 是一种编译器指令,告诉编译器:这个文件只编译一次。
  • 多数现代编译器(如 GCC、Clang、MSVC)都支持它。

优点:

  • 简洁易读
  • 不易出错(不用手动写宏名);
  • 编译器处理更高效(文件路径作为 key,不需字符串比较)。

方法二:传统的 include guard(兼容性最强)

// math_utils.h
#ifndef MATH_UTILS_H
#define MATH_UTILS_H int add(int a, int b); #endif // MATH_UTILS_H

原理:

  • 利用宏定义,如果 MATH_UTILS_H 没被定义,就定义它并包含内容。
  • 如果文件被再次包含,由于宏已定义,内容就不会重复编译。

优点:

  • 100% 兼容所有 C/C++ 编译器(包括老旧的或不支持 #pragma once 的编译器)。

选择建议:

条件 推荐使用
使用现代编译器(如 GCC/Clang/MSVC) #pragma once
追求最大兼容性(跨平台旧编译器) #ifndef 宏守卫

(三)链接

链接(Linking)是 C++ 编译流程的最后一个阶段,其作用是将多个目标文件(.o)和库文件合并成一个可执行文件,并解决它们之间的符号引用(比如函数、变量的调用与定义)。


1、链接的作用

总结一句话:

链接的作用是把多个“碎片化的目标文件”拼接成一个完整可执行程序,并解决符号引用问题。


具体功能包括:

功能 举例说明
符号解析(Symbol Resolution) main.cpp 中调用的 add() 对应到 math_utils.cpp 里的实现
地址重定位(Relocation) 确定每个函数/变量在内存中的最终位置
合并多个目标文件/库文件 多个 .o 文件和 .a/.so 库合并为可执行文件
处理静态库和动态库的引用 如链接 libm.alibm.so(数学库)

2、常见链接问题(非常重要)

undefined reference(最常见错误

原因:声明了某个函数/变量,但没有定义(或链接不到定义)。

// main.cpp
void foo(); // 声明
int main() {
foo(); // 链接时找不到定义就报错
}

报错:

undefined reference to `foo()`

原因:

  • 忘记实现
  • 实现在另一个 .cpp 但没参与链接
  • 静态库/动态库没链接进来(如 -lm-lpthread

multiple definition(重复定义)

原因:某函数或变量在多个 .o 文件中都定义了一遍。

比如:

// a.h
int x = 5; // 这是定义,不是声明!
// a.cpp 和 b.cpp 都包含了 a.h → 链接时重复定义 x

解决办法

  • extern int x; 声明
  • 真正的 int x = 5; 放在 .cpp 文件中
  • 或用 inline 修饰函数定义、或使用 static 局部化作用域

重复符号但未链接失败(静态变量或静态函数)

如果你写了 static void helper(),哪怕在多个文件中重复,也不会冲突。

原因:static 修饰的函数/变量只在当前编译单元可见,不参与全局链接。


链接顺序错误(特别是在 Linux 下)

g++ main.o -lmylib

g++ -lmylib main.o

GNU LD 是从左往右解析依赖的,如果你的库在左边但 main.o 中引用的符号在右边,它会找不到。


3、静态链接 vs 动态链接

类型 描述 优点 缺点
静态链接 .a 编译时复制代码进可执行文件 运行时独立,不需外部依赖 程序体积大
动态链接 .so 编译时只记录库位置,运行时加载 程序小,可更新库 运行依赖外部 .so

4、常用链接参数(GCC)

参数 含义
-c 只编译,不链接(生成 .o
-o output 指定输出文件名
-l<name> 链接库(如 -lm 表示链接 libm.so 或 libm.a)
-L<path> 指定库搜索路径
-static 强制静态链接
-shared 生成动态库 .so
-Wl,-rpath=... 设置动态库运行时路径

(四)ODR原则

ODR(One Definition Rule,唯一定义规则) 是 C++ 的一个核心规则,确保程序的链接阶段行为一致、确定。它规定了变量、函数、类等在整个程序中只能有一个定义,否则会导致链接错误或未定义行为。


1、ODR 是什么?

One Definition Rule(唯一定义规则)

在一个程序中,每个变量、函数、类、模板、枚举等都必须最多只有一个定义,而可以有多个声明。这个“唯一定义”必须在所有使用它的翻译单元中一致


2、声明 vs 定义

  • 声明(declaration):告诉编译器“有这个东西”,但不提供实现。
  • 定义(definition):提供了完整内容或内存分配。
extern int x;      // 声明
int x = 42; // 定义

3、ODR 的几种典型应用

1. 普通变量

// config.hpp
const int SIZE = 100;

如果这个头文件被多个 .cpp 文件包含,会违反 ODR!

修复方法:

  • 使用 inline const(C++17):

    inline const int SIZE = 100;
  • 或使用 extern + .cpp 定义:

    // config.hpp
    extern const int SIZE; // config.cpp
    const int SIZE = 100;

2. 函数定义

// utils.hpp
int add(int a, int b) {
return a + b;
}

多个 .cpp 包含此头文件,会导致链接器错误:multiple definition of add

️ 正确做法:加 inline 或将定义放在 .cpp 中。


3. 类成员函数

class A {
public:
void sayHi() {
std::cout << "Hi" << std::endl;
}
};

类内定义的成员函数是 自动 inline 的,所以不违反 ODR,可以放头文件中。


4. 模板

模板必须放在头文件中,因为它在实例化时才生成代码,必须可见定义

template<typename T>
T square(T x) {
return x * x;
}

️ 合法:定义写在 .hpp

不合法:只写声明在 .hpp,把定义放在 .cpp


5. 同名函数/类在不同文件中重复定义

// file1.cpp
int foo() { return 1; } // file2.cpp
int foo() { return 2; }

链接时报错:multiple definition of foo


4、什么时候 ODR 不适用?

  • 在函数体内部的局部变量,不参与 ODR 检查。
  • 内联函数、模板实例、类内函数默认支持多份定义,只要内容一致即可。

5、如何避免 ODR 问题

情况 正确做法
多文件共享变量 使用 extern + 单一 .cpp 定义
头文件中定义变量 使用 inline(C++17)
头文件中定义函数 使用 inline 或函数模板
模板定义 保持全部写在头文件
类成员函数类内定义 默认 inline,合法
非模板函数定义 建议写在 .cpp

ODR 确保了整个程序中对每个实体的实现只有一个真实定义,防止了链接冲突和运行期不一致的问题,是编译器链接阶段的一道安全网。

(五)inline的作用

inline 是 C++ 中一个重要的关键字,最初用于建议编译器将函数的调用“内联展开”(即把函数体直接替换到调用处),以减少函数调用的开销。

但随着 C++ 的发展,inline 的用途逐渐扩展,尤其在头文件中定义函数和变量时变得非常重要。


1、inline 的主要作用

建议编译器内联展开函数(性能优化)

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

编译器可能会add(2, 3) 替换为 2 + 3,省掉函数调用开销(尤其是小函数)。

️ 注意:是否真正内联是编译器的决定,inline 只是“建议”。


允许函数或变量定义出现在多个翻译单元中(核心用途)

这是现代 C++ 中更重要的用途!

举例:头文件中定义函数或变量

// math.hpp
inline int square(int x) {
return x * x;
}
  • 如果没有 inline,多个 .cpp 文件包含 math.hpp,会导致 ODR(One Definition Rule)冲突
  • 加上 inline,编译器允许多个定义存在,只要内容一致

从 C++17 开始,inline 还可用于变量

允许变量的定义出现在多个翻译单元中而不违反 One Definition Rule(ODR)

// config.hpp
inline const int BUFFER_SIZE = 1024;

这样你就可以在多个 .cpp#include "config.hpp" 而不会重复定义冲突。


2、使用场景总结

场景 说明
小函数性能优化 inline 建议内联展开,避免调用开销(但现代编译器可自动优化)
头文件中定义函数 必须加 inline,否则多文件包含会重复定义,链接错误
头文件中定义 const 变量 必须加 inline(C++17 之后),或使用 extern 声明
模板函数/类 默认就是 inline,不需要显式写

3、和 static 的区别(重要!)

  • inline多个翻译单元共享一个定义
  • static每个翻译单元都有自己的副本(内部链接)
// inline 版本:多个 .cpp 文件共享
inline int globalFunc() { return 1; } // static 版本:每个 .cpp 文件都有一份
static int globalFunc() { return 1; }

4、ODR(One Definition Rule)相关说明

在 C++ 中,如果一个函数或变量在多个 .cpp 文件中定义且没有 inlinestatic 修饰,就会违反 ODR,导致链接错误。

使用 inline 是合法解决方案,允许在多个编译单元中拥有同一实体的定义


5、什么时候不需要 inline

自动隐式 inline,这意味着它们可以也应该直接定义在头文件中**,不会违反 One Definition Rule(ODR),也不会导致链接错误。

  • 函数模板:自动隐式 inline
  • 类内定义的成员函数:自动隐式 inline
class A {
public:
int getX() { return x; } // 自动是 inline
};

(六)多文件项目的基本结构

在 C++ 中,多文件项目的组织方式直接关系到模块化、可维护性、可复用性,同时影响编译速度和链接行为。下面从项目结构、文件职责、如何编译链接、以及实用建议四个方面详细说明:

假设我们写一个简单的数学库项目:

MyProject/
├── main.cpp // 主程序入口
├── math/
│ ├── math_utils.h // 函数声明(头文件)
│ └── math_utils.cpp // 函数定义(实现文件)
├── string/
│ ├── string_utils.h
│ └── string_utils.cpp
└── Makefile // 或 CMakeLists.txt

1、每类文件的职责

文件类型 后缀 作用
源文件 .cpp 写具体的实现(函数体、类定义等)
头文件 .h / .hpp 放函数声明、类定义、宏、模板等,不写函数体(除非是 inline 或模板)
实现文件 .cpp 通常和同名 .h 配对
主程序入口 main.cpp int main() 所在文件
构建脚本 Makefile / CMakeLists.txt 编译自动化

2、函数/类的声明与定义分离

math_utils.h(声明)

#ifndef MATH_UTILS_H
#define MATH_UTILS_H int add(int a, int b);
int subtract(int a, int b); #endif

math_utils.cpp(定义)

#include "math_utils.h"

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

main.cpp

#include <iostream>
#include "math/math_utils.h" int main() {
std::cout << add(5, 3) << std::endl;
return 0;
}

3、如何编译和链接

手动编译方式(GCC/Clang):

g++ -c math/math_utils.cpp -o math_utils.o
g++ -c main.cpp -o main.o
g++ math_utils.o main.o -o myprogram

一步完成:

g++ main.cpp math/math_utils.cpp -o myprogram

4、使用 Makefile(推荐)

# Makefile
CXX = g++
CXXFLAGS = -std=c++11 -Wall
OBJECTS = main.o math/math_utils.o string/string_utils.o myprogram: $(OBJECTS)
$(CXX) $(OBJECTS) -o myprogram %.o: %.cpp
$(CXX) $(CXXFLAGS) -c $< -o $@ clean:
rm -f *.o */*.o myprogram

使用:

make
make clean

5、项目组织建议

建议 说明
头文件保护 每个 .h 文件用 #pragma once#ifndef
命名空间 避免函数/类名冲突
按模块分目录 math/io/network/
源文件不互相 include .cpp 只包含 .h,不要包含别的 .cpp
头文件只写声明,不写定义 除非是模板或 inline 函数
类定义放头文件,类成员函数实现放 .cpp 文件 分离职责
用构建工具 makecmake 简化构建过程
避免全局变量 用类封装或传参

(七)头文件和源文件

(一)所有函数和变量都写在头文件中

所有函数和变量都写在头文件中(即:函数和变量的定义都在头文件中)就是:不使用 .cpp 文件,所有的函数实现、全局变量定义、类定义等都直接写在 .h.hpp 文件中。这是一种不推荐的做法,但在某些特殊场景下会被使用。


示例:

// myheader.h

int globalVar = 0;  // 全局变量定义

void foo() {
// 函数定义
} class MyClass {
public:
void bar() {
// 类成员函数内联定义
}
};

存在的问题(主要缺点)

1. 违反 One Definition Rule (ODR)

  • C++ 要求每个非 inline 的函数或变量在整个程序中只能有一个定义。
  • 如果你在多个 .cpp 文件中 #include "myheader.h",那么 globalVarfoo() 都会被重复定义。
  • 这会导致链接错误(multiple definition error)。

示例报错:

duplicate symbol _globalVar in:
main.o
other.o
ld: 1 duplicate symbol for architecture x86_64

2. 全局变量重复定义

  • 普通全局变量不能在头文件中定义多次。
  • 必须使用 extern 声明 + .cpp 中定义的方式。

3. 编译速度变慢

  • 所有包含这个头文件的 .cpp 文件都会包含完整的实现代码。
  • 修改一次头文件,所有依赖它的 .cpp 文件都要重新编译。

4. 难以维护与协作

  • 头文件应该只暴露接口,而不是实现。
  • 把实现也放在头文件中,破坏了模块化设计原则,不利于团队协作。

可以接受的情况(特殊情况)

虽然一般不推荐,但以下几种情况是可以在头文件中写定义的:

1. 模板函数/类

template<typename T>
T add(T a, T b) {
return a + b;
}
  • 模板必须在头文件中定义,因为编译器需要在使用时看到完整定义。

2. inline 函数

inline void bar() {
// ...
}
  • inline 关键字允许函数在多个翻译单元中出现。

3. constexpr 变量

constexpr int MaxValue = 100;
  • constexpr 是隐式 inline 的。

4. static const 整型常量

class MyClass {
public:
static const int Value = 42;
};

(二)更规范的写法

如果你希望让函数和变量都在一个文件中管理,可以考虑以下做法:

1.使用单个 .cpp 文件 + 对应头文件

// mylib.h
#ifndef MYLIB_H
#define MYLIB_H extern int globalVar;
void foo(); #endif
// mylib.cpp
#include "mylib.h" int globalVar = 0; void foo() {
// 实现
}

2.使用静态库或动态库

  • 将多个 .cpp 编译为 .a.dll,然后通过头文件调用。

总结

写法 是否推荐 说明
所有函数和变量都写在头文件中 不推荐 容易导致链接错误、结构混乱
模板、inline 函数、constexpr 等写在头文件中 推荐 合理合法,符合标准
函数声明在头文件,定义在 .cpp 文件 强烈推荐 最佳实践,适合项目开发

头文件只放:

  • 类定义
  • 函数声明
  • extern 全局变量声明
  • inline / constexpr / template 函数定义

源文件放:

  • 函数实现
  • 全局变量定义
  • 静态变量定义

这样可以保证代码清晰、可维护、可扩展,适用于各种规模的项目。

c++ 预处理 编译 链接 文件组织形式的更多相关文章

  1. C语言预处理编译链接各个阶段错误,分阶段的说一下

    C语言预处理编译链接各个阶段错误,分阶段的说一下 C语言预处理编译链接各个阶段错误,分阶段的说一下比如指针异常,数组下标越界什么的    我来答 1个回答 #热议# 你觉得这辈子有希望看到996消失 ...

  2. C++生成二级制文件过程(预处理->编译->链接 )

    转载请注明出处 Windows下C++编程,通过VC生成工程,编写C++源文件,点运行,代码没问题直接出结果.VC什么都帮我们搞了,不了解其中过程也完全没问题. 转到linux下写c++,总觉得有点虚 ...

  3. 实现一个基于tcc/tlink的简单的编译链接工具

    一.基础研究 在这里我们需要提供一套新的c语言开发工具cc,它支持的c程序不是从main开始运行而是从CMain开始运行. 书上已经对该工具程序进行了需求分析:(1)要在屏幕中间显示彩色的字符串:(2 ...

  4. C/C++程序编译流程(预处理->编译->汇编->链接)

    程序的基本流程如图: 1. 预处理 预处理相当于根据预处理指令组装新的C/C++程序.经过预处理,会产生一个没有宏定义,没有条件编译指令,没有特殊符号的输出文件,这个文件的含义同原本的文件无异,只是内 ...

  5. C语言预处理 编译 汇编 链接四个阶段

    c程序(源代码)转换成可以在硬件上运行的程序(可执行代码),需要进行编译和链接. 编译过程 编译过程又可以分成两个阶段:编译和会汇编. 编译 编译是读取源程序(字符流),对之进行词法和语法的分析,将高 ...

  6. C语言程序经过编译链接后形成二进制映像文件的组成

    C语言程序经过编译链接后形成二进制映像文件由栈,堆,数据段,代码段组成,其中数据段又分为:只读数据段,已经初始化读写数据段,未初始化数据段(BSS段).如下图所示: 1.栈区(stack):由编译器自 ...

  7. VS Code通过code runner插件编译运行多个cpp文件 | 链接编译.h文件

    1.多个cpp文件在同一级目录 参考:https://jingyan.baidu.com/article/2f9b480d7ceb3d01ca6cc224.html 此时可通过修改Code Runne ...

  8. C++开始前篇,深入编译链接

    C++开始,为什么要写这个东西,因为按照课堂进度的话,现在的C++已经学到模板以及重载了,有时却仍然因为一些小问题无法解答,原因是忘记了开始时学到的知识,深知不能像猴子掰棒子一样,掰一个扔一个,因此, ...

  9. linux 编译,链接和加载

    1.   序 最近在折腾各种.so,碰到了一些问题,一开始对于很多错误也没有头绪,茫然不知所措.索性化了一天多时间将<<程序员的自我修养—链接.装载与库>>中部分内容略读了一遍 ...

  10. 关于C++编译链接和模板函数

    一,关于编译链接编译指的的把编译单元生成目标文件的过程链接是把目标文件链接到一起的过程编译单元:可以认为是一个.c或者.cpp文件.每个编译单元经过预处理会得到一个临时的编译单元.预处理会间接包含其他 ...

随机推荐

  1. 【虚拟机】Windows(x86)上部署ARM虚拟机(Ubuntu)

    [虚拟机]Windows(x86)上部署ARM虚拟机(Ubuntu) 零.起因 最近在学嵌入式,这就不得不涉及ARM指令集,但是电脑是x86指令集的,用手机不太方便,买开发板又要等几天--,总之就是要 ...

  2. 一次windows下使用cmake遇到的问题

    背景 在windows下的cmake和mingw提供的make,在windows环境下进行了简单尝试,结果发现make的时候失败: #include <iostream> int main ...

  3. CH9121default与classical设置方法

    SYN发送间隔调整方法: 网口连接设备后双击设备列表中要配置的设备在扩展参数中单击获取扩展参数,在超时处理模式选项选择Classical然后执行设置扩展参数,最后点击复位模块后生效(仅TCP CLIE ...

  4. Sentinel源码—1.使用演示和简介

    大纲 1.Sentinel流量治理框架简介 2.Sentinel源码编译及Demo演示 3.Dashboard功能介绍 4.流控规则使用演示 5.熔断规则使用演示 6.热点规则使用演示 7.授权规则使 ...

  5. 基于OpenCV与Tesseract的文档扫描增强器实战教程(附完整代码)

    引言:文档数字化的智能解决方案 在移动办公时代,手机拍摄文档已成为常态,但随之带来的图像畸变.光照不均.文字倾斜等问题严重影响OCR识别效果.本文将通过OpenCV和Tesseract构建一款具备实时 ...

  6. 异步编程与Tortoise-ORM框架

    title: 异步编程与Tortoise-ORM框架 date: 2025/04/19 00:13:05 updated: 2025/04/19 00:13:05 author: cmdragon e ...

  7. Java 单元测试简单扫盲

    前言 仔细回想起来,上次认真编写单元测试已经是两年前的事了.那时候觉得写单元测试是种负担. 为了应付代码覆盖率要求,常常依赖工具自动生成测试用例,有时需要启动Spring容器,有时又不需要(当时还分不 ...

  8. 使用IDEA管理服务器Docker及远程仓库

    目录 配置连接Docker服务器及远程仓库 连接服务器Docker 远程仓库(可选) IDEA管理 确保docker服务器已经开启了远程守护进程访问.[1] 配置连接Docker服务器及远程仓库 连接 ...

  9. 个人对Debian桌面系统的简单使用分享

    前言 自从安装Debian12作为双系统已经过了大半个月,平常主用Debian,偶尔切回Windows找找文档,总体来说体验还是很不错的.先贴个桌面照 为什么要使用Linux作为个人桌面 当初刚从Wi ...

  10. 【笔记】Python3|(一)Python 申请并调用国内百度、阿里、腾讯、有道的翻译 API 的教程和测试情况(第三方库 translate 和 腾讯 API 篇)

    var code = "dccf4c95-7458-4b38-b8ae-d45b3e59c218" 价格和 API 申请参考: 免费翻译接口最新最全汇总(百度翻译,腾讯翻译,谷歌翻 ...