计算机基础理论|——数据结构与算法链表、栈、队列、树与图、查找与排序等。|——程序设计基础结构化、面向对象方法。|——软件工程基础软件生命周期、测试与调试。|——数据库设计基础数据模型、关系代数、SQL。|——C语言重点考察结构化编程能力核心是指针、函数、数组和结构体。|——C重点是掌握面向对象编程OOP思想核心是类与对象、继承与派生。目录计算机基础理论引言一、C 语言中如何定义和使用 bool 类型1.1 传统 C 语言模拟方式1.2 现代 C 语言推荐方式C99 及以上二、C 中创建静态数组与对象数组2.1 基本类型静态数组2.2 类对象数组三、C 语言 UTF-8 转 GBK 编码Windows四、深拷贝 vs 浅拷贝何时需要深拷贝4.1 概念对比4.2 示例代码4.3 最佳实践五、委托构造函数消除重复初始化代码5.1 问题场景5.2 解决方案委托构造函数C115.3 注意事项六、角度值归一化将角度限制在 [0, 360) 范围内七、类型判断使用 typeid 与更好的替代方案7.1 使用 typeid 运算符7.2 局限性7.3 更好的替代方案八、构造函数 ≠ 成员函数正确初始化方式8.1 错误做法8.2 正确做法8.3 正确的构造函数定义与使用九、总结与建议引言 无论是初学者还是有一定经验的 C 开发者总会遇到一些容易混淆或遗忘的细节如何在 C 语言中定义 bool深拷贝和浅拷贝有什么区别委托构造函数如何避免代码重复角度值怎样归一化到 [0, 360)本文整理了 C/C 开发中多个高频“坑点”给出清晰的解释、代码示例和最佳实践帮助你写出更健壮、更易维护的代码。适用于 C11 及以上标准部分内容兼容 C99。一、C 语言中如何定义和使用 bool 类型C89/C90 标准中没有内置的 bool 类型通常使用 int 配合宏定义来模拟。C99 标准引入了 stdbool.h提供了 bool、true、false。1.1 传统 C 语言模拟方式ctypedef int bool;#define true 1#define false 0bool flag true;if (flag false) {// ...}1.2 现代 C 语言推荐方式C99 及以上c#include stdbool.hbool flag true;if (!flag) {// ...} **注意**stdbool.h 中的 bool 实际是 _Bool 的宏true/false 分别展开为 1/0。建议在支持 C99 的环境中使用标准头文件提高可移植性。二、C 中创建静态数组与对象数组2.1 基本类型静态数组cpp#define MAXSIZE_STATICARRAY 100int arr[MAXSIZE_STATICARRAY]; // 栈上分配大小必须为编译期常量2.2 类对象数组cpp#include stringusing namespace std;class Student {public:string name;int age;Student() default; // 默认构造函数Student(const string n, int a) : name(n), age(a) {}};// 创建对象数组的几种方式Student students[3]; // 调用默认构造函数 3 次Student classArr[2] {Student(A, 20), Student(B, 21)}; // 初始化列表**注意事项**- 如果类没有默认构造函数则必须为每个元素提供初始化器。- 静态数组大小在编译时确定不可变。如果需要动态大小使用 std::vectorStudent。三、C 语言 UTF-8 转 GBK 编码Windows在 Windows 环境下控制台默认使用 GBK 编码而源代码文件通常是 UTF-8。直接 printf 中文会乱码。下面提供一个通用的转换函数。c#include stdio.h#include stdlib.h#include windows.h// 将 UTF-8 字符串转换为 GBK 编码返回的指针需要 free()char* utf8_to_gbk(const char* utf8) {// 1. UTF-8 → 宽字符 Unicodeint wlen MultiByteToWideChar(CP_UTF8, 0, utf8, -1, NULL, 0);wchar_t* wstr (wchar_t*)malloc(wlen * sizeof(wchar_t));if (!wstr) return NULL;MultiByteToWideChar(CP_UTF8, 0, utf8, -1, wstr, wlen);// 2. Unicode → GBK (代码页 936)int len WideCharToMultiByte(936, 0, wstr, -1, NULL, 0, NULL, NULL);char* gbk (char*)malloc(len);if (gbk) {WideCharToMultiByte(936, 0, wstr, -1, gbk, len, NULL, NULL);}free(wstr);return gbk;}int main() {const char* utf8_str 正常退出循环逻辑返回0退出程序;char* gbk_str utf8_to_gbk(utf8_str);if (gbk_str) {printf(%s\n, gbk_str);free(gbk_str);}return 0;} **注意**此代码仅适用于 Windows 平台。Linux 下通常使用 iconv 库进行编码转换。四、深拷贝 vs 浅拷贝何时需要深拷贝4.1 概念对比# 深拷贝 vs 浅拷贝 对比表[[shallow_vs_deep]]characteristic 资源处理shallow 仅复制指针多个对象指向同一块堆内存deep 复制指针指向的内容分配独立内存[[shallow_vs_deep]]characteristic 修改影响shallow 修改一个对象会影响另一个deep 完全独立互不影响[[shallow_vs_deep]]characteristic 资源释放shallow 可能重复释放double free导致崩溃deep 各自释放自己的资源安全[[shallow_vs_deep]]characteristic 性能shallow 快只复制指针deep 慢需要分配内存和复制数据[[shallow_vs_deep]]characteristic 内存占用shallow 小多对象共享同一资源deep 大每个对象有独立副本4.2 示例代码cppclass Shallow {public:int* data;Shallow(int val) { data new int(val); }// 默认拷贝构造是浅拷贝};class Deep {public:int* data;Deep(int val) { data new int(val); }Deep(const Deep other) { // 深拷贝构造函数data new int(*other.data);}~Deep() { delete data; }// 赋值运算符也需要深拷贝此处省略};4.3 最佳实践- **遵循“三/五法则”**如果类需要自定义析构函数、拷贝构造函数或拷贝赋值运算符通常三者都需要定义或全部禁用。- **优先使用 RAII 容器**如 std::vector、std::string 等它们已经实现深拷贝语义避免手动管理内存。- **使用移动语义**C11 起可减少不必要的深拷贝开销。五、委托构造函数消除重复初始化代码5.1 问题场景当类有多个构造函数时经常出现重复的初始化逻辑。例如cppclass MyClass {public:MyClass(int a) { /* 公共初始化 A */; /* 特定逻辑 */ }MyClass(int a, int b) { /* 公共初始化 A */; /* 另一特定逻辑 */ }};5.2 解决方案委托构造函数C11一个构造函数可以调用同一个类的另一个构造函数避免代码重复。cpp#include QDebug // Qt 示例也可替换为 iostreamclass MyClass {public:// 主构造函数包含公共初始化explicit MyClass(QObject* parent nullptr) : QObject(parent) {qDebug() 公共初始化代码;}// 委托构造函数MyClass(const std::string str) : MyClass(nullptr) {qDebug() 字符串参数特有逻辑;m_name QString::fromStdString(str);}private:QString m_name;};5.3 注意事项- 委托构造函数不能同时使用成员初始化列表除目标构造函数外。- 避免循环委托A 委托 BB 又委托 A会导致未定义行为。- 委托链最终必须落在一个非委托构造函数上。六、角度值归一化将角度限制在 [0, 360) 范围内在图形、机器人或物理引擎中经常需要将角度规范化到标准区间防止数值溢出或无效值。cpp#include cmath // 用于 fmod// 将任意角度归一化到 [0, 360)double normalizeAngle(double angle) {// 取模运算结果在 [0, 360) 范围angle std::fmod(angle, 360.0);if (angle 0) {angle 360.0;}// 处理浮点精度导致的 -0.0if (angle 0.0) angle 0.0;return angle;}// 角度增加或减少 delta 度double adjustAngle(double originalAngle, double delta) {return normalizeAngle(originalAngle delta);} **注意**fmod 返回的符号与被除数相同所以需要额外的负数调整。对于整数角度也可用 % 运算符但浮点数场景必须用 fmod。七、类型判断使用 typeid 与更好的替代方案7.1 使用 typeid 运算符cpp#include iostream#include typeinfoint a 10;if (typeid(a) typeid(int)) {std::cout a 是 int 类型\n;}7.2 局限性- typeid 返回的 std::type_info 对象的比较依赖于实现可能不稳定。- 对于多态类型typeid 会返回最派生类型需要启用 RTTI。- 过多使用 typeid 通常意味着设计问题如滥用运行时类型分支应优先考虑虚函数或 std::variant。7.3 更好的替代方案- **编译期类型比较**使用 std::is_sameT, U::valueC11。- **运行时分发**使用虚函数或 std::visit针对 std::variant。- **常量表达式判断**if constexpr 和 std::is_same_v。cpptemplatetypename Tvoid process(T value) {if constexpr (std::is_same_vstd::decay_tT, int) {std::cout 处理 int\n;} else {std::cout 其他类型\n;}}八、构造函数 ≠ 成员函数正确初始化方式很多初学者会错误地尝试直接调用构造函数来重新初始化对象这是不可能的。构造函数仅在对象创建时执行一次。8.1 错误做法cppPerson p(张三, 20, 1.75f, true);p.Person(李四, 22, 1.80f, false); // 错误不能直接调用构造函数8.2 正确做法- **重新赋值**通过成员函数修改或使用拷贝赋值运算符。- **placement new**高级用法在已有内存上重新构造对象但需要手动析构旧对象。- **使用智能指针**重新创建新对象并替换指针。8.3 正确的构造函数定义与使用cpp#include iostream#include stringusing namespace std;class Person {private:string name;int age;float height;bool isStudent;public:// 构造函数使用初始化列表Person(string n, int a, float h, bool s): name(n), age(a), height(h), isStudent(s) {}void printInfo() const {cout 姓名 name , 年龄 age , 身高 height m, 学生 (isStudent ? 是 : 否) endl;}};int main() {Person p(张三, 20, 1.75f, true);p.printInfo();// 如需重新初始化可以创建新对象并赋值需要实现赋值运算符return 0;} **提示**若需要修改对象的所有成员建议提供 reset() 成员函数或直接使用赋值操作。九、总结# 总结与建议表[[summary]]common_issue C 语言 bool 类型solution 优先 #include stdbool.hC99 以上[[summary]]common_issue 静态对象数组solution 确保类有默认构造函数或使用初始化列表[[summary]]common_issue 编码转换Windowssolution 使用 MultiByteToWideChar WideCharToMultiByte[[summary]]common_issue 深拷贝 vs 浅拷贝solution 遵循三/五法则优先使用 RAII 容器[[summary]]common_issue 重复初始化代码solution 使用 C11 委托构造函数[[summary]]common_issue 角度归一化solution fmod 负数修正注意浮点精度[[summary]]common_issue 运行时类型判断solution 优先虚函数谨慎使用 typeid[[summary]]common_issue 构造函数调用solution 不能显式调用通过创建新对象或赋值修改**核心原则**- **理解 C 的资源管理语义**拷贝、赋值、析构应保持一致。- **利用现代 C 特性**委托构造函数、移动语义、constexpr 等简化代码并提高安全性。- **避免 C 风格的强制转换和类型判断**尽量使用模板和编译期多态。希望这份指南能帮助你避开常见陷阱写出更高质量的 C 代码。如果你有其他避坑经验欢迎在评论区分享交流。