导图社区 C++(C语言重制版)(第一版)
这是一个关于C加加(C语言重制版)(第一版)的思维导图,全面且系统地梳理了C++相关的多个关键领域,有助于系统地学习和掌握C++语言的各个方面。
编辑于2025-09-08 14:47:58C++
问题
优点
缺点
常识
基本常识
C++的发展历史
C++98
1998年发布(ISOAEC 14882:1998),它不仅描述了当年已有的C++特性,还对该语言进行了扩展, 添加了异常、运行阶段类型识别(RTTI)、模板和标准模板库(STL)。
C++03
2003年发布(ISOAEC 14882:2003),对C++98进行了整理和修订错误,但没有新增特性,故使用C++98来表示C++98/C++2003。
C++11
2011年发布(ISOAEC 14882:2011)。
标识符命名规则
可以用小写字母、大写字母、数字和下划线_来命名。 而且名称的第1个字符必须是字母或下划线,不能是数字。
不可与关键字重名
C++的名称区分大小写,即把同一个字母的大小和小写区分为两个不同的字符❤️
同一条声明可声明多个变量
等价
int feet,fathoms;
int feet; int fathoms;
函数传参顺序:从右到左
对C/C++来说,真为非0假为0
单撇号'内为单个字符',双撇号"内为多个字符(即字符串)。
所有的语句都以分号结束
标准规定,每一个C/C++程序都必须包含一个main函数,程序在执行时总是从main开始往下执行的,编译器在链接阶段会寻找main函数,如果找不到则会报错。
main函数中为什么要有一个return语句
`main` 函数的返回值(通常称为 退出状态码)用于告知操作系统程序是否正常结束。 返回 `0`:表示程序成功执行(`EXIT_SUCCESS`)。 返回非零值(如 `1`):表示程序异常终止(`EXIT_FAILURE`),不同非零值可表示不同错误类型。 操作系统依赖:父进程(如Shell脚本)可以通过检查此状态码决定后续操作(例如是否重试、记录错误等)。
ANSI/ISOC++标准规定:如果 `main` 函数执行到末尾没有 `return`,编译器会隐式返回 `0`,视为成功退出。 注:此规则仅适用于 `main` 函数,其他函数不返回仍会导致编译警告/错误。
C++可以使用所有的C语言函数,只要包含对应的头文件就行。
名词常识
头文件(包含文件)
如果想让几个源文件可以访问相同的信息,可以把此信息放入一个文件中,然后利用#include指令把该文件的内容带进每个源文件中。 把按照此种方式包含的文件称为头文件(有时称为包含文件)。
C语言的传统是,头文件使用扩展名 h,将其作为一种通过名称标识文件类型的简单方式。 但C++的用法变了。现在对老式C的头文件保留了扩展名h(C++程序仍可以使用这种文件),而C++头文件则没有扩展名。 有些C头文件被转换为C++头文件,这些文件被重新命名,去掉了扩展名h,并在文件名称前面加上前缀c(表明来自C语言)。例如,C++版本的math.h为cmath。 有时C头文件的C版本和C++版本相同,而有时候新版本做了一些修改。
库文件
库文件是计算机上的一类文件,可以简单的把库文件看成一种代码仓库, 它提供给使用者一些可以直接拿来用的变量、函数或类。
库是特殊的一种程序,编写库的程序和编写一般的程序区别不大, 只是库不能单独运行。
静态库
静态库在程序的链接阶段被复制到了程序中
命名规则:
Linux:libxxx.a
Windows:libxxx.lib
Linux下静态库的制作:
gcc获得.o文件 gcc -c a.c b.c
将.o文件打包,使用ar工具(archive) ar rcs libxxx.a a.o b.o
优点:
静态库被打包到应用程序中加载速度快。
发布程序无需提供静态库,移植方便。
缺点:
消耗系统资源,浪费内存。
更新、部署、发布麻烦。
动态库(共享库)
动态库在链接阶段没有被复制到程序中,而是程序在运行时由系统动态加载到内存中供程序调用。
命名规则:
Linux:libxxx.so
Windows:libxxx.dll
Linux下动态库的制作:
gcc获得.o文件,得到和位置无关的代码 gcc -c -fpic/-fPIC a.c b.c
gcc得到动态库 gcc -shared a.o b.o -o libxxx.so
优点:
可以实现进程间资源共享(共享库)。
更新、部署、发布简单。
可以控制何时加载动态库。
缺点:
加载速度比静态库慢。
发布程序时需要提供依赖的动态库。
静态库、动态库区别来自链接阶段如何处理,链接成可执行程序。分别称为静态链接方式和动态链接方式。
库的好处:1. 代码保密;2. 方便部署和分发。
对象
类似于变量,不过变量是由数据类型定义的,而对象是由类定义的。
内置
编译器自带的类型
声明
引用性声明(声明) (referncing declaration)
不需要建立存储空间的。
告知编译器变量已被创建。
具体查看关键字extern
定义性声明(定义) (defining declaration)
需要建立存储空间的。
创建一个变量
初始化
创建一个变量并为其赋值
符合语句(分程序)(代码块)(语句块)(程序块):在函数中用大括号把许多语句和声明组合到一起,形成单条语句。
语句块与简单的语句不同,语句块不用分号当作结尾。
位操作
保留
原样不动
清零
全部为0
置位
全部为1
取反
1变0,0变1
一元运算需要两个元素:一元(单目)运算符以及该运算符作用的一个变量。
二元运算需要三个元素:二元运算符以及该运算符作用的两个变量。
EOF意思是end of file,本质上是-1。
程序的错误被称为bug
找出并修正程序的错误的过程叫做调试
语句:一条完整的指令
步长:地址属性。做加1操作需移动的字节数
位宽:所占的内存大小,实质为有多少个二进制数字。
符号常量
使用字符代指具体的常量
赋值
例int a = 5
a就是符号常量
明示常量(宏)
使用#define定义的符号常量。
指针常量:char * const arr; 指针指向的值可以改变,指针的指向不可以改变。
常量指针:char const *arr; 指针的指向可以改变,指针指向的值不可以改变。
指针:变量地址的别称。
数据溢出:溢出的本质是数据二进制的长度超出了对应数据类型的内存大小。
程序会自动将超出内存大小的二进制值抹去。
大于最大值,叫做向上溢出(overflow);小于最小值,叫做向下溢出(underflow)。
嵌套循环:一个循环体内又包含另一个完整的循环结构。
内嵌的循环中还可以嵌套循环,这就是多层循环。
各种语言中关于循环的嵌套的概念都是一样的。
嵌套调用:在调用函数时,被调用函数体内还可以再调用其他函数,甚至是被调用函数本身。
注:被调用函数在执行过程中再次调用被调用函数本身,这被称为直接递归。
注:被调用函数在执行过程中遇到一个调用其他函数的语句,而在执行被被调用函数时又调用被调用函数,这被称为间接递归。
缓冲区
缓冲区(Buffer)又称为缓存(Cache),是内存空间的一部分。 计算机在内存中预留了一定的存储空间,用来暂时保存输入或输出的数据,这部分预留的空间就叫做缓冲区(缓存)。
有时候,从键盘输入的内容,或者将要输出到显示器上的内容,会暂时进入缓冲区, 待时机成熟,再一股脑将缓冲区中的所有内容“倒出”,我们才能看到变量的值被刷新,或者屏幕产生变化。
作用
缓冲区是为了让低速的输入输出设备和高速的用户程序能够协调工作,并降低输入输出设备的读写次数。
例如,我们都知道硬盘的速度要远低于 CPU,它们之间有好几个数量级的差距,当向硬盘写入数据时,程序需要等待,就好像卡顿了一样,用户体验非常差。 计算机上绝大多数应用程序都需要和硬件打交道,例如读写硬盘、向显示器输出、从键盘输入等,如果每个程序都等待硬件,那么整台计算机也将变得卡顿。
但是有了缓冲区,就可以将数据先放入缓冲区中(内存的读写速度也远高于硬盘),然后程序可以继续往下执行, 等所有的数据都准备好了,再将缓冲区中的所有数据一次性地写入硬盘,这样程序就减少了等待的次数,变得流畅起来。
缓冲区的另外一个好处是可以减少硬件设备的读写次数。其实我们的程序并不能直接读写硬件, 它必须告诉操作系统,让操作系统内核(Kernel)去调用驱动程序,只有驱动程序才能真正的操作硬件。
从用户程序到硬件设备要经过好几层的转换,每一层的转换都有时间和空间的开销,而且开销不一定小; 一旦用户程序需要密集的输入输出操作,这种开销将变得非常大,会成为制约程序性能的瓶颈。
这个时候,分配缓冲区就是必不可少的。每次调用读写函数,先将数据放入缓冲区,等数据都准备好了再进行真正的读写操作, 这就大大减少了转换的次数。实践证明,合理的缓冲区设置能成倍提高程序性能。
缓冲区其实就是一块内存空间,它用在硬件设备和用户程序之间,用来缓存数据,目的是让快速的 CPU 不必等待慢速的输入输出设备,同时减少操作硬件的次数。
根据不同的标准,缓冲区可以有不同的分类。
按照缓冲区对应的是输入设备还是输出设备分类
输入缓冲区
输出缓冲区
按照数据刷新(也可以称为清空缓冲区,就是将缓冲区中的数据“倒出”)的时机分类
全缓冲
当缓冲区被填满以后才进行真正的输入输出操作。 缓冲区的大小都有限制的,比如 1KB、4MB 等,数据量达到最大值时就清空缓冲区。
全缓冲的典型代表是对硬盘文件的读写:在实际开发中,将数据写入文件后,打开文件并不能立即看到内容, 只有清空缓冲区,或者关闭文件,或者关闭程序后,才能在文件中看到内容。这种现象,就是缓冲区在作怪。
行缓冲
当在输入或者输出的过程中遇到换行符时,才执行真正的输入输出操作。
行缓冲的典型代表就是标准输入设备(也即键盘)和标准输出设备(也即显示器)。
不带缓冲
不带缓冲区,数据就没有地方缓存,必须立即进行输入输出。
条件编译
条件编译的主要目的是在编译时根据特定的条件选择性地包含或排除代码段。 通过使用预处理器指令,如 #ifdef、#ifndef、#if、#else 和 #endif,可以实现这种有条件的编译操作。
函数类型是指函数返回值的类型
函数类型是指针的函数就是指针型函数
函数头
返回值的数据类型 函数名 (形式参数列表)
函数体
在函数头之下由最外层的{}括起来的部分
子函数:一个文件中除了main主函数之外的其他程序员自定义函数。
程序常识
C语言编程原理
C编程的基本策略是,用程序把源代码文件转换为可执行文件(其中包含可直接运行的机器语言代码)。 典型的C实现通过编译和链接两个步骤来完成这一过程。编译器把源代码转换成中间代码,链接器把中间代码和其他代码合并,生成可执行文件。 C使用这种分而治之的方法方便对程序进行模块化,可以独立编译单独的模块,稍后再用链接器合并已编译的模块。通过这种方式,如果只更改某个模块,不必因此重新编译其他模块。另外,链接器还将你编写的程序和预编译的库代码合并。 中间文件有多种形式。我们在这里描述的是最普遍的一种形式,即把源代码转换为机器语言代码,并把结果放在目标代码文件(或简称目标文件)中(这里假设源代码只有一个文件)。 虽然目标文件中包含机器语言代码,但是并不能直接运行该文件。因为目标文件中存储的是编译器翻译的源代码,这还不是一个完整的程序。 目标代码文件缺失启动代码(startup code)。
启动代码充当着程序和操作系统之间的接口。 例如,可以在MS Windows或Linux系统下运行IBMPC兼容机。这两种情况所使用的硬件相同,所以目标代码相同, 但是 Windows和Linux 所需的启动代码不同,因为这些系统处理程序的方式不同。 目标代码还缺少库函数。几乎所有的C程序都要使用C标准库中的函数,例如,concrete.c中就使用了printf()函数。 目标代码文件并不包含该函数的代码,它只包含了使用printf()函数的指令。printf()函数真正的代码存储在另一个被称为库的文件中。 库文件中有许多函数的目标代码。链接器的作用是,把你编写的目标代码、系统的标准启动代码和库代码这3部分合并成一个文件,即可执行文件。 对于库代码,链接器只会把程序中要用到的库函数代码提取出来(见图1.4)。
简而言之,文件和可执行文件都由机器语言指令组成的, 但目标文件中只包含编译器为你编写的代码翻译的机器语言代码, 可执行文件中还包含你编写的程序中使用的库函数和启动代码的机器代码。 在有些系统中,必须分别运行编译程序和链接程序, 而在另一些系统中,编译器会自动启动链接器,用户只需给出编译命令。
C Primer Plus图1-4
一个C语言程序由一个或多个源程序文件(程序模块)组成
一个源程序文件由以下三个部分组成
预处理指令
C编译系统在对源程序进行“翻译”以前,先由一个预处理器(也称预处理程序、预编译器)对预处理指令进行预处理。
例:#include <stdio.h>
#include<stdio.h>的作用相当于把stdio.h文件中的所有内容都输入该行所在的位置。 实际上,这是一种“拷贝-粘贴”的操作。include 文件提供了一种方便的途径共享许多程序共有的信息。
由预处理得到的结果与程序其他部分一起,组成一个完整的、可以用来编译的最后的源程序,然后由编译程序对该源程序正式进行编译,才得到目标程序。
全局变量
在函数之外进行的数据声明。
函数定义
函数是C程序的主要组成部分,是C程序的基本单位。程序的几乎全部工作都是由各个函数分别完成的, 在程序中,每个函数都用来实现一个或几个特定的功能。编写C程序的工作主要就是编写一个个函数。
一个C语言程序是由一个或多个函数组成的,而函数又可以大概分为三类。
主函数
main函数,每个程序都有且只有一个,每次程序运行时都从main函数开始运行。
子函数
在程序中除main函数之外程序员定义的其他函数。
系统函数
在库文件中已经定义好的函数,用#include指令包含一下头文件就能直接调用。
一个函数包括两个部分。
函数首部。即函数的第1行,包括函数名、函数类型、函数属性、函数参数(形式数)名、参数类型。一个函数名后面必须跟一对圆括号,括号内写函数的参数名及其类型。 如果函数没有参数,可以在括号中写void,也可以什么都不写。
函数体。即函数首部下面的花括号内的部分。 如果在一个函数中包括有多层花括号,则最外层的一对花括号是函数体的范围。
声明部分:定义在本函数中所用到的变量。
执行部分。由若干个语句组成,指定在函数中所进行的操作。
注:在某些情况下也可以没有声明部分,甚至可以既无声明部分也无执行部分。
注:一个源程序文件为一个编译单位,在程序编译时是以源程序文件为单位进行编译的。
main()被启动代码调用,而启动代码由编译器添加到程序中的,是程序和操作系统之间的桥梁。
注意事项
限定符
控制符
关键字
预处理
名称空间
变量
对象
一种根据类标准而创建的实体
cout
由ostream类定义创建的对象,在iostream中定义。
将数据插入到输出流中
格式
cout << 参数
cin
由istream类定义创建的对象,在iostream中定义
从输入流中读取数据
格式
cin >> 参数
数据类型
运算符(操作符)
转义字符
函数
语句
链表
文件
0-1-1_1
0-1-1_2
0-1-1_3
0-1-2
0-1-3
0-1-4
0-1-5
0-2-1
1-4
0-0-1
0-0-2
0-0-3