从 C 转向 C面向已有 C 语言基础的人员。由于 C的基础语法与 C 语言有相似之处故仅整理二者核心差异与 C的新增特性。阅读后即可上手写基础 C 程序并过渡到类、模板、STL 学习。本文只讲核心语法复杂用法后续按需补充。前言学校这学期开了 C的课程为了复习巩固遂开始对之前的 C笔记进行整理一周了才整理一篇由于本人当初学的时候是从 C 学到 C的当时也很懵逼又把 C基础语法学了一遍遂整理此篇希望能帮助到大家从 C 快速过渡到 C基础语法大多互通。下一步类与面向对象。慢慢来吧♀️核心差异速查表下表涉及部分 C 新语法看不懂可先行跳过主要用于快速对比差异。常见任务C 写法C 写法输出printf(Hello\n);cout Hello endl;输入scanf(%d, a);cin a;字符串定义char str[100];string str;字符串拷贝strcpy(dest, src);dest src;动态分配单个对象int* p malloc(sizeof(int));int* p new int(42);释放单个对象free(p);delete p;动态数组int* arr malloc(n*sizeof(int));int* arr new int[n];或vectorint v(n);释放数组free(arr);delete[] arr;函数参数传递大对象传指针void f(Struct* s)传引用void f(Struct s)空指针NULLnullptr布尔类型用int模拟 (0/1)bool/true/false常量定义#define PI 3.14constexpr double PI 3.14;类型别名typedef int MyInt;using MyInt int;程序结构与命名空间Hello World!#include iostream using namespace std; // 仅示例用大型项目建议显式写 std:: int main() { cout Hello World! endl; return 0; }差异说明#include iostream对应 C 的stdio.h。同为头文件包含标准输入输出流。using namespace std;引入标准命名空间用于简化代码。cout ... endlC标准输出流endl换行并刷新缓冲区。注意命名空间的引入不可滥用小型示例可用大型项目推荐显式写法如std::cout命名空间为什么需要命名空间命名空间主要是为了解决命名冲突的问题。不同库可能定义同名函数/变量命名空间可将其隔离。定义与使用如果你需要为自己写的函数构建命名空间可以 参考下面的语法namespace Mylib { void print() { std::cout Mylib::print std:: endl; } } // 调用时必须加上命名空间前缀 Mylib::print(); // 输出 MyLib::print不难发现命名空间中可以使用其他命名空间的内容。标准命名空间 stdC标准库的所有内容都定义在std命名空间中是最常使用的命名空间。其使用方式如下加前缀std::cout Hello std::endl;using 声明引入单用法using std::cout; using std::endl; cout Hello endl;using 指令引入整个命名空间慎用⚠️using namespace std; cout Hello endl;嵌套与别名有时为了更好地管理可以嵌套定义命名空间。但在这种情况下调用方式就比较繁琐。此时可以通过为命名空间起别名的方式解决这个问题。示例如下namespace Outer { namespace Inner { void func() } } // 调用Outer::Inner::func(); //起别名 namespace OI Outer::Inner; OI::func();输入输出I/OC 用流做输入输出无需格式化控制符类型安全自动识别类型。何为流stream数据传输通道cin/cout/cerr/clog都是流对象。输入 cinint a,b; cin a b;从流中提取根据变量类型解析数据。自动跳过空格、Tab回车。输出 cout在上面的内容中我们已经使用了cout的基本用法。这里给出一个稍微复杂的样例int a 10; double b 3.14; cout a a ,b b endl;可连续输出把数据送入流。不自动换行使用\n或endl后者会刷新缓冲区略慢。错误/日志输出此处简单介绍一下 C中的标准错误输出和标准日志输出。// 标准错误流 cerr 错误信息 endl; // 标准日志流 clog 日志信息 endl;目前来说这部分内容不常用但当接触大型项目和团队协作时错误处理与日志是不可或缺的技能。格式化控制有时你不得不格式化变量的输出形式比如保留 两位小数。这时可以使用iomanip中的操纵符来实现。比如保留 n 位小数可以用setprecison(n)搭配fixed来实现#include iomanip cout setprecision(2) fixed 3.14159; // 输出3.14setprecision()用于保留有效数字位数搭配fixed可实现保留小数。引用引用是变量的别名必须在定义时初始化且不能改变指向。int a 10; // int ref; 错误(必须初始化) int ref a; // ref 是 a 的别名 ref 20; // a 变为 20不难发现修改引用的值原变量的值也会改变。因此其可替代指针作为函数参数进行传递void swap(int x,int y) { int tmp x; x y; y tmp; } // 调用 swap(a,b)直接传变量自动传引用足以见得引用传参可直接修改原变量的值并且在调用时直接传变量即可会自动传相应变量的引用。是不是十分的简便C函数传参建议使用引用如果想要修改函数外变量。常见错误引用未初始化误以为ref b能改变引用的指向实际是将b的值赋给被引用的变量函数返回局部变量的引用悬垂引用指针 VS 引用此处简单列出了二者区别特性指针引用可重新绑定是否可为空是需判空否必须初始化语法需用*和直接使用别名函数增强函数重载现在我们要写多个功能相同但操作对象不同的函数比如对不同数据类型的数据求和。如果是 C 语言是不是要写add_int()、add_double()等多个不同的函数名管理起来十分麻烦。而 C引入了函数重载(overload)允许函数名相同参数列表不同调用时编辑器可根据参数个数和类型自动匹配比如add(int a,int b)add(double a,double b)是不是就很方便了⚠️注意仅返回值类型不同不能构成重载调用时无法区分。缺省参数默认参数C允许函数在声明或定义时给参数一个默认值。调用时若省略该参数则使用默认值。int add_or_subtract(int a,int b,int c 1) { if(c1) return a b; if(c0) return a - b; } cout add_or_subtract(1,1); // 输出 2 cout add_or_subtract(1,1,0); // 输出 0⚠️ 核心规则默认参数必须从右向左连续提供且一般在声明中指定若分离声明与定义。常见错误中间有非缺省参数void func(int a,int b 10,int c)❌ c 也必须有默认值。声明与定义分离时默认值只能在一处给出。内联函数inlineC用inline替代 C 语言的带参宏#define。inline建议编译器将函数视为内联函数处理。作用在编译时将函数体直接嵌入调用处省去函数调用的开销。适用场景代码短小且频繁调用的函数。对比 C 宏宏只是文本替换极易出错无类型检查、参数有副作用如i被执行两次内联函数本质还是函数既有宏的效率又安全。注意inline只是对编译器的建议编译器会根据内部代码开销和是否存在循环/递归来自行选择。滥用会导致代码膨胀可执行文件变大。补充C 中也有inlineC 99但 C更常用。动态内存管理C使用new/delete替代malloc/free类型安全且能自动调用构造/析构后续类相关。操作C 语法对应 C 语法分配单个对象int* p new int(5);int* p malloc(sizeof(int));释放单个对象delete p;free(p);分配数组int* arr new int[10];int* arr malloc(10*sizeof(int));释放数组delete[] arr;free(arr);重要new和delete、new[]和delete[]必须配对使用否则行为未定义。二维数组的实现逻辑和 C 类似都需要先分配行指针数组再为每一行分配列数组// 1. 分配行指针数组 int **arr new int*[rows]; // 2. 为每一行分配列数组 for(int i 0;i rows;i) { arr[i] new int[cols]; } // 1. 先释放每一行的列数组 for(int i 0;i rows;i) { delete[] arr[i]; } // 2. 再释放行指针数组 delete[] arr;现代 C的忠告动态数组不推荐用上述手动方式实现需手动管理内存现代 C推荐使用vector见STL部分可自动管理内存。动态数组的替代vectorvector实际为 STL 的内容但鉴于上文提到了且比较常用遂在此做简单介绍。vector容器可自动管理内存、动态增长比new[]更安全。#include vector vectorint arr {1,2,3} // 初始化 arr.push_back(4); // 尾部添加 cout arr[0] , size arr.size() endl;详细用法在STL章节。字符串std::stringC 风格字符串char[]仍然可用但 C提供了string类型需要头文件string。#include string using std::string; string s1 Hello; string s2 World; string s3 s1 s2; // 字符串拼接 cout s3 endl; // Hello World // 常用操作 int len s1.length(); // 或 size() if (s1 s2) { }; // 直接比较 s1[0] h; // 索引访问修改 s1.append( C); // 追加目前只需把它理解成一个智能的字符数组内部提供了很多简便用法本质为类涉及运算符重载。布尔类型与空指针bool类型C 内置bool取值true1或false0bool flag true; if(flag) { }nullptr关键字用于表示空指针比NULL更安全避免与整数重载混淆知道即可。int* p nullptr; if(p nullptr) { }常量const与constexprC 推荐用const或constexpr替代#define宏。为什么 C 语言总爱用#define因为 C 语言的const只是“只读变量”不是真正的常量const int SIZE 10; int arr[SIZE]; // ❌ C 语言报错数组大小需要常量SIZE 不算所以 C 只能被迫用#define SIZE 10文本替换无类型坑多。C 的解法C 强化了const只要初始值是常量它就是编译期常量const int SIZE 10; int arr[SIZE]; // ✅ C 合法constexprC11比const更严格强制要求值必须在编译期算出来语义更安全。constexpr double PI 3.14; // 推荐真正的常量一般来说const用的较多constexpr常用于性能优化或模版参数。类型别名usingvstypedef两者功能相似但using语法更统一尤其在模板中后续学习。typedef int MyInt; using MyInt int; // 等义 // 函数指针示例 typedef void (*Func)(int); using Func void(*)(int); // 更清晰类型推导auto编译器自动推导变量类型减少冗长代码。auto a 10; // int auto b 3.14; // double auto c Hello; // const char* (注意不是 string) auto d std::string(Hello); // 显式构造 string // 常用场景迭代器 vectorint v {1,2,3}; for (auto it v.begin(); it ! v.end(); it) { }注意auto会去掉引用和const如需保留可用auto或const auto。迭代器此处了解即可。范围 for 循环 (Range-based for)用于简化遍历数组或容器STLC11 引入。int arr[] {1, 2, 3, 4}; for (int x : arr) { cout x ; } // 修改元素需用引用 for (int x : arr) { x * 2; } // 配合 string 或 vector string s Hello; for (char ch : s) { cout ch; }强制类型转换了解C 提供更细粒度的类型转换操作符比 C 的(type)value更安全易查。转换操作符用途示例static_cast常见类型转换int→doubledouble d static_castdouble(i);const_cast去掉 const 属性const_castint*(c);reinterpret_cast底层位模式转换危险指针转整数等dynamic_cast多态类型向下转换需继承类与对象章节学习目前只需了解static_cast基本可替代 C 风格转换。下一篇面向过程 VS 面向对象待整理
C++笔记(01)从C到C++
从 C 转向 C面向已有 C 语言基础的人员。由于 C的基础语法与 C 语言有相似之处故仅整理二者核心差异与 C的新增特性。阅读后即可上手写基础 C 程序并过渡到类、模板、STL 学习。本文只讲核心语法复杂用法后续按需补充。前言学校这学期开了 C的课程为了复习巩固遂开始对之前的 C笔记进行整理一周了才整理一篇由于本人当初学的时候是从 C 学到 C的当时也很懵逼又把 C基础语法学了一遍遂整理此篇希望能帮助到大家从 C 快速过渡到 C基础语法大多互通。下一步类与面向对象。慢慢来吧♀️核心差异速查表下表涉及部分 C 新语法看不懂可先行跳过主要用于快速对比差异。常见任务C 写法C 写法输出printf(Hello\n);cout Hello endl;输入scanf(%d, a);cin a;字符串定义char str[100];string str;字符串拷贝strcpy(dest, src);dest src;动态分配单个对象int* p malloc(sizeof(int));int* p new int(42);释放单个对象free(p);delete p;动态数组int* arr malloc(n*sizeof(int));int* arr new int[n];或vectorint v(n);释放数组free(arr);delete[] arr;函数参数传递大对象传指针void f(Struct* s)传引用void f(Struct s)空指针NULLnullptr布尔类型用int模拟 (0/1)bool/true/false常量定义#define PI 3.14constexpr double PI 3.14;类型别名typedef int MyInt;using MyInt int;程序结构与命名空间Hello World!#include iostream using namespace std; // 仅示例用大型项目建议显式写 std:: int main() { cout Hello World! endl; return 0; }差异说明#include iostream对应 C 的stdio.h。同为头文件包含标准输入输出流。using namespace std;引入标准命名空间用于简化代码。cout ... endlC标准输出流endl换行并刷新缓冲区。注意命名空间的引入不可滥用小型示例可用大型项目推荐显式写法如std::cout命名空间为什么需要命名空间命名空间主要是为了解决命名冲突的问题。不同库可能定义同名函数/变量命名空间可将其隔离。定义与使用如果你需要为自己写的函数构建命名空间可以 参考下面的语法namespace Mylib { void print() { std::cout Mylib::print std:: endl; } } // 调用时必须加上命名空间前缀 Mylib::print(); // 输出 MyLib::print不难发现命名空间中可以使用其他命名空间的内容。标准命名空间 stdC标准库的所有内容都定义在std命名空间中是最常使用的命名空间。其使用方式如下加前缀std::cout Hello std::endl;using 声明引入单用法using std::cout; using std::endl; cout Hello endl;using 指令引入整个命名空间慎用⚠️using namespace std; cout Hello endl;嵌套与别名有时为了更好地管理可以嵌套定义命名空间。但在这种情况下调用方式就比较繁琐。此时可以通过为命名空间起别名的方式解决这个问题。示例如下namespace Outer { namespace Inner { void func() } } // 调用Outer::Inner::func(); //起别名 namespace OI Outer::Inner; OI::func();输入输出I/OC 用流做输入输出无需格式化控制符类型安全自动识别类型。何为流stream数据传输通道cin/cout/cerr/clog都是流对象。输入 cinint a,b; cin a b;从流中提取根据变量类型解析数据。自动跳过空格、Tab回车。输出 cout在上面的内容中我们已经使用了cout的基本用法。这里给出一个稍微复杂的样例int a 10; double b 3.14; cout a a ,b b endl;可连续输出把数据送入流。不自动换行使用\n或endl后者会刷新缓冲区略慢。错误/日志输出此处简单介绍一下 C中的标准错误输出和标准日志输出。// 标准错误流 cerr 错误信息 endl; // 标准日志流 clog 日志信息 endl;目前来说这部分内容不常用但当接触大型项目和团队协作时错误处理与日志是不可或缺的技能。格式化控制有时你不得不格式化变量的输出形式比如保留 两位小数。这时可以使用iomanip中的操纵符来实现。比如保留 n 位小数可以用setprecison(n)搭配fixed来实现#include iomanip cout setprecision(2) fixed 3.14159; // 输出3.14setprecision()用于保留有效数字位数搭配fixed可实现保留小数。引用引用是变量的别名必须在定义时初始化且不能改变指向。int a 10; // int ref; 错误(必须初始化) int ref a; // ref 是 a 的别名 ref 20; // a 变为 20不难发现修改引用的值原变量的值也会改变。因此其可替代指针作为函数参数进行传递void swap(int x,int y) { int tmp x; x y; y tmp; } // 调用 swap(a,b)直接传变量自动传引用足以见得引用传参可直接修改原变量的值并且在调用时直接传变量即可会自动传相应变量的引用。是不是十分的简便C函数传参建议使用引用如果想要修改函数外变量。常见错误引用未初始化误以为ref b能改变引用的指向实际是将b的值赋给被引用的变量函数返回局部变量的引用悬垂引用指针 VS 引用此处简单列出了二者区别特性指针引用可重新绑定是否可为空是需判空否必须初始化语法需用*和直接使用别名函数增强函数重载现在我们要写多个功能相同但操作对象不同的函数比如对不同数据类型的数据求和。如果是 C 语言是不是要写add_int()、add_double()等多个不同的函数名管理起来十分麻烦。而 C引入了函数重载(overload)允许函数名相同参数列表不同调用时编辑器可根据参数个数和类型自动匹配比如add(int a,int b)add(double a,double b)是不是就很方便了⚠️注意仅返回值类型不同不能构成重载调用时无法区分。缺省参数默认参数C允许函数在声明或定义时给参数一个默认值。调用时若省略该参数则使用默认值。int add_or_subtract(int a,int b,int c 1) { if(c1) return a b; if(c0) return a - b; } cout add_or_subtract(1,1); // 输出 2 cout add_or_subtract(1,1,0); // 输出 0⚠️ 核心规则默认参数必须从右向左连续提供且一般在声明中指定若分离声明与定义。常见错误中间有非缺省参数void func(int a,int b 10,int c)❌ c 也必须有默认值。声明与定义分离时默认值只能在一处给出。内联函数inlineC用inline替代 C 语言的带参宏#define。inline建议编译器将函数视为内联函数处理。作用在编译时将函数体直接嵌入调用处省去函数调用的开销。适用场景代码短小且频繁调用的函数。对比 C 宏宏只是文本替换极易出错无类型检查、参数有副作用如i被执行两次内联函数本质还是函数既有宏的效率又安全。注意inline只是对编译器的建议编译器会根据内部代码开销和是否存在循环/递归来自行选择。滥用会导致代码膨胀可执行文件变大。补充C 中也有inlineC 99但 C更常用。动态内存管理C使用new/delete替代malloc/free类型安全且能自动调用构造/析构后续类相关。操作C 语法对应 C 语法分配单个对象int* p new int(5);int* p malloc(sizeof(int));释放单个对象delete p;free(p);分配数组int* arr new int[10];int* arr malloc(10*sizeof(int));释放数组delete[] arr;free(arr);重要new和delete、new[]和delete[]必须配对使用否则行为未定义。二维数组的实现逻辑和 C 类似都需要先分配行指针数组再为每一行分配列数组// 1. 分配行指针数组 int **arr new int*[rows]; // 2. 为每一行分配列数组 for(int i 0;i rows;i) { arr[i] new int[cols]; } // 1. 先释放每一行的列数组 for(int i 0;i rows;i) { delete[] arr[i]; } // 2. 再释放行指针数组 delete[] arr;现代 C的忠告动态数组不推荐用上述手动方式实现需手动管理内存现代 C推荐使用vector见STL部分可自动管理内存。动态数组的替代vectorvector实际为 STL 的内容但鉴于上文提到了且比较常用遂在此做简单介绍。vector容器可自动管理内存、动态增长比new[]更安全。#include vector vectorint arr {1,2,3} // 初始化 arr.push_back(4); // 尾部添加 cout arr[0] , size arr.size() endl;详细用法在STL章节。字符串std::stringC 风格字符串char[]仍然可用但 C提供了string类型需要头文件string。#include string using std::string; string s1 Hello; string s2 World; string s3 s1 s2; // 字符串拼接 cout s3 endl; // Hello World // 常用操作 int len s1.length(); // 或 size() if (s1 s2) { }; // 直接比较 s1[0] h; // 索引访问修改 s1.append( C); // 追加目前只需把它理解成一个智能的字符数组内部提供了很多简便用法本质为类涉及运算符重载。布尔类型与空指针bool类型C 内置bool取值true1或false0bool flag true; if(flag) { }nullptr关键字用于表示空指针比NULL更安全避免与整数重载混淆知道即可。int* p nullptr; if(p nullptr) { }常量const与constexprC 推荐用const或constexpr替代#define宏。为什么 C 语言总爱用#define因为 C 语言的const只是“只读变量”不是真正的常量const int SIZE 10; int arr[SIZE]; // ❌ C 语言报错数组大小需要常量SIZE 不算所以 C 只能被迫用#define SIZE 10文本替换无类型坑多。C 的解法C 强化了const只要初始值是常量它就是编译期常量const int SIZE 10; int arr[SIZE]; // ✅ C 合法constexprC11比const更严格强制要求值必须在编译期算出来语义更安全。constexpr double PI 3.14; // 推荐真正的常量一般来说const用的较多constexpr常用于性能优化或模版参数。类型别名usingvstypedef两者功能相似但using语法更统一尤其在模板中后续学习。typedef int MyInt; using MyInt int; // 等义 // 函数指针示例 typedef void (*Func)(int); using Func void(*)(int); // 更清晰类型推导auto编译器自动推导变量类型减少冗长代码。auto a 10; // int auto b 3.14; // double auto c Hello; // const char* (注意不是 string) auto d std::string(Hello); // 显式构造 string // 常用场景迭代器 vectorint v {1,2,3}; for (auto it v.begin(); it ! v.end(); it) { }注意auto会去掉引用和const如需保留可用auto或const auto。迭代器此处了解即可。范围 for 循环 (Range-based for)用于简化遍历数组或容器STLC11 引入。int arr[] {1, 2, 3, 4}; for (int x : arr) { cout x ; } // 修改元素需用引用 for (int x : arr) { x * 2; } // 配合 string 或 vector string s Hello; for (char ch : s) { cout ch; }强制类型转换了解C 提供更细粒度的类型转换操作符比 C 的(type)value更安全易查。转换操作符用途示例static_cast常见类型转换int→doubledouble d static_castdouble(i);const_cast去掉 const 属性const_castint*(c);reinterpret_cast底层位模式转换危险指针转整数等dynamic_cast多态类型向下转换需继承类与对象章节学习目前只需了解static_cast基本可替代 C 风格转换。下一篇面向过程 VS 面向对象待整理