C语言简略总结

一、数据
1、基本类型
(1)、整数家族(signed、unsigned)
整型(4个字节)
有符号范围:-232~232-1
无符号范围:0~2^32-1
整型提升:有符号数补充对齐符号位、无符号数补零
长整型(>=4字节):有符号范围:-232~232-1
无符号范围:0~2^32-1
短整型(2个字节):有符号数范围:-32768-32767
无符号范围:0-2^15-1
双长整型(8个字节):有符号范围:-263~263-1
无符号范围:0-2^63-1
字符型(1个字节):有符号范围:-128-127
无符号范围:0-255
布尔型(bool)

(2)、浮点类型
单精度浮点型(4个字节、6位有效数字)
数值范围:0以及1.210-38~1.7*1038
双精度浮点型(8个字节、15位有效数字)
数值范围:0以及2.3
10-308~1.7*10308
长双精度型(8个字节、15位有效数字)
数值范围:0以及2.310-308~1.7*10308
复数浮点型
浮点型注意:
需要注意在内存中存储方式不同
需要注意精度丢失问题
浮点数判零、设置精度范围
浮点数不能进行位运算
2、枚举类型
枚举类型取值:
枚举{ }中内容为枚举类型可能取值,默认从0开始,依次递增1,也可以在定义自行赋值
优点:
增加代码可读性和可维护性
和define定义的标识符相比,枚举有类型检查更加严谨
防止了命名污染(封装)
便于调试,define不便于调试,调试时是以替换的内容
使用方便,一次可以定义多给常量
3、空类型
4、派生类型
(1)、派生类型
指针类型:char
、int*、void*…
数组类型:int arr[ ]
(2)、结构变量:
1、结构成员:可以是标量、数组、指针、甚至其他结构体,通过点操作符(.)访问
2、结构体变量的赋值:结构体变量的赋值或者初始化或者在定义后按字段赋值
3、结构体变量的比较:不能用==来进行比较,需要用memcmp( )函数来进行比较
4、结构体变量的地址可以作为参数传给函数,结构体数组名就是一个地址,可以传给函数,如果是同类型的结构体变量,可以整体赋值

(3)、结构体的位域
1、定义:位域是一个位的字段,不同长度的字段存储于一个或多个其所声明的变量中(如整型变量)
2、类型:char、short、int
3、特点:字段可以不命名,一个位域字段必须存储在其位域类型的一个单元所占空间中,不能横跨两个单元
4、优点:有些信息在存储时,并不需要占用一个完整的字节,而只需要几个或一个二进制位,如开关量;可以方便的利用位域把一个变量按位分解
5、缺点:不同系统对位域的处理可能有不同的结构,不利于程序的移植
(3)、结构体内存对齐
1、第一个成员在结构体偏移量为0的位置
2、其他成员需对齐到自身对齐数的整数倍地址处,对齐数=编译器默认值与该成员大小的较小值
3、结构体总大小为所有成员最大对齐数的整数倍
4、如果嵌套结构体,嵌套结构体对齐到自己最大对齐数整数倍处,结构体整体大小是所有最大对齐数的整数倍
(4)、结构体内存对齐原因
1、平台原因(移植原因)
2、性能原因:对齐的内存访问仅需一次
(5)、位段
1、位段的成员必须是整型、无符号整型、有符号整型、字符型
2、位段的空间按照4字节(int)或1字节(char)开辟
3、位段不跨平台,可移植程序应避免位段使用
4、位段从左向右或从右向左分配标准未定义
5两个位段,第二给无法容纳于第一个剩余的位段,剩余位段舍弃还是利用不确定
位段最大数目不确定(16位机器最大16、32位机器最大32)

(6)、结构与联合的区别
1、联合所有成员公用一块地址空间,结构不同成员的存放地址不同
2、对于联合的不同成员赋值,将会对其它成员重写,原来成员的值就不存在了,而对于结构的不同成员赋值是互不影响的
3、联合体(共用体)只能存在一个值,如果给某个值赋值后,另一个值就不存在

(7)、联合(共用体)类型
1、成员共用同一块空间
2、联合的大小至少是最大成员的大小
3、当最大成员大小不是最大对齐数整数倍时,要对齐到对齐数的整数倍
(8)、函数类型:void fun( );

