一、编译时检查vs运行时检查
| 特性 | 编译时检查 | 运行时检查 |
|---|---|---|
| 发生时间 | 程序运行之前(编译阶段) | 程序运行期间 |
| 检查内容 | 语法、类型、访问权限、函数签名等 | 数组越界、空指针、除零、文件不存在、网络中断等 |
| 执行者 | 编译器(如 GCC, Javac, TypeScript Compiler) | 程序本身或运行时环境(如 JVM, CLR) |
| 错误类型 | 编译错误(Compilation Error) | 运行时异常/错误(Runtime Exception/Error) |
| 对程序的影响 | 程序无法生成可执行文件,必须修复错误才能运行。 | 程序已经运行,可能导致崩溃、异常或未定义行为。 |
| 性能 | 增加编译时间,但不影响运行时性能。 | 增加运行时的开销,因为需要在代码执行时进行判断。 |
| 确定性 | 高。只要代码不变,检查结果就是确定的。 | 低。依赖于运行时的输入、环境状态等。 |
| 主要目标 | 保证代码的正确性和结构性,提前发现大量低级错误。 | 保证程序在不确定环境下的健壮性和容错能力。 |
| 典型语言 | Java, C#, C++, TypeScript, Go(静态类型语言) | JavaScript, Python, Ruby(动态类型语言),以及所有语言的运行时校验 |
二、举例说明:
2.1 编译时检查
2.1.1 语法错误
#include <iostream>
using namespace std;
int main() {
int x = 5
// 编译错误:缺少分号
// error: expected ';' after expression
return 0;
}
2.1.2 类型错误
#include <iostream>
#include <string>
using namespace std;
int main() {
string name = "Alice";
int length = name.length(); // 正确
// 编译错误:类型不匹配
string result = 42; // error: cannot convert 'int' to 'std::string'
return 0;
}
2.1.3 函数签名错误
#include <iostream>
using namespace std;
void printNumber(int x) {
cout << "Number: " << x << endl;
}
int main() {
printNumber(10); // 正确
printNumber("hello"); // 编译错误:参数类型不匹配
// error: could not convert '("hello")' from 'const char*' to 'int'
return 0;
}
2.1.4 模板实例化检查
#include <iostream>
using namespace std;
template<typename T>
T add(T a, T b) {
return a + b;
}
int main() {
cout << add(5, 3) << endl; // 正确:int + int
cout << add(2.5, 3.7) << endl; // 正确:double + double
// 编译错误:没有匹配的运算符
struct Point { int x, y; };
Point p1{1,2}, p2{3,4};
// auto result = add(p1, p2); // error: no match for 'operator+'
return 0;
}
2.1.5 constexptr检查
#include <iostream>
using namespace std;
constexpr int factorial(int n) {
return (n <= 1) ? 1 : n * factorial(n - 1);
}
int main() {
// 编译时计算
constexpr int result = factorial(5); // 在编译时计算 5!
cout << "5! = " << result << endl; // 输出 120
// 编译错误:constexpr 函数参数必须在编译时可知
int n = 5;
// constexpr int bad = factorial(n); // error: 'n' is not a constant expression
return 0;
}
2.2 运行时检查
2.2.1 数组越界
#include <iostream>
#include <vector>
using namespace std;
int main() {
vector<int> numbers = {1, 2, 3, 4, 5};
// 使用 at() 方法进行运行时边界检查
try {
cout << numbers.at(2) << endl; // 正确:输出 3
cout << numbers.at(10) << endl; // 运行时异常:越界访问
}
catch (const out_of_range& e) {
cout << "捕获到异常: " << e.what() << endl;
}
// 使用 [] 运算符(通常无边界检查)
cout << numbers[2] << endl; // 正确:输出 3
cout << numbers[10] << endl; // 未定义行为!可能崩溃或输出垃圾值
return 0;
}
2.2.2 动态类型转换
#include <iostream>
using namespace std;
class Animal {
public:
virtual ~Animal() = default;
};
class Dog : public Animal {
public:
void bark() { cout << "Woof!" << endl; }
};
class Cat : public Animal {
public:
void meow() { cout << "Meow!" << endl; }
};
int main() {
Animal* animal1 = new Dog();
Animal* animal2 = new Cat();
// 运行时类型检查:dynamic_cast
if (Dog* dog = dynamic_cast<Dog*>(animal1)) {
dog->bark(); // 会执行:animal1 确实是 Dog
}
if (Dog* dog = dynamic_cast<Dog*>(animal2)) {
dog->bark(); // 不会执行:animal2 是 Cat,不是 Dog
} else {
cout << "animal2 不是 Dog 类型" << endl;
}
delete animal1;
delete animal2;
return 0;
}
2.2.3 除零检查
#include <iostream>
#include <stdexcept>
using namespace std;
double divide(int a, int b) {
if (b == 0) {
throw runtime_error("除零错误");
}
return static_cast<double>(a) / b;
}
int main() {
try {
cout << divide(10, 2) << endl; // 正确:输出 5
cout << divide(10, 0) << endl; // 运行时异常
}
catch (const runtime_error& e) {
cout << "捕获到异常: " << e.what() << endl;
}
return 0;
}
2.2.4 文件操作检查
#include <iostream>
#include <fstream>
using namespace std;
int main() {
ifstream file("不存在的文件.txt");
// 运行时检查文件是否成功打开
if (!file.is_open()) {
cout << "无法打开文件!" << endl;
return 1;
}
string content;
while (getline(file, content)) {
cout << content << endl;
}
// 检查文件读取状态
if (file.bad()) {
cout << "文件读取过程中发生错误" << endl;
}
file.close();
return 0;
}
2.2.5 智能指针为空检查
#include <iostream>
#include <memory>
using namespace std;
class Resource {
public:
void use() { cout << "使用资源" << endl; }
};
int main() {
unique_ptr<Resource> ptr1 = make_unique<Resource>();
unique_ptr<Resource> ptr2 = nullptr;
// 运行时检查指针是否为空
if (ptr1) {
ptr1->use(); // 会执行
}
if (ptr2) {
ptr2->use(); // 不会执行
} else {
cout << "ptr2 是空指针" << endl;
}
return 0;
}
三、总结
在 C++ 中,良好的编程实践是:
-
尽可能在编译时发现问题(使用强类型、static_assert 等)
-
对不可避免的运行时错误进行适当处理(异常处理、错误码等)
-
使用工具如 ASan、UBSan 等来检测未定义行为

被折叠的 条评论
为什么被折叠?



