
目录1.C的第一个程序2.命名空间namespace一、为什么有namespace二、namespace的特性特性1命名空间可以拆分追加定义特性2命名空间可以嵌套特性3匿名命名空间无名字namespace特性4命名空间的三种使用方式3.C的输入和输出一、标准输出 cout二、标准输入 cin三、连续输入与输出四、注意事项4.缺省函数一、缺省参数的基本用法二、缺省参数的规则三、注意事项5.函数重载一、函数重载的基本用法二、函数重载的规则三、函数重载的匹配规则四、函数重载与缺省参数五、注意事项6.引用一、引用的特性二、引用的使用1. 引用作为函数参数2. 引用作为函数返回值3. 常引用4. 引用与指针的区别7.inline一、inline 的基本用法二、inline 的作用三、inline 的注意事项四、inline 与宏的对比示例8.nullptr一、nullptr 的基本用法二、nullptr 与 NULL 的区别三、nullptr 与函数重载四、nullptr 的注意事项1.C的第一个程序#includestdio.h int main() { printf(hello world!\n); return 0; }C 设计目标之一就是兼容绝大多数 C 语言语法C 是 C 的超集。所以 C 语言实现的 hello world 依旧可以运行C 中需要把定义文件代码后缀改为 .cppvs 编译器看到是 .cpp 就会调用 C 编译器编译linux 下要用 g 编译不再是 gcc。当然 C 也有自己的输入和输出在 C 中 hello world! 应该这样写#includeiostream using namespace std; int main() { couthello world!endl; return 0; }1.iostream标准输入输出流头文件input output stream2.couthello world!endl;cout控制台输出对象输出运算符把后面的内容送到屏幕打印hello world!字符串常量原样输出endl换行同时刷新缓冲区2.命名空间namespace一、为什么有namespace解决名字冲突(重名)在编译的过程中出现的报错原因是在stdlib.h标准库中有一个全局函数叫rand()(生成随机数的函数)当定义了一个全局变量int rand 10rand这个名字既是全局函数名又是全局变量名名字冲突导致在编译的过程中出现报错为了解决这个问题在C中引进了namespace。二、namespace的特性特性1命名空间可以拆分追加定义namespace A { int a 1; } name space B { int b 2; } //等价于 //namespace A //{ // int a 1; // int b 2; //}特性2命名空间可以嵌套namespace Outer { namespace Inner { void func() { std::outOuter::Inner::funcstd::endl; } } } // 访问Outer::Inner::func();特性3匿名命名空间无名字namespace里面的标识符仅当前源文件可见等价于内部链接属性代替staticnamespace { int num 10;//只能在本cpp使用 }特性4命名空间的三种使用方式1.指定命名空间访问。namespace N { int a 10; int b 20; } int main() { cout N::a endl; return 0; }2.using将命名空间中某个成员展开。namespace N { int a 10; int b 20; } using N::a; int main() { cout a endl; return 0; }3.展开命名空间中全部成员。namespace N { int a 10; int b 20; } using namespace N; int main() { cout a b endl; return 0; }3.C的输入和输出C 除了兼容 C 语言的 printf 和 scanf 之外还提供了自己的一套输入输出方式使用标准输入输出流对象cin和cout配合运算符和完成数据的读取与打印。一、标准输出 coutcout 是控制台输出对象配合输出运算符将内容送到屏幕打印。endl 表示换行同时刷新缓冲区。#includeiostream using namespace std; int main() { cout hello world! endl; return 0; }运行结果hello world!二、标准输入 cincin 是控制台输入对象配合输入运算符从键盘读取数据。读取时自动根据变量类型解析数据多个数据之间用空格或换行分隔。#includeiostream using namespace std; int main() { int a 0; double b 0.0; cin a b; cout a a , b b endl; return 0; }运行示例输入 10 3.1410 3.14 a 10, b 3.14三、连续输入与输出 和 支持连续拼接可以一次性读取或打印多个数据书写简洁可读性强。#includeiostream using namespace std; int main() { int x 0; int y 0; cin x y; cout x y x y endl; return 0; }四、注意事项使用 cin 和 cout 前需要包含头文件iostream。如果使用了 using namespace std;可以直接写 cin、cout、endl否则需要写成 std::cin、std::cout、std::endl。cin 读取字符串时遇到空格会停止读取整行请使用 getline 函数。endl 除了换行还会刷新缓冲区如果只是换行也可以使用 \n。4.缺省函数缺省函数默认参数函数是指在函数声明或定义时为参数指定默认值调用时如果省略该参数编译器会自动使用默认值。缺省参数让函数调用更灵活减少重复代码。一、缺省参数的基本用法在函数声明时给参数赋默认值调用时可以省略带默认值的参数。#includeiostream using namespace std; void Print(int a, int b 10) { cout a a , b b endl; } int main() { Print(1); // b 使用默认值 10 Print(1, 20); // b 使用传入值 20 return 0; }运行结果a 1, b 10 a 1, b 20二、缺省参数的规则缺省参数必须从右往左连续设置不能跳跃。例如void f(int a 1, int b)是错误的。缺省值只能在函数声明或定义中指定一次不能重复指定。缺省值可以是常量、全局变量或表达式但不能是局部变量。调用时如果传入参数则使用传入值如果省略则使用默认值。三、注意事项缺省参数一般写在函数声明中定义处不要再写默认值避免重复定义报错。缺省参数让函数调用更简洁但过度使用会降低代码可读性建议按需使用。在头文件中声明函数时给出缺省值源文件定义时不再重复给出。5.函数重载函数重载Function Overloading是 C 中一项重要的特性它允许在同一个作用域内定义多个同名函数只要它们的参数列表不同参数个数、类型或顺序不同即可。编译器会根据调用时传入的参数自动匹配最合适的函数版本。一、函数重载的基本用法下面通过一个简单的例子来理解函数重载。我们定义三个同名函数 Add分别用于计算两个整数、两个浮点数以及三个整数的和。#includeiostream using namespace std; int Add(int a, int b) { return a b; } double Add(double a, double b) { return a b; } int Add(int a, int b, int c) { return a b c; } int main() { cout Add(1, 2) endl; // 调用 int Add(int, int) cout Add(1.5, 2.5) endl; // 调用 double Add(double, double) cout Add(1, 2, 3) endl; // 调用 int Add(int, int, int) return 0; }运行结果3 4 6可以看到虽然函数名都是 Add但编译器根据实参的类型和个数自动选择了对应的函数版本这就是函数重载的核心作用。二、函数重载的规则函数重载要求参数列表不同包括参数个数不同、参数类型不同或参数顺序不同。仅仅返回值不同不能构成函数重载。例如int f()和double f()会报重复定义错误。参数顺序不同也可以构成重载例如void f(int a, double b)和void f(double a, int b)是两个不同的函数。重载函数必须在同一个作用域内跨命名空间或跨类的同名函数不属于重载。三、函数重载的匹配规则当调用一个重载函数时编译器会按照以下优先级进行匹配精确匹配实参类型与形参类型完全一致。类型提升匹配例如 char 提升为 intfloat 提升为 double。标准转换匹配例如 int 转换为 doubleint 转换为 long。用户自定义转换匹配通过构造函数或类型转换运算符实现。如果编译器在匹配过程中发现多个函数都能匹配且无法区分优先级就会产生二义性歧义导致编译报错。四、函数重载与缺省参数缺省参数和函数重载有时可以互相替代但要注意避免二义性。例如下面的写法会导致调用f()时编译器无法确定该调用哪个函数。void f(int a 1) { } void f() { } // 错误调用 f() 时产生二义性因此在设计接口时如果同时使用缺省参数和函数重载需要仔细考虑是否会产生歧义。五、注意事项函数重载不能仅靠返回值区分必须保证参数列表不同。重载函数过多会降低代码可读性建议按需使用避免滥用。在 C 语言中不支持函数重载这是 C 与 C 的一个重要区别。函数重载与函数模板Template功能有重叠复杂场景下可以考虑使用模板替代。6.引用引⽤不是新定义⼀个变量⽽是给已存在变量取了⼀个别名编译器不会为引⽤变量开辟内存空间 它和它引⽤的变量共⽤同⼀块内存空间。类型名 引用别名 引用对象int main() { int a 10; //引用:b和c是a的别名 int b a; int c a; //也可以给b取别名d也相当于a的别名 int d b; //这里取地址可以看到是一样的 cout a endl; cout b endl; cout c endl; cout d endl; return 0; }运行结果一、引用的特性引用在定义时必须初始化。一个变量可以有多个引用体。引用一旦引用一个实体在不能引用其他实体。int main() { int a 10; int b 20; //int c; //编译报错引用必须初始化 int d a; int e a; //这里并非让d引用b因为C引⽤不能改变指向这⾥是⼀个赋值 d b; //把b赋值给d之后相当于把b赋值给了a cout a: a endl; cout d: d endl; //通过打印a和d的地址可以看到d还是a的引用 cout a: a endl; cout d: d endl; return 0; }运行结果二、引用的使用引用最常见的应用场景是作为函数参数和函数返回值下面分别介绍。1. 引用作为函数参数引用传参可以让函数直接操作实参本身而不需要拷贝一份数据既节省内存又能在函数内部修改实参的值。#includeiostream using namespace std; // 值传递形参是实参的拷贝修改形参不影响实参 void Swap1(int a, int b) { int tmp a; a b; b tmp; } // 引用传递形参是实参的别名修改形参就是修改实参 void Swap2(int a, int b) { int tmp a; a b; b tmp; } int main() { int x 10, y 20; Swap1(x, y); cout Swap1 之后: x x , y y endl; Swap2(x, y); cout Swap2 之后: x x , y y endl; return 0; }运行结果Swap1 之后: x 10, y 20 Swap2 之后: x 20, y 10可以看到值传递的 Swap1 并没有真正交换 x 和 y而引用传递的 Swap2 成功交换了两个变量的值。2. 引用作为函数返回值当函数返回引用时返回的是变量本身而不是拷贝这样可以避免拷贝开销同时允许对返回值进行赋值操作。#includeiostream using namespace std; int g_count 0; // 返回引用可以避免拷贝也可以作为左值使用 int GetCount() { return g_count; } int main() { GetCount() 100; // 通过引用修改全局变量 cout g_count g_count endl; GetCount(); cout g_count g_count endl; return 0; }运行结果g_count 100 g_count 101需要注意的是返回引用时不能返回局部变量的引用因为局部变量在函数结束后会被销毁返回的引用会变成悬空引用。3. 常引用使用 const 修饰的引用称为常引用常引用不能通过引用修改所引用的变量常用于函数参数防止在函数内部意外修改实参。#includeiostream using namespace std; void Print(const int a) { // a 100; // 错误常引用不能修改所引用的变量 cout a a endl; } int main() { int x 10; Print(x); // 常引用可以绑定常量 const int r 20; cout r r endl; return 0; }运行结果a 10 r 204. 引用与指针的区别引用在定义时必须初始化指针可以不初始化。引用一旦绑定某个变量就不能再改变指向指针可以随时改变指向。引用没有自己的内存空间它和所引用的变量共用同一块内存指针有自己的内存空间存储的是变量的地址。引用比指针更安全使用更简洁但灵活性不如指针。7.inlineinline内联函数是 C 中用于优化函数调用开销的一种机制。它建议编译器在调用处直接将函数体展开从而避免函数调用的压栈、跳转和返回等额外开销适合体积小、调用频繁的函数。一、inline 的基本用法在函数定义或声明前加上 inline 关键字即可将该函数声明为内联函数。#includeiostream using namespace std; // 内联函数在调用处直接展开函数体 inline int Add(int a, int b) { return a b; } int main() { int result Add(3, 5); // 等价于 int result 3 5; cout result result endl; return 0; }运行结果result 8二、inline 的作用减少函数调用开销省去参数压栈、函数跳转和返回等步骤提升执行效率。适合小函数函数体很短、调用频繁时收益明显例如简单的 getter/setter 或数学运算。与宏的对比inline 是真正的函数有类型检查比宏更安全宏只是文本替换容易出错。三、inline 的注意事项inline 只是建议编译器有权忽略。函数体过大、包含循环或递归时编译器通常不会内联。内联函数通常定义在头文件中因为编译器需要在每个调用点看到完整的函数体才能展开。inline 不能用于递归函数因为递归无法在编译期无限展开。在类内定义的成员函数默认是内联的无需显式加 inline 关键字。四、inline 与宏的对比示例下面通过一个例子对比宏和内联函数的区别#includeiostream using namespace std; // 宏纯文本替换没有类型检查 #define SQUARE_MACRO(x) ((x) * (x)) // 内联函数有类型检查更安全 inline int Square(int x) { return x * x; } int main() { int a 5; cout 宏: SQUARE_MACRO(a 1) endl; // 展开为 ((a1)*(a1)) cout 内联: Square(a 1) endl; // 直接计算 6*6 return 0; }运行结果宏: 36 内联: 36虽然这里结果相同但宏在复杂表达式下容易因优先级问题产生错误而内联函数作为真正的函数行为更可预测。8.nullptrnullptr 是 C11 引入的空指针常量用于替代 C 语言中的 NULL 宏。它拥有明确的类型 std::nullptr_t可以安全地赋值给任意类型的指针避免 NULL 在重载和类型推导场景下产生的二义性问题。一、nullptr 的基本用法nullptr 可以直接赋值给指针变量也可以用于指针的比较和初始化。#includeiostream using namespace std; int main() { int* p nullptr; // 空指针初始化 if (p nullptr) { cout p 是空指针 endl; } p new int(10); if (p ! nullptr) { cout *p *p endl; } delete p; p nullptr; // 释放后置空防止悬空指针 return 0; }运行结果p 是空指针 *p 10二、nullptr 与 NULL 的区别类型不同NULL 本质上是整数 0 或 ((void*)0)而 nullptr 是独立的 std::nullptr_t 类型。重载安全在函数重载中NULL 可能被匹配为 int 参数而 nullptr 只会匹配指针类型参数。类型推导使用 auto 或模板推导时nullptr 能保持指针语义NULL 则可能被推导为整数。三、nullptr 与函数重载下面通过一个例子展示 nullptr 在重载场景下的优势#includeiostream using namespace std; void Show(int x) { cout int 版本: x endl; } void Show(int* p) { cout 指针版本: (p nullptr ? 空指针 : 非空指针) endl; } int main() { Show(nullptr); // 明确调用指针版本 // Show(NULL); // 在部分编译器下可能产生二义性 return 0; }运行结果指针版本: 空指针可以看到使用 nullptr 可以明确匹配指针版本的重载函数而 NULL 在某些编译器下可能被当作整数 0导致调用 int 版本或产生二义性。四、nullptr 的注意事项nullptr 只能赋值给指针类型不能赋值给 int 等非指针类型。在 C11 及以后版本中建议统一使用 nullptr 代替 NULL。delete 指针后建议将其置为 nullptr避免悬空指针带来的安全隐患。nullptr 可以用于函数参数、返回值以及条件判断语义清晰。