二、符号
1、符号(注意优先级)
优先级(高到低):
第一级:
圆括号【( )】、下标运算符【[ ]】、分量运算符的指向结构体成员运算符 【-> 】、结构体成员运算符 【.】
第二级:
逻辑非运算符【!】、按位取反运算符【~】、自增自减运算符【++】【 --】、负号运算符【-】、类型转换运算符【(类型)】、指针运算符和取地址运算符【和&】、长度运算符【sizeof】
第三级:
乘法运算符【
】、除法运算符【/】、取余运算符【%】
第四级:
加法运算符【+】、减法运算符【-】
第五级:
左移动运算符【<<】、右移动运算符【>>】
第六级:
关系运算符【< > <= >=】
第七级:
等于运算符【==】、不等于运算符【!=】
第八级:
按位与运算符【&】
第九级:
按位异或运算符【^】
第十级:
按位或运算符【|】
第十一级:
逻辑与运算符【&&】
第十二级:
逻辑或运算符【||】
第十三级:
条件运算符【?:】
第十四级:
赋值运算符【= += -= *= /= %= >>= <<.= &= |= ^=】
第十五级:
逗号运算符【,】

2、字符常量:
(1)、普通字符,即用单撇号括起来的一个符号
(2)、转义字符,即特殊字符常量,即反斜符(\)开头的字符
3、注意要点:
(1)、字符常量在存储计算机的存储单元中时,一般是采用ASCII代码存储的
(2)、字符常量只能用单撇号括起来,不能使用单引号或其它括号
(3)、字符常量是区分大小写的
(4)、单撇号只是限界符,不属于字符常量中的一部分,字符常量只能是一个字符,不包括单撇号
(5)、单撇号里面可以是数字、字母等C语言字符集中除 ‘ 和 \ 以外所有可实现的单给字符,单数字被定义为字符之后则不能参与数值运算
标识符:
三、关键字
预定义标识符:包含系统类库名、系统常量名、系统函数名
用户标识符:用户根据需要自己定义的标识符
1、32个关键字
Int、double、long、char
Float、short、signed、unsigned
Struct、union、enum、static、void
For、do、while
If、else、goto
Switch、case、default、break、continue
Register、const、volatile、auto
Typedef、exterm、return、sizeof
2、部分关键字解析
Register:
寄存器关键字,建议将变量处理为寄存器变量,最终结果取决于编译器
Volatile:
1、强制编译器每次从内存中取得该变量的值,而不是被优化后的寄存器中读取
常用于硬件寄存器、中断服务子程序访问到的非自动变量、多线程应用中被几个任务共享的变量
2、多线程应用被几个任务共享的变量

Static:
1、修饰存储类型使之成为静态存储类型(定义为静态局部变量,该变量存在静态区,故程序结束时,该变量不会被销毁)
2、修饰链接属性使之成为内部链接属性(静态函数只能在声明它的源文件中使用)
(在模块内,但函数外:可被模块内所有函数访问,但不能被模块外函数访问,属于全局变量
在模块内,且函数内:只能在声明它的模块的本地范围内使用)
3、static定义局部变量后:改变了它的存储方式,即改变了它的生存期
4、static定义全局变量后:改变了它的作用域,限制了它的使用范围(变窄了)
5、static定义的变量/函数等:只初始化一次
Const:
1、可修饰数组、一般变量、指针、函数参数、函数返回值
2、编译器通常不为const只读变量分配操作空间而是将其存在符号表内,效率也高
3、声明常变量,使得指定的变量不能被修改
4、修饰函数形参,使形参在函数内不能被修改,表示输入参数
5、修饰函数返回值,使函数的返回值不能被修改
6、const成员只能在构造函数的初始化列表中初始化
7、类的const成员函数不能修改类的成员变量,而且一个const类对象只能调用其const成员函数,不能调用非const成员函数
8、重载现象:const成员函数与同名、同返回值、同参数列表的非const成员函数属于重载现象

Const与#define相比的优点:
1、const可以定义常量、修饰函数参数、修饰函数返回值,被const修饰的东西都受到了强制保护,可以预防意外的变动,能提高程序的健壮性
2、const常量有数据类型,而宏常量没有数据类型。编译器可以对前者进行类型安全检查,而对后者只有进行字符替换,没有类型安全检查,并且在字符替换可能会产生意想不到的错误
3、调试工具可以对const常量进行调试,但不能对宏常量进行调试
修饰指针
const int *p p可变,*p不可变
int const *p p可变,*p不可变
int *const p p不可变,*p可变
const int *const p p不可变,*不可变

