福田网站建设费用,wordpress 文章型,做别墅装修的公司,1g做网站空间相关代码gitee自取#xff1a;
C语言学习日记: 加油努力 (gitee.com)
接上期#xff1a;
【数据结构初阶】十一、归并排序(比较排序)的讲解和实现 #xff08;递归版本 非递归版本 -- C语言实现#xff09;-CSDN博客 引言#xff1a;什么是C C语言是结构化和模块化的…
相关代码gitee自取
C语言学习日记: 加油努力 (gitee.com) 接上期
【数据结构初阶】十一、归并排序(比较排序)的讲解和实现 递归版本 非递归版本 -- C语言实现-CSDN博客 引言什么是C C语言是结构化和模块化的语言适合处理较小规模的程序。对于复杂的问题规模较大的程序需要高度的抽象和建模时C语言就不合适了。 为了解决软件危机20世纪80年代计算机界提出了OOPobject oriented programming面向对象思想支持面向对象的程序设计语言应运而生 1982年Bjarne Stroustrup博士在C语言的基础上引入并扩充了面向对象的概念发明了一种新的程序语言为了表达该语言与C语言的渊源关系命名为C 因此C是基于C语言而产生的 它即可以进行C语言的过程化程序设计又可以进行以抽象数据类型为特点的基于对象的程序设计还可以进行面向对象的程序设计 C是在C语言的基础之上容纳了面向对象编程思想 并增加了许多有用的库以及编程范式等。 熟悉C语言的话对C的学习有一定的帮助 本篇博客主要目标 1、补充C语言语法的不足以及C是如何对C语言设计不合理的地方进行优化的 如作用域方面、IO方面、函数方面、指针方面、宏方面等 2、为后续了解类和对象打下基础 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 引言C的发展史 1979年贝尔实验室的本贾尼等人试图分析unix内核的时候试图将内核模块化于是在C语言的基础上进行扩展增加了类的机制完成了一个可以运行的预处理程序称之为 C with classes C语言也进行着逐步递进、由浅入深的过程 C的历史版本 阶段内容C with classes 类及派生类、公有和私有成员、类的构造和析构、友元、内联函数、赋值运算符 重载等 C1.0添加虚函数概念函数和运算符重载引用、常量等C2.0更加完善支持面向对象新增保护成员、多重继承、对象的初始化、抽象类、静态成员以及const成员函数C3.0进一步完善引入模板解决多重继承产生的二义性问题和相应构造和析构的处理C98C标准第一个版本绝大多数编译器都支持得到了国际标准化组织ISO和美国标准化协会认可以模板方式重写C标准库引入了STL标准模板库C03C标准第二个版本语言特性无大改变主要修订错误、减少多异性C05 C标准委员会发布了一份计数报告Technical Report -- TR1正式更名为C0x 即计划在本世纪第一个10年的某个时间发布 C11增加了许多特性使得C更像一种新语言 如正则表达式、基于范围for循环、auto关键字、新容器、列表初始化、标准线程库等C14对C11的扩展主要是修复C11中的漏洞以及改进 如泛型的lambda表达式auto的返回值类型推导二进制字面常量等C17在C11上做了一些小幅改进增加了19个新特性 如static_assert()的文本信息可选Fold表达式用于可变的模板 if 和 switch 语句中的初始化器等C20自C11以来最大的发行版引入了许多新的特性 如模块Modules、协程Coroutines、范围Ranges、概念Constraints 等重大特性 还有对已有特性的更新如Lambda支持模板、范围for循环支持初始化等C23制定中…… ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 一、C关键字C98版本 C总计63个关键字包含C语言的32个关键字 该C版本关键字表格 asmdoifreturntrycontinueautodoubleinlineshorttypedefforbooldynamic_castintsignedtypeidpublicbreakelselongsizeoftypenamethrowcaseenummutablestaticunionwchar_tcatchexplicitnamespacestatic_castunsigneddefaultcharexportnewstructusingfriendclassexternoperatorswitchvirtualregisterconstfalseprivatetemplatevoidtrueconst_castfloatprotectedthisvolatilewhiledeletegotoreinterpret_cast 红色关键字为之前C语言博客有提到或使用过的关键字 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 二、namespace -- 命名空间关键字 1. 命名空间的作用 在C/C中变量、函数和后面要了解的类都是大量存在的 这些变量、函数和类的名称都将存在于全局作用域中可能会导致很多冲突。 使用命名空间的目的是对标识符的名称进行本地化以避免命名冲突或名字污染namespace关键字的出现就是针对这种问题的 示例 2. 命名空间的定义 定义命名空间需要使用到namespace关键字 关键字后接命名空间的名称然后再加上一对大括号{}即可 大括号{}中内容为命名空间的成员 在一般开发中会使用项目的名称作为命名空间的名称 一般命名空间的定义 命名空间嵌套子命名空间 存在多个相同名称的命名空间 同一个工程中允许存在多个相同名称的命名空间编译器最后会合并成同一个命名空间 一个工程中的 test.h头文件 和 text.cppC文件中两个同名命名空间会被合并成一个 3. 命名空间的使用 定义一个命名空间就定义了一个新的作用域 命名空间中的所有内容都局限于该命名空间中 所以不能通过直接调用来使用命名空间中的内容 命名空间的使用有三种方式 方式一 加命名空间名称和作用域限定符 作用域限定符两个冒号 -- : : 方式二 using namespace 命名空间名称 展开命名空间 使用 using namespace 命名空间名称 可以展开对应的命名空间展开后可以直接通过该命名空间中成员的名称使用该成员 但是使用该方式对命名空间的展开会导致命名空间的所有内容暴露出来可能又会导致命名冲突问题 所以一般在自己使用时为了方便才会使用该方式 如果是项目工程该方式一定要慎重使用可能会出大问题的 方式三 使用using关键字只展开命名空间中的某个成员 通过方式二直接展开命名空间会有命名冲突的风险 那么我们可以通过using 命名空间名称::指定成员 来指定只展开命名空间中的某个成员 这种方式是比较常用的 通常是对一些常用的成员对象进行使用 来避免频繁使用方式一调用命名空间减轻代码冗余 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 三、C输出输入 C中有了新的输入和输出方法虽然说有了新的输入输出方法 但之前C语言中的输入和输出方法也是可以用的 在了解C的输入和输出方法前需要先了解以下概念 std -- C标准库命名空间 std是C标准库的命名空间名C将标准库的定义实现都放在这个命名空间中上一标题我们知道了使用命名空间的三种方式而使用std命名空间的方式要考虑实际情况 std命名空间的使用惯例 在自己的日常练习中建议直接方式二即可 即using namespace std 这样使用std命名空间就很方便了不用频繁使用方式一进行操作 而在项目工程中using namespace std 展开方式二展开标准库就全部暴露出来了如果我们定义了跟库中重名的 类型/对象/函数 就会存在命名冲突问题。该问题在日常练习中很少出现 但是项目开发中代码较多、规模大就很容易出现。 所以建议在项目开发中使用方式三指定展开 即using std::成员(对象)名 来指定展开std命名空间中常用的几个库对象/类型/成员 像是C输出时使用的cout输入时使用的cin cout 和 cin cout -- console(控制台) out -- 标准输出对象(控制台) -- 流插入cin -- console(控制台) in -- 标准输入对象(键盘) -- 流提取 使用cout标准输出对象和cin标准输入对象时需要包含 iostream 头文件IO流头文件以及 按命名空间使用方法使用std 注C iostream头文件 不需要像C语言一样加“.h”后缀 早期标准库将所有功能在全局域中实现声明在“.h”后缀的头文件中使用时只需包含对应头文件即可后来将其标准库所有功能实现在std命名空间下为了和C语言头文件区分也为了正确使用命名空间规定C头文件不加“.h”后缀。 旧编译器vc 6.0中还支持 iostream.h 格式后续编译器已不支持 因此推荐使用 iostream std命名空间 的方式 cout 和 cin 是全局的流对象endlendline是特殊的C符号表示换行输出他们都包含在 iostream头文件 中 使用cout进行输出时还需要用到 -- 流插入运算符 使用cin进行输入时还需要用到 -- 流提取运算符 在C语言中 和 是位于算符在C中又多了以上身份 使用C输入和输出相对C语言更方便不需要像 scanf / printf 输入输出时要手动控制格式%d、%p……C的输入和输出可以自动识别变量类型 实际上 cout 和 cin 分别是 ostream 和 istream 类型的对象 和 也涉及运算符重载等知识这里只是简单了解并使用关于cout和cin还有很多更复杂的用法 比如控制浮点数输出精度控制整型输出进制格式等但并不常用实在需要使用时可以用C语言来操作C兼容C语言的操作 示例 对应代码 //包含IO流头文件
#include iostream//指定展开命名空间成员
using std::cout; //指定展开标准输出对象控制台
using std::cin; //指定展开标准输入对象键盘
using std::endl; //指定展开C换行符号int main()
{int a 10; //整型变量double b 3.14; //浮点型变量cout 使用cout打印当前a和b endl;//使用cout进行输出cout a endl b endl;/** 通过cout标准输出对象和流插入运算符进行输出打印* * 先将a这个变量流进std::cout这个控制台中打印* 再进行endl换行再将b这个变量* 流进std::cout这个控制台中打印再换行。* * 即使 a变量 和 b变量 的类型不同也能打印* C的输入和输出可以自动识别变量类型*/cout 使用cin分别输入数据到a和b endl;//使用cin进行输入cin a b;/** 通过cin标准输入对象和流提取运算符对数据进行输入** 让你在控制台上输入的数据分别流入a和b这两个变量中** 即使 a变量 和 b变量 的类型不同也能输入* C的输入和输出可以自动识别变量类型*/cout 输入后再使用cout进行输出打印 endl;cout a endl b endl;return 0;
} ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 四、缺省参数 1. 缺省函数的概念 缺省参数是在声明或定义函数时为函数的参数指定一个缺省值。 在调用该函数时如果没有指定实参则采用该形参的缺省值否则使用指定的实参 有了缺省参数后可以调整参数的各种形式来调用该函数 缺省值必须是常量或者全局变量 C语言不支持缺省参数编译器不支持 缺省参数不能在函数声明和定义中同时出现 2. 缺省函数的分类 全缺省参数 函数的所有参数都设置对应的缺省参数 图示 半缺省参数 只对函数的部分参数设置对应的缺省参数 半缺省参数必须从右往左依次来给出不能间隔着给传参“从左往右”传半缺省参数“从右往左”给 图示 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 五、函数重载 自然语言中一个词可以有多重含义 人们可以通过上下文来判断该词真实的含义即该词被重载了 函数重载的概念 函数重载是函数的一种特殊情况C允许在同一作用域中声明几个功能类似的同名函数这些同名函数的形参列表参数个数、参数类型、类型顺序不同函数重载常被用来处理实现功能类似但数据类型不同的问题 图示 难C支持函数重载的原理 -- 名字修饰name Mangling 为什么C语言不支持函数重载而C支持函数重载 要解决这个问题需要先了解一下编译链接的过程往期相关博客学C的第三十四天【程序环境和预处理】_高高的胖子的博客-CSDN博客 简单解释编译链接过程 假设我们现在有三个文件 Func.h Func.cpp Test,cpp 函数声明 函数实现 主函数调用 执行主函数时需要进行以下过程 预处理 编译 汇编 链接 现有文件和其内容 预处理 预处理过程操作包括头文件展开(主要) / 宏替换 / 条件编译 / 去除注释 在 Fun.cpp文件 和 Test.cpp文件 中因为都包含了 Func.h头文件 所以在预处理时会对头文件进行展开之后会生成预处理文件Func.i文件 和 Test.i文件 所以在 Func.i文件 中就会有Func函数的声明和实现Func.i函数声明和定义 而 Test.i文件 中会有被调用的函数的声明和调用Test.i函数的声明和实际调用 编译 编译过程操作包括检查语法是否错误 / 生成汇编代码 进行编译时会生成汇编代码文件.s文件 即 Func.s文件 和 Test.s文件分别由 Func.i文件 和 Test.i文件 生成 Func.s文件 中存放了两个重载函数对应的汇编代码 而 Test.s文件 中则存放了主函数main函数的汇编代码 包括被调用的两个重载函数的汇编代码 而要调用这两个重载函数还需要用到汇编语言中的 call指令 来获取函数的地址。 但在编译阶段因为 Test.i文件 中只包含了 Func.h头文件只有函数声明没有函数实现 所以 call指令 还无法获得对应的函数地址。 在这种情况下编译器会判断调用的函数和头文件中函数是否匹配如果匹配的话 即使 call指令 还没找到函数地址也可以先让其通过编译方便实现多文件项目 汇编 汇编过程操作包括将汇编代码文件中的代码转换为二进制的机器码 二级制的机器码CPU能“读懂”的代码 汇编后会生成目标文件.o文件 即 Func.o文件 和 Test,o文件分别由 Func.s文件 和 Test.s文件 生成 两个文件都将汇编代码转换成了对应的二进制机器码 链接 链接过程操作包括将目标文件链接合并到一起链接一些没有确定函数地址等等 将汇编操作中的 Func.o文件 和 Test.o文件 合并为 a.out文件默认情况下 合并后的 a,out文件 中 之前在编译过程中call指令未找到的函数地址可以在合并后的 a.out文件 中找到 因为合并前的 Func.o文件 就包含对应函数的实现 --------------------------------------------------------------------------------------------- C支持重载函数而C语言不支持的原因 通过上面对编译链接过程的简单了解我们可以知道 在编译过程中 call指令 还未找到被调用函数的地址直到链接过程合并文件后才能够找到 C语言不支持重载函数的原因 在C语言中没有重载函数即函数名唯一的情况下 要找函数地址只需要通过唯一的函数名即可找到 即在链接过程中通过唯一的函数名在 Func.o目标文件 中的符号表进行对地址的查找 所以如果C语言中有重载函数函数名不唯一的情况下就无法在链接过程中找到函数地址 因此C语言无法支持重载函数 C支持重载函数的原因 -- 名字修饰name Mangling 注不同编译器实现方式不同这里以Linux中的g为例 在C中有重载函数的情况下即函数名不唯一的情况下 C有一个函数可以通过函数名和参数情况修饰出一个新的函数名字 函数名相同但参数情况不同就能修饰出不同的函数名字 再通过修饰出的函数名字来查找对应的函数地址。 修饰名字构成方式_Z 函数名字符个数 函数名 各参数首字母 假设有一个函数Func(int a, double b) 修饰后的函数名字为_Z4Funcid 假设有另一个函数Func(double b, int a) 修饰后的函数名字为_Z4Funcdi 所以即使函数名相同也可以通过参数情况来创建出不同的函数名字