)
C 由本贾尼・斯特劳斯特鲁普在贝尔实验室于 20 世纪 80 年代在 C 语言基础上研发最初叫作 “带类的 C”。作为一门强大且广泛应用的编程语言其设计融合了面向过程与面向对象的特性。后续历经 C98、C11、C17、C20 等标准迭代如今广泛用于系统开发、游戏引擎、高性能服务器与嵌入式开发等场景。本文将重点介绍五个在 C 编程中频繁使用且至关重要的基础概念命名空间、输入输出、缺省参数、函数重载和引用。目录1. 命名空间 (Namespace)1.1 定义与使用1.2 域查找优先级1.3 多场景下命名空间不同文件下相同命名空间不同文件下的同一个命名空间情况①内部变量名字不重复 —— 正常合并情况②内部出现同名变量 —— 编译报错⇒解决方案嵌套命名空间拓展【C11 简化嵌套写法】核心小结1.4 using 指令与声明2. 输入输出 (Input/Output)2.1 标准输出流 (cout)2.2 标准输入流 (cin)3. 缺省参数 (Default Arguments)3.1 基本用法3.2 规则与注意事项4. 函数重载 (Function Overloading)4.1 函数重载的定义4.2重点返回值不参与重载4.3 注意重载和缺省参数一起使用容易产生调用歧义4.4 重载与否C/C【核心函数名修饰规则】核心小结1. 命名空间 (Namespace)命名空间-----用于组织代码、避免命名冲突。两种冲突①代码模块与库冲突②代码模块之间冲突即当不同的库或代码模块定义了相同名称的标识符如变量、函数、类时命名空间可以将它们分隔开来。1.1 定义与使用定义命名空间使用namespace关键字使用域作用限定符::作用域解析运算符访问其成员。格式域名::访问的成员名1.2 域查找优先级查找规则局部域 全局域 命名空间域如果不做任何限定编译器优先找局部作用域的变量局部没有再去找全局。命名空间里面的内容默认不会自动被访问。int a 1; //全局 namespace bit { int a 2; //命名空间域 } int main() { int a 3; //局部 printf(%d\n, a); //输出3优先局部 printf(%d\n, bit::a);//输出2指定访问bit命名空间的a printf(%d\n, ::a); //输出1::左边不加域名代表访问全局域 return 0; }PS仅存在全局变量和命名空间的成员变量时即注释掉局部 int a 3会去访问全局变量----默认不去访问命名空间域的内容未展开/指定访问。1.3 多场景下命名空间不同文件下相同命名空间1不同变量→合并2相同变量 → 报错⇒ 解决方案嵌套解决不同文件下的同一个命名空间头文件bit.h和test.cpp源文件写同名 namespace bit编译器会把它们合并成同一个命名空间。情况①内部变量名字不重复 —— 正常合并bit.h namespace bit { int b 4; }test.cpp #includebit.h namespace bit { int a 2; } int main() { printf(%d\n,bit::b); //输出4来自头文件里面的bit return 0; }⇒ 原理不同文件的namespace bit会合并最终等价于namespace bit { int b 4; int a 2; }情况②内部出现同名变量 —— 编译报错bit.hnamespace bit { int a 4; }test.cpp #includebit.h namespace bit { int a 2; //合并之后bit里面有两个int a重名冲突 } int main() { printf(%d\n, bit::a); //报错变量重定义 return 0; }同一个命名空间合并之后不能出现一模一样的成员名字直接报重定义错误。⇒解决方案嵌套命名空间命名空间里面还可以再写命名空间用来进一步隔离名字解决重名问题。int a 1; namespace bit1 { int a 2; namespace bit2 //嵌套在bit1内部 { int a 4; } } int main() { printf(%d\n, bit1::a); // 2访问bit1下的a printf(%d\n, bit1::bit2::a); //4访问bit1里面bit2的a //printf(%d\n, bit2::a); //错误bit2属于bit1不能直接访问 return 0; }ps访问语法外层命名空间::内层命名空间::成员拓展【C11 简化嵌套写法】C11 之后可以简写嵌套命名空间namespace bit1::bit2 { int x 10; } //等价于 namespace bit1{ namespace bit2{ int x 10; } }访问依旧bit1::bit2::x核心小结多个文件写相同名字的 namespace编译器自动合并为同一个命名空间合并后命名空间内部不能有重名成员否则编译报错命名空间支持嵌套用来分层隔离标识符访问嵌套命名空间必须完整写外层::内层::变量不能直接拿内层名字。1.4 using 指令与声明为了避免每次都写冗长的命名空间前缀可以使用using指令。using 指令引入整个命名空间的所有成员可能引起命名污染需谨慎使用。展开全部usingeg.using namespace std;using namespace 域名→ 导入全部成员ps:using 指令不推荐在头文件在使用→每次别的.cpp文件a.cpp、b.cpp、c.cppinclude 这个头文件都要展开一遍到时候发生重名冲突没办法快速地找到出错文件即污染风险↑→ 而.cpp 是独立编译单元它里面写的 using namespace std; 作用域只局限在当前这一个 cpp 文件内部。using 声明仅引入命名空间中的特定成员更为安全。指定访问eg.using std::cout;using 域名成员→ 导入单个成员#include iostream using std::cout; // using 声明只引入 cout using std::endl; // using 声明只引入 endl // using namespace std; // using 指令引入整个 std 命名空间 int main() { cout Hello, World! endl; // 无需 std:: return 0; }2. 输入输出 (Input/Output)C 使用流Stream的概念进行输入输出操作主要涉及iostream库中的几个标准对象。2.1 标准输出流 (cout)std::cout是标准输出流对象通常连接到控制台使用流插入运算符 输出数据 【 写了using namespace std/using std::cout; 的情况可以直接写成 cout 】#include iostream int main() { int age 25; double salary 8000.50; std::cout Age: age std::endl; std::cout Salary: salary std::endl; // 连续输出 std::cout Name: Alice , Score: 95 std::endl; return 0; }2.2 标准输入流 (cin)std::cin是标准输入流对象从控制台读取数据使用流提取运算符 输入数据 【 写了using namespace std的情况可以直接写成 cin 】#include iostream #include string int main() { std::string name; int number; std::cout Enter your name: ; std::cin name; // 读取字符串遇到空格停止 std::cout Enter a number: ; std::cin number; // 读取整数 std::cout Hello, name ! Your number is number std::endl; return 0; }ps1. cin 读取字符串时以空格为分隔符。要读取整行包括空格需使用std::getline(std::cin, str)。2. 无论是cout还是cin都能自动识别类型但可能会失去精度 → 使用C语言的printf自动识别类型 → 要点缓冲时间 ----- ∴C再这方面时间↑3. 缺省参数 (Default Arguments)C 允许在函数声明/定义时为参数设置默认值。当我们调用函数不传实参时函数会自动使用预先设置的默认值如果传入实参则会覆盖默认值使用我们传入的值。3.1 基本用法int Func(int a 10, int b 20, int c 30) { return (a b - c); } int main() { // 只传前两个参数第三个使用默认值 30 int a Func(30, 10); cout a endl; // 30 10 - 30 10 return 0; }3.2 规则与注意事项传参从左到右依次传。缺省参数必须从右向左连续设置不允许断层。即如果一个参数有缺省值其右边的所有参数也必须都有缺省值。通常在函数声明中指定而非定义即声明给定义不给声明和定义不能同时给除非函数定义也是声明。缺省值可以是常量、全局变量或函数调用在编译时能确定结果的。// 正确从右向左连续缺省 void func1(int a, int b 5, int c 10); // void func2(int a 1, int b, int c 10); // 错误a有缺省但b没有 // 带多个缺省参数的函数 void drawRect(int width, int height, std::string color black, bool filled false); // 调用示例 // drawRect(100, 200); // 使用默认 color 和 filled // drawRect(100, 200, blue); // 指定 color使用默认 filled // drawRect(100, 200, red, true); // 指定所有参数4. 函数重载 (Function Overloading)函数重载允许在同一作用域内定义多个同名函数只要它们的参数列表参数的类型、个数或顺序不同。编译器根据调用时提供的实参来决定调用哪个函数。4.1 函数重载的定义在同一个作用域内定义函数名相同、形参列表不同的多个函数叫做函数重载。形参不同参数个数不同、参数类型不同、参数类型顺序不同。⚠返回值类型不能用来区分重载。void f() { cout f()endl; } void f(int a) { cout f(int a)endl; }上面两个函数参数个数不一样构成重载。4.2重点返回值不参与重载int Func(); double Func(); int main() { Func(); //二义性编译器无法单凭调用语句判断想要哪个返回值 return 0; }函数调用的时候编译器只看传入的实参不会看返回值。仅仅返回值不同、参数列表完全相同不能构成重载直接编译报错。4.3 注意重载和缺省参数一起使用容易产生调用歧义void f() { cout f()endl; } void f(int a0) { cout f(int a)endl; } int main() { f(); //编译报错不明确调用哪个函数二义性 return 0; }虽然两个函数满足重载的语法条件但是无参调用时两个函数都可以匹配编译器不知道选谁报调用歧义。4.4 重载与否C/C【核心函数名修饰规则】C 语言编译后函数名不做修饰只保留原始函数名。同名函数链接时直接报重定义错误所以 C 无法实现重载。C 语言编译阶段执行函数名修饰Name Mangling。编译器根据函数名、参数个数、参数类型生成一个独一无二的新符号名存到符号表。同名但参数不同的函数修饰之后得到不同的名字链接的时候不会冲突以此实现函数重载。补充g 和 Windows MSVC 的修饰规则不一样但原理一致。【ps关于C语言不支持函数重载原因可单独开一篇进一步了解。】核心小结重载条件同作用域、函数名相同、参数列表不同返回值不作为重载判断依据。重载 缺省参数极易产生调用二义性编码要避开。底层原理C 无名字修饰不能重载C 通过函数名修饰给不同版本生成不同符号名实现重载。引用部分放在下讲