Extern作用:
1、用于修饰变量或函数,表明该变量或函数都是在别的文件中定义的,提示编译器在其他文件中寻找
2、Extern “C”的作用结束为了能够正确实现C++代码调用其他C语言代码,加上extern “C”后,会指示编译器这部分代码按C语言的编译方式进行编译,而不是C++的

四、语句
1、分支语句
If:
else与之最近邻的if相匹配
Switch:
每一个case后的break语句不可省,最后一个分支default语句不能省
每个case具有唯一值switch(整型表达式){case:整型常量表达式…}
Goto:
在需要跳转的语句前加标签,执行时goto:标签(危险的语句,尽量避免使用)
2、循环语句
For:
注意边界问题、控制变量尽量采用“半开半闭区间”、不可在循环体内部修饰变量
While:
嵌套循环、只桃出内层循环
do while :
与while语句相同但会比while语句多执行一次

五、数组
1、一维数组:
数组名表示数组首元素地址,当数组名作为sizeof或&的操作数时,数组名表示整个数组
2、二维数组:
1、相当于一维数组,区别在于其第一维元素其实是一个数组,数组名表表示整个数组,当数组名作为sizeof或&的操作数时,数组名表示数组的第一行
2、定义一个二维数组,可以省略第一维的定义,当不能省略第二维的定义,系统根据初始化的数据个数和第二维的长度可以确定第一维的长度
3、柔性数组
(1)、特点
结构体中柔性数组成员前必须至少一个其他成员
sizeof返回这种结构的大小不包括柔性数组的内存包含柔性数组的结构用mallor c( )函数分配内存时,分配内存应大于结构大小
(2)、优点
方便释放内存
减少内存碎片,提高访问速度
4、字符型数组:
1、在定义字符型数组时,可以直接用字符串对数组赋初值,只要数组定义的大小能容纳字符串中的字符即可
2、对定义的字符型指针变量可以将字符串常量对其赋值,单数组名是地址常量,是不能用字符串对其赋值的

5、指针数组
(1)、&a[i][j] 数组元素a[i][j]的地址
(2)、&a[i] 数组元素a[i][0]的地址
(3)、a[0]+j 数组元素a[0][j]的地址
(4)、((a+i)+j)数组元素a[i][j]的地址
(5)、*(a[i]+j) 数组元素a[i][j]的地址

6、指针数组:
int *arr[ ]整型指针数组
char *arr[ ]字符型指针数组

7、函数指针数组:
int (p[ ] )(int, int) p先和[ ] 结合,数组内容是int()(int, int)的函数指针

六、指针
对于指针变量来说,值为NULL就是值为0的意思,不指向任何的地址信息
1、一级指针:
指向一个地址,解引用得到存在该地址内的值
2、二级指针:
charpp二级指针解引用为以及指针*(char)pp=p
3、指针运算:
++优先级高于指针减指针=指针间元素个数(两指针在同一空间)指针加减整数跳过相应指针类型大小这个数
数组指针
int(p)[10]:p先和结合,说明p是指针变量,指向一个大小为10整型数组;([ ] 优先级高于*)
4、函数指针:
void(pfun)( )指针pfun指向函数,函数无参数,返回类型为void
5、函数指针数组的指针:
void(
(*ppfunArr[ ])(const char *) 指向一个数组的指针,数组的元素是函数指针
6、回调函数:
一个通过函数指针调用的函数,如果你把函数指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们称这就是回调函数

7、指针和数组的区别
数组要么在静态存储区被创建,要么在栈上被创建。指针可以随时指向任意类型的内存块

7、引用于指针的区别:
(1)、引用必须被初始化,指针则不必
(2)、引用初始化以后不能被改变,指针可以改变所指对象
(3)、不存在指向空的引用,但是存在指向空值的指针

七、函数
1、声明:
函数使用一般先在头文件中声明再使用,声明是告诉编译器有一个函数叫什么、返回类型、参数是什么
2、定义:
定义是函数的具体实现,交代函数的功能实现
3、函数的参数
(1)、实参:
真实传给函数的参数叫实参
(2)、形参:
形参在函数调用时才分配空间,调用完成后自动销毁,形产是实参的一份临时拷贝
4、库函数:
< >为引用库函数,从标准路径搜索
5、自定义函数:
“ ”为引用自定义函数,从用户自定义路径搜索
6、函数的链接属性
(1)、外部链接属性:一个标识符不仅可以在当前源文件使用,使用extern的声明可在其它源文件内使用,全局变量都具有外部连接属性
(2)、内部连接属性:具有外部连接属性的标识符前加static就会变为内部连接属性
(3)、无属性:局部变量是无属性的
7、函数的栈帧:待补充

8、函数参数的入栈顺序:从右向左
9、Inline内联函数:
建议编译器做内联展开处理,即函数直接内嵌入调用程序的主体,省去调用/返回指令

10、函数重载:
在同一作用域内,可以具有相同函数名,不同参数列表(参数的个数,类型或者顺序)的函数
11、函数调用:
函数调用时,直接传入对应类型的变量就可以了,数组就是直接传数组名

11、函数递归:
把一个大型复杂问题层层转化为一个原问题相似的规模较小的问题来求解(1、存在限制条件2、每次递归越来越近该限制条件)
12、main函数参数解析
(1)、argc命令行参数个数
(2)、argc[ ] 存放指向命令参数的指针数组,最后一个元素为NULL、envp存放指向环境变量的字符指针数组
11、可变参数列表:
(1)、可变参数列表:可变参数列表通过宏来实现
va_list(访问参数列表未确定部分)、va_arg(初始化)、va_end
(2)、可变参数列表必须从头到尾逐个访问
(3)、可变参数列表中至少有一个命名参数
(4)、这些宏无法直接判断司机存在餐宿数量、类型
12、字符串函数:
strcpy , strcat , strcmp , strlen , strstr , strchr , mecpy , memmove
13、输入输出函数
错误报告函数void perror(const char*str)用于提示错误的标准原因

终止执行 void exit(int status)  0正常退出、非0异常退出

(1)、I/O函数库
1、流:文本流、二进制流
2、输入、输出函数
getchar , putchar
fgetc , getc , fputtc , putc
fgets, gets , fputs , puts
scanf , printf , fscanf , fread , fweite

14、补充:
函数可以嵌套调用,而不可以嵌套定义
八、引用:
引用就是某一变量(目标)的一个别名,对引用的操作与对变量直接操作完全一样。
1、、引用的声明方法:类型标识符 &引用名=目标变量名;
举个例子:
int a;
int &ra=a; //定义引用ra,它是变量a的引用,即别名
2、、说明:
(1)、&在此不是求地址运算,而是起标识作用。
(2)、类型标识符是指目标变量的类型。
(3)、声明引用时,必须同时对其进行初始化。
(4)、引用声明完毕后,相当于目标变量名有两个名称,即该目标原名称和引用名,且不能再把该引用名作为其他变量名的别名。
ra=1; 等价于 a=1;
(5)、声明一个引用,不是新定义了一个变量,它只表示该引用名是目标变量名的一个别名,它本身不是一种数据类型,因此引用本身不占存储单元,系统也不给引用分配存储单元。故:对引用求地址,就是对目标变量求地址。&ra与&a相等。
(6)、不能建立数组的引用。因为数组是一个由若干个元素所组成的集合,所以无法建立一个数组的别名。

3、引用的主要作用:
(1)在引用的使用中,单纯给某个变量取个别名是毫无意义的,引用的目的主要用于在函数参数传递中,解决大块数据或对象的传递效率和空间不如意的问题。
(2)用引用传递函数的参数,能保证参数传递中不产生副本,提高传递的效率,且通过const的使用,保证了引用传递的安全性。
(3)引用与指针的区别是,指针通过某个指针变量指向一个对象后,对它所指向的变量间接操作。程序中使用指针,程序的可读性差;而引用本身就是目标变量的别名,对引用的操作就是对目标变量的操作。
(4)使用引用的时机。流操作符<<和>>、赋值操作符=的返回值、拷贝构造函数的参数、赋值操作符=的参数、其它情况都推荐使用引用。

九、预处理
预处理又称预编译,是整个编译过程最先做的工作,主要处理#开头指令
1、什么时候进行:
(1)、总是需要使用不经常改动的大型代码
(2)、程序由多个模块组成,所有模块都使用一组标准的包含文件和相同的编译选项

2、预定义符号

FILE:进行编译的源文件名
LINE:文件当前的行号
DATE:文件被编译的日期
TIME:文件被编译的时间
STDC:如果编译器遵循ANSI_C,其值为1(Linux),否则未定义(VS)
#error , #pragma …
3、define定义的标识符:
全部大写,最后不加分号,避免替换后悬空其他语句
4、宏定义、宏替换:
宏的名字大写、宏不能出现递归,宏的参数内部不计算,预处理阶段仅为替换(注意不要省括号)
5、条件编译:
#if #endif
6、补充
宏va_start: 初始化va_list类型的变量,将其值设置为可变参数的第一个变量
宏va_arg: 返回va_list变量的值,并使该变量指向下一个可变参数
宏va_end: va_arg访问完最后一个可变参数之后调用的

十、编译链接
1、预处理器(test.i):
文本处理(gcc-E test.c>test.i)
#include的处理
删注释
#define的替换
2、编译(test.s):
(gcc-S test.c)编译为汇编语言(语法、词法、语义分析、符号汇总)
3、汇编(test.o):
形成符号表,翻译为机器语言
4、链接(tese.exe):
合并段表、符号表的合并和符号表的重定义
十一、数据在内存中的存储方式
1、数据的源码,反码,补码:
计算机系统一律用补码表示和存储,使用补码可以将符号位和数值域统一处理,且CPU中只有加法器
2、整型,浮点型存储方式:
创建一个变量开辟内存空间由变量类型决定,整型浮点型存储方式有很大差异
3、大小端字节序存储:
(1)、大端存储模式(大端字节序):
一个数据低位字节序内容放高地址中,高位字节序放在低地址中
(2)、小端存储模式(小端字节序):
一个数据高位字节序内容放在高地址中,低位字节序内容放在低地址中

十二、内存分布
1、栈区(stack):
由编译器自动分配释放,存放为运行函数分配的局部变量,函数形参,返回数据,返回地址等。操作方式类似于数据结构中的栈
2、堆区(heap):
一般由程序员分配释放,若程序员不释放,程序结束时由OS回收,分配方式类似于链接(malloc,calloc,realloc)
3、全局区(静态区):
相对而言的全局,不绝对,存放全局变量,静态数据,程序结束后由系统释放
4、文字常量区:
存放常量字符串,程序结束后由系统释放
5、程序代码区:
存放函数体(类成员函数和全局函数)的二进制代码

6、堆与栈的区别:
(1)、申请方式:
Stack:系统自动分配
Heap:程序员自己申请

(2)、申请后系统的响应:
Stack:只要栈的剩余空间大于所申请的空间,系统将为程序提供内存,否则将报异常提示栈溢出
Heap:遍历空闲内存地址的链表,寻找一个空间大于所申请的堆节点,然后将该节点从空闲节点链表中删除,并将该节点分配给程序

(3)、申请大小的限制:
Stack:在windows下为2M
Heap:受限于计算机系统中有效的虚拟内存
(4)、申请效率
Stack:速度快,但程序员无法控制
Heap:速度慢,且容易产生内存碎片
(5)、存储内容
Stack:局部变量,参数等
Heap:程序员自行安排

7、堆栈溢出的原因:
没有回收垃圾资源
层次太深的递归调用
8、队列与栈的区别:
队列先进先出,栈先进后出
9、内存分配回收
(1)、malloc/free:申请动态内存与释放
(2)、new/delete:在malloc/free功能的基础上,增加了构造函数与解析函数
(3)、malloc(0)返回值:如果请求的长度位0,则返回一个NULL指针或不能用于访问对象的非NULL指针,该指针能被free安全使用

10、内存分配方式及其区别
(1)、静态存储区分配,内存在程序编译的时候就已经分配好了,这块内存在程序的整个运行期间都存在
(2)、栈,在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动释放,栈内存分配运算内置于处理器的指令集
(3)、堆,亦称为动态内存分配,程序在运行的时候malloc或new申请任意多少的内存,程序员自己负责在何时用free或delete释放内存。动态内存的生存期由程序员决定

十三、动态内存管理
1、malloc
(1)、void *malloc (size_t size)
(2)、开辟连续可用的空间,开辟成功返回开辟号空间的指针,开辟失败返回NULL,所以使用时需要判空
(3)、mallorc不知道开辟空间类型,具体类型在使用时决定
(4)、如果size=0行为标准未定义,具体取决于编译器
2、free
void free(void * ptr)释放和回收动态开辟的内存空间,free后将指针赋为空指针
3、calloc
(1)、void * calloc(size_t num , size_t size)
(2)、为num个大小为size的元素开辟一块空间,并把空间内每个字节初始化为0
4、realloc
(1)、void *realloc (void *ptr ,size_t size)
(2)、对动态开辟的内存进行调整,返回调整后的地址,并将原数据移动到新空间中;原有空间足够大,则继续开辟;原有空间之后空间不够,则开辟新的空间
(3)、如果ptr为NULL则不做调整直接开辟空间
5、常见动态内存错误
(1)、对NULL指针进行解引用操作:malloc函数,若申请失败则返回空指针,所以使用时需进行判空操作
(2)、对动态开辟内存的越界访问
(3)、重复释放动态开辟的内存空间
(4)、只释放了部分动态开辟的内存空间
(5)、对非动态开辟的内存空间进行了释放:free的行为由编译器决定,若对NULL进行释放则什么都不做
(6)、未释放动态开辟的内存造成内存泄露

十四、排序
1、冒泡排序
冒泡排序就是把小的元素往前调或者把大的元素往后调。比较是相邻的两个元素比较,交换也发生在这两个元素之间。所以,如果两个元素相等,我想你是不会无聊地把他俩交换一下的;如果两个相等的元素没有相邻,那么即使通过前面的两两交换把两个相邻起来,这时候也不会交换,所以相同元素的前后顺序并没有改变,所以冒泡排序是一种稳定的排序算法

2、选择排序
选择排序是给每个位置选择当前元素最小的,比如给第一个位置选择最小的,在剩余元素里面给第二个元素选择第二小的,依次类推,直到第n-1个元素,第n个元素不用选择了,因为只剩下它一个最大的元素。那么,在一趟选择,如果当前元素比一个元素小,而该小的元素又出现在一个和当前元素相等的元素后面,那么交换后稳定性就被破坏了,比较拗口,举个例子,序列 5 8 5 2 9 ,我们知道第一遍选择第1个元素5会和2交换,那么原序中2个5的相对前后顺序就被破坏了,所以选择排序不是一个稳定的排序算法

3、插入排序
插入排序是在一个已经有序的小序列的基础上,一次插入一个元素。当然,刚开始这个有序的小序列只有1个元素,就是第一个元素。比较是从有序序列的末尾开始,也就是想要插入的元素和已经有序的最大者开始比起,如果比它大则直接插入在其后面,否则一直往前找直到找到它该插入的位置。如果碰见一个和插入元素相等的,那么插入元素把想插入的元素放在相等元素的后面。所以,相等元素的前后顺序没有改变,从原无序序列出去的顺序就是排好序后的顺序,所以插入排序是稳定的。

4、快速排序
快速排序有两个方向,左边的i下标一直往右走,当a[i] <= a[center_index],其中center_index是中枢元素的数组下标,一般取为数组第0个元素。而右边的j下标一直往左走,当a[j] > a[center_index]。如果i和j都走不动了,i <= j,交换a[i]和a[j],重复上面的过程,直到i > j。 交换a[j]和a[center_index],完成一趟快速排序。在中枢元素和a[j]交换的时候,很有可能把前面的元素的稳定性打乱,比如序列为5 3 3 4 3 8 9 10 11,现在中枢元素5和3(第5个元素,下标从1开始计)交换就会把元素3的稳定性打乱,所以快速排序是一个不稳定的排序算法,不稳定发生在中枢元素和a[j] 交换的时刻。

5、归并排序
归并排序是把序列递归地分成短序列,递归出口是短序列只有1个元素(认为直接有序)或者2个序列(1次比较和交换),然后把各个有序的段序列合并成一个有序的长序列,不断合并直到原序列全部排好序。可以发现,在1个或2个元素时,1个元素不会交换,2个元素如果大小相等也没有人故意交换,这不会破坏稳定性。那么,在短的有序序列合并的过程中,稳定是是否受到破坏?没有,合并过程中我们可以保证如果两个当前元素相等时,我们把处在前面的序列的元素保存在结果序列的前面,这样就保证了稳定性。所以,归并排序也是稳定的排序算法。

6、基数排序
基数排序是按照低位先排序,然后收集;再按照高位排序,然后再收集;依次类推,直到最高位。有时候有些属性是有优先级顺序的,先按低优先级排序,再按高优先级排序,最后的次序就是高优先级高的在前,高优先级相同的低优先级高的在前。基数排序基于分别排序,分别收集,所以其是稳定的排序算法。

7、希尔排序(shell)
希尔排序是按照不同步长对元素进行插入排序,当刚开始元素很无序的时候,步长最大,所以插入排序的元素个数很少,速度很快;当元素基本有序了,步长很小, 插入排序对于有序的序列效率很高。所以,希尔排序的时间复杂度会比O(n^2)好一些。由于多次插入排序,我们知道一次插入排序是稳定的,不会改变相同元素的相对顺序,但在不同的插入排序过程中,相同的元素可能在各自的插入排序中移动,最后其稳定性就会被打乱,所以shell排序是不稳定的。

8、堆排序
我们知道堆的结构是节点i的孩子为2 * i和2 * i + 1节点,大顶堆要求父节点大于等于其2个子节点,小顶堆要求父节点小于等于其2个子节点。在一个长为n 的序列,堆排序的过程是从第n / 2开始和其子节点共3个值选择最大(大顶堆)或者最小(小顶堆),这3个元素之间的选择当然不会破坏稳定性。但当为n / 2 - 1, n / 2 - 2, … 1这些个父节点选择元素时,就会破坏稳定性。有可能第n / 2个父节点交换把后面一个元素交换过去了,而第n / 2 - 1个父节点把后面一个相同的元素没 有交换,那么这2个相同的元素之间的稳定性就被破坏了。所以,堆排序不是稳定的排序算法

十四、补充
1、中断
(1)、ISR不能返回一个值
(2)、ISR不能传递参数
(3)、ISR不进行浮点运算

冒泡算法的时间复杂度:O(n^2)

Constructor:不能声明为虚函数

:: :局部变量与全局变量重名时,想使用全局变量时用::符号

引用已经定义过的全局变量:
1、使用引用头文件的方式
2、用extern关键字

for( ;1 ;) :死循环

三种基本的数据模型:层次模型、网状模型、关系模型

变量:
c语言程序中的变量必须定义后再使用

Int 、unsigned int 所占字节数相同

注释:
C程序中注释部分可以出现在程序中任意合适的地方
八进制输出时,前面不会带0的

Rypedef:声明一个已经存在的数据类型的同义字

ASSERT( )的作用: 调试程序常用的宏,用来判断程序中是否出现了明显的非法数据

System(“pause”)的作用:系统暂停程序,按任意键继续

.h头文件中的#infndef/define/endiif的作用:防止该头文件被重复引用

< >与“ “的区别:前者从标准程序库的路径寻找头文件,后者

C++中的类与C中的struct区别:c++中的类具有成员保护功能,并且具有继承,多态oo特点,而c中的struct没有,c中的struct没有成员函数,且不能继承,派生等

#:把宏参数转化为字符串的运算符

##:把两个宏参数连接的运算符

C语言中的输入输出语句都是通过标准库实现的

实时系统的基本特性:在特定时间内完成特定的任务,实时性与可靠性

全局变量与局部变量的区别:全局变量 储存在静态数据区,局部变量在堆栈

二叉树平衡:左右子树都是平衡二叉树,且左右子数的深度差值绝对值不大于1

Malloc和calloc的区别是分配连续区域长度为1块与n块的区别

\0:字符串结束标志位

Fget函数:要求文件打开方式必须是读或读写的方式或者追加的方式

三种基本结构可以表示任何复杂的算法

算法的有穷性:不管是简单算法还是复杂算法都可以在有限的操作步骤之后结束,

函数的定义不可以嵌套,单函数的调用可以嵌套

除了类属关系运算符"."、成员指针运算符".*"、作用域运算符"::"、sizeof运算符和三目运算符"?:"外其余运算符都可以重载

在缺省{ }时,else总是和它上面离它最近的未配对的if配对

^为异或操作,若参加运算的两个二进制位值相同则为0,否则为1

构造函数和析构函数都是没有返回值类型

&a+1不是首地址+1,系统会认为加了一个整个a数组,偏移了整个数组a的大小(也就是5个int的大小)

x&(x-1)统计1的个数,x|(x+1)统计0的个数

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小明爱吃包子

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值