导图社区 C语言第五版
这是一个关于C语言第五版的思维导图,C是一门流行的语言,融合了计算机科学理论和实践的控制特性。C语言的设计理念让用户能轻松地完成自顶向下的规划、结构化编程和模块化设计。因此,用C语言编写的程序更易懂、更可靠。
编辑于2025-04-26 18:41:29C语言
问题
语句分为哪几类?
明示常量和符号常量之间的关系?
我认为明示常量是符号常量的一种
枚举类型是不是基本数据类型?
我认为不是
->和.叫什么运算符?
现在叫结构成员访问运算符
或点操作符.和箭头操作符->
优点
能够直接访问硬件
设计特性
C是一门流行的语言,融合了计算机科学理论和实践的控制特性。 C语言的设计理念让用户能轻松地完成自顶向下的规划、结构化编程和模块化设计。因此,用C语言编写的程序更易懂、更可靠。
高效性
C是高效的语言。在设计上,它充分利用了当前计算机的优势,因此C程序相对更紧凑,而且运行速度很快。 实际上,C 语言具有通常是汇编语言才具有的微调控制能力,可以根据具体情况微调程序以获得最大运行速度或最有效地使用内存。
注:汇编语言是为特殊的中央处理单元设计的一系列内部指令,使用助记符来表示;不同的CPU 系列使用不同的汇编语言。
可移植性
C是可移植的语言。这意味着,在一种系统中编写的C程序稍作修改或不修改就能在其他系统运行。如需修改,也只需简单更改主程序头文件中的少许项即可。 从8位微处理器到克雷超级计算机,许多计算机体系结构都可以使用C编译器(C编译器是把C代码转换成计算机内部指令的程序)。 但是要注意,程序中针对特殊硬件设备(如,显示监视器)或操作系统特殊功能(如,Windows8或OSX)编写的部分,通常是不可移植的。 由于C语言与UNIX 关系密切,UNIX 系统通常 会将C编译器作为软件包的一部分。安装Linux 时,通常也会安装C编译器。 供个人计算机使用的C编译器很多,运行各种版本的Windows和Macintosh(即Mac)的PC都能找到合适的C编译器。 因此,无论是使用家庭计算机、专业工作站,还是大型机,都能找到针对特定系统的C编译器。
强大而灵活
C语言功能强大且灵活(计算机领域经常使用这两个词)例如,功能强大且灵活的UNIX操作系统,大部分是用C语言写的; 其他语言如,FORTRAN、Perl、Python、Pascal、LISP、Logo、BASIC)的许多编译器和解释器都是用C语言编写的。 因此,在UNIX机上使用FORTRAN时,最终是由C程序生成最后的可执行程序。 C程序可以用于解决物理学和工程学的问题,甚至可用于制作电影的动画特效。
面向程序员
C语言是为了满足程序员的需求而设计的,程序员利用C可以访问硬件、操控内存中的位。C语言有丰富的运算符,能让程序员简洁地表达自己的意图。 C语言不像Pascal 甚至是C++那么严格。这样的灵活性既是优点也是缺点。 优点是,许多任务用C来处理都非常简洁(如,转换数据的格式); 缺点是,你可能会犯一些莫名其妙的错误,这些错误不可能在其他语言中出现。 C语言在提供更多自由的同时,也让使用者承担了更大的责任。 另外,大多数C实现都有一个大型的库,包含众多有用的C函数。 这些函数用于处理程序员经常需要解决的问题。
缺点
要享受用C语言自由编程的乐趣,就必须承担更多的责任。特别是,C语言使用指针,而涉及指针的编程错误往往难以察觉。C语言紧凑简洁,结合了大量的运算符。正因如此,我们也可以编写出让人极其费解的代码。
常识
基本常识
C语言的发展历史
K&R C
C语言发展之除并没有任何标准,直到出现了K&R C(经典C标准),但是只定义了C语言,没有定义C库。
美国国家标准协会(ANSI)与1983组建了一个委员会,开发了一套新的标准,与1989年正式发布。 该标准(ANSI C)定义了C语言和C标椎库,国际标椎化组织于1990年采用了这套标准。这就是C90标椎(业界人士也称ANSI C 标准)
C89/C90
最早的C语言标准,由美国国家标准学会(ANSI)和国际标准化组织(ISO)于1989年联合发布。
规定了C语言的基本语法、数据类型、运算符等,并定义了一个基本的标准库。
C99
1994年,由ANSI/ISO联合委员会开始修订C标准。
1999年,1994年对C语言的修订引出了ISO 9899:1999的发表,它通常被称为C99。
引入了新的语言特性,如单行注释、布尔类型、变长数组、复合字面量等。 此外,C99标准还增加了更多的库函数,包括对复数数学、格式化输入输出、浮点数环境等的支持。
C11
2011年,国际标准化组织(ISO)和国际电工委员会(IEC)旗下的C语言标准委员会(ISO/IEC JTC1/SC22/WG14)正式发布了C11标准。
在C99的基础上进行了进一步改进,并引入了一些新的特性,如类型泛型、多线程支持、匿名结构体和联合体、对 Unicode 字符的支持等。C11还扩展了标准库,增加了新的头文件和函数,如对多线程编程的支持、原子操作、时间和日期处理等。
C17/C18
2018 年发布,是目前为止最新的 C 语言编程标准,用来修订 C11 标准。
没有引入新的语言特性,只对 C11 进行了补充和修正。
计算机表示内存大小的单位
bit(位)(比特)
byte(字节)
半字
word(字)
双字
KB
MB
G
T
1T=1024G=1024^2MB=1024^3KB=1024^4bit
1双字=2字=4半字=8字节=64bit
记数法
数字
1000000000
123000
322.56
0.000056
科学记数法
1.0×10^9
1.23×10^5
3.2256×10^2
5.6×10^-5
指数记数法(e记数法)
1.0e9
1.23e5
3.2256e2
5.6e-5
进制
0x或0X前缀表十六进制
0前缀表八进制
0b或0B前缀表二进制
注:标准的C语言并不支持二进制写法,有些编译器自己进行了扩展,才会支持二进制数字。
标识符命名规则
可以用小写字母、大写字母、数字和下划线_来命名。 而且名称的第1个字符必须是字母或下划线,不能是数字。
C语言的名称区分大小写,即把同一个字母的大小和小写区分为两个不同的字符❤️
同一条声明可声明多个变量
等价
int feet,fathoms;
int feet; int fathoms;
函数传参顺序:从右到左
对C语言来说,真为1假为0
单撇号'内为单个字符',双撇号"内为多个字符(即字符串)。
2^16=6,5536
2^32=42,9496,7296
2^64=1844,6744,0737,0955,1616
C名词常识
头文件
如果想让几个源文件可以访问相同的信息,可以把此信息放入一个文件中,然后利用#include指令把该文件的内容带进每个源文件中。 把按照此种方式包含的文件称为头文件(有时称为包含文件),头文件的扩展名为.h。
库文件
库文件是计算机上的一类文件,可以简单的把库文件看成一种代码仓库, 它提供给使用者一些可以直接拿来用的变量、函数或类。
库是特殊的一种程序,编写库的程序和编写一般的程序区别不大, 只是库不能单独运行。
静态库
静态库在程序的链接阶段被复制到了程序中
命名规则:
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)
不需要建立存储空间的。
告知编译器变量已被创建。
定义性声明(定义) (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实现通过编译和链接两个步骤来完成这一过程。编译器把源代码转换成中间代码,链接器把中间代码和其他代码合并,生成可执行文件。 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,也可以什么都不写。
函数体。即函数首部下面的花括号内的部分。 如果在一个函数中包括有多层花括号,则最外层的一对花括号是函数体的范围。
声明部分:定义在本函数中所用到的变量。
执行部分。由若干个语句组成,指定在函数中所进行的操作。
注:在某些情况下也可以没有声明部分,甚至可以既无声明部分也无执行部分。
注:一个源程序文件为一个编译单位,在程序编译时是以源程序文件为单位进行编译的。
从源文件到可执行文件的过程
预处理:test.c→test.i
预处理器将源文件(.c文件)进行预处理,生成预处理后的文件(.i文件)。
宏替换
头文件展开
条件编译
处理其他预处理指令#pragma等
去注释
编译:test.i→test.s
编译器将预处理后的文件(.i文件)进行词法分析、语法分析、语义分析以及优化,生成汇编文件(.s文件)。
汇编:Linux:test.s→test.o / Windows:test.s→test.obj
汇编器将汇编文件(.s文件)根据汇编指令与机器指令的对照表进行翻译,生成目标文件(.o/.obj文件),即机器可以识别的二进制文件。
链接:Linux:test.o→test.out / Windows:test.obj→test.exe
链接器将多个目标文件(.o/.obj文件)和库文件进行链接,生成可执行文件(.out/.exe文件)。
程序是从上到下一个函数,一个函数,一条语句,一条语句依次执行的。
注意事项
注意编码规范
注意变量的可读性,尽量不要使用例如单个字母的命名方式,除非是循环变量。
注释书写规范
/*注释*/
结束符号可以放在任何位置
//注释//
结束符号必须与开始符号在同一行
注:当注释符号出现在字符串中时,被视为是字符串的一部分。
先定义,后初始化,有需要再声明。
定义:数据类型 变量名
创建一个变量
初始化:数据类型 变量名1 = 常量
给变量赋值
注:可以在没有定义的情况下,直接初始化,会自动创建一个变量并赋值。
声明:extern 数据类型 变量名
告诉编译器有这个变量。
注:变量必须已经被创建
在每个数据声明和语句的最后必须有一个分号。
编写C语言的过程
1.定义程序的目标
在脑中构想出程序应实现的效果。
2.设计程序
具体想想怎样实现效果,程序该分为哪几个部分,每个部分又该有什么样的功能。
3.编写代码
编写代码将功能实现。
4.编译
经过编译,将代码翻译成机器语言。
5.运行程序
运行程序看看效果。
6.测试和调试程序
用多种不同的方法和手段测试,看看程序能否达到想要的效果。
7.维护和修改程序
根据自己的需要修改程序。
在执行交互式程序时,当你使用键盘输入信息后,必须敲一下Enter或Return键,来告诉程序该进行下一步了。
外部声明指的是在函数的外部。
限定符
const
限定一个变量为只读 (可调用,但不能改变)。
注:在C语言中,用const类型限定符声明的是变量,不是常量。
volatile
直接存取原始内存地址
例:volatile int a
确保本条指令不会被编译器的优化而忽略。
volatile对应的变量可能在你的程序本身不知道的情况下发生改变
比如多线程的程序,共同访问的内存当中,多个程序都可以操纵这个变量
你自己的程序,是无法判定何时这个变量会发生变化
还比如,他和一个外部设备的某个状态对应,当外部设备发生操作的时候, 通过驱动程序和中断事件,系统改变了这个变量的数值,而你的程序并不知道。
对于volatile类型的变量,系统每次用到他的时候都是直接从对应的内存当中提取, 而不会利用cache当中的原有数值,以适应它的未知何时会发生的变化, 系统对这种变量的处理不会做优化——显然也是因为它的数值随时都可能变化的情况。
关键字
按发布时间分类(44个)
K&R C
auto
声明自动变量
break
转向语句
case
switch条件语句
char
数据类型:字符型
continue
转向语句
default
do
循环语句
double
数据类型:双精度浮点型
else
if条件语句
extern
声明这个变量(是全局变量)(函数),告诉编译器在外部文件中有这个变量(函数),要通过编译。
float
数据类型:单精度浮点型
for
循环语句
goto
转向语句
if
if条件语句
int
数据类型:整型
long
数据类型:长整型
register
修饰变量作为一个寄存器变量,让该变量的访问速度达到最快。
例:register int a
寄存器变量是指一个变量直接引用寄存器, 也就是对变量名的操作的结果是直接对寄存器进行访问。
注:待声明为寄存器变量的类型应该是CPU寄存器所能接受的类型,寄存器变量是单个变量,变量长度应该小于等于寄存器长度。
注:不能对寄存器变量使用取地址符“&”,因为该变量没有内存地址。
注:尽量在大量、频繁操作时使用寄存器变量,且声明的变量个数应该尽量少。
注:该关键字,现在已经不会有人在使用了,因为现在的编译器已经很智能了,能够进行比人更好的代码优化(会自动识别给需要的变量加上 register )。 早期编译器需要人为指定register,来进行手动优化,现在不需要了。
restrich
return
转向语句
short
数据类型:短整型
sizeof
计算变量(或数据类型)在当前系统中占用内存的字节数
格式
sizeof(参数1)
注:后面的括号可以省略
注:既是运算符,又是关键字
static
声明一个变量为静态变量。
注:在函数中声明变量时, static 关键字指定变量只初始化一次,并在之后调用该函数时保留其状态。
注:在声明变量时,变量具有静态持续时间,并且除非您指定另一个值。
注:static关键字没有赋值时,默认赋值为 0
注:在全局和/或命名空间范围(在单个文件范围内声明变量或函数时)static关键字指定变量或函数为内部链接,即外部文件无法引用该变量或函数。
注:全局变量和函数本身是具有外部链接属性的,在Hello.c文件中定义的全局变量和函数, 在test.c文件中可以通过【链接】来使用;
但如果全局变量被static修饰,那这个外部链接属性就会被修改成内部链接属性,此时这个全局变量就只能在自己的源文件中使用;
注:如果加了static,就会对其它源文件隐藏。利用这一特性可以在不同的文件中定义同名函数和同名变量,而不必担心命名冲突。 static可以修饰函数和变量,将其对其他源文件隐藏起来,从而避免命名冲突。对于函数来讲,static的作用仅限于该隐藏功能。
struct
定义结构体(联合体)数据类型。
switch
switch条件语句
typedef
给数据类型重命名
例:typedef unsignsd long int UNIT
将unsignsd long int类型命名为UNIT
好处
解决编译器默认有无符号移植性问题
提高代码可读性
提高编码效率
为程序进行加密
union
定义共用体数据类型。
unsigned
数据类型(前缀):无符号
while
循环语句
C89
const
enum
定义枚举数据类型。
signed
数据类型(前缀):有符号
void
数据类型:空
volatile
C99
inline
C11
_Alignas
_Alignof
_Atomic
_ Bool
数据类型:布尔型
_Complex
数据类型:复数
_Generic
_Imaginary
数据类型:虚数
_Noreturn
_Static_assert
_Thread_local
按功能分类(32个)
数据类型关键字
基本数据类型
void:声明函数无返回值或无参数,声明无类型指针,显式丢弃运算结果。
char:字符型类型数据,属于整型数据的一种。
int:整型数据,通常为编译器指定的机器字长。
float:单精度浮点型数据,属于浮点数据的一种。
double:双精度浮点型数据,属于浮点数据的一种。
类型修饰关键字
short:修饰int,短整型数据,可省略被修饰的int。
long:修饰int,长整形数据,可省略被修饰的int。
signed:修饰整型数据,有符号数据类型。
unsigned:修饰整型数据,无符号数据类型。
复杂类型关键字
struct:结构体声明。
union:共用体声明。
enum:枚举声明。
typedef:声明类型别名。
sizeof:得到特定类型或特定类型变量的大小。
存储级别关键字
auto:指定为自动变量,由编译器自动分配及释放。通常在栈上分配。
static:指定为静态变量,分配在静态变量区,修饰函数时,指定函数作用域为文件内部。
extern:指定对应变量为外部变量,即在另外的目标文件中定义,可以认为是约定由另外文件声明。
const:与volatile合称“cv特性”,指定变量不可被当前线程/进程改变(但有可能被系统或其他线程/进程改变)。
流程控制关键字
跳转结构
return:用在函数体中,返回特定值(或者是void值,即不返回值)。
continue:结束当前循环,开始下一轮循环。
break:跳出当前循环或switch结构。
goto:无条件跳转语句。
分支结构
if:条件语句。
else:条件语句否定分支(与if连用)。
switch:开关语句(多重分支语句)。
case:开关语句中的分支标记。
default:开关语句中的“其他”分治,可选。
循环结构
for:for循环结构,for(1;2;3)4;的执行顺序为1->2->4->3->2...循环,其中2为循环条件。
do:do循环结构,do 1 while(2);的执行顺序是1->2->1...循环,2为循环条件。
while:while循环结构,while(1) 2;的执行顺序是1->2->1...循环,1为循环条件。
以上循环语句,当循环条件表达式为真则继续循环,为假则跳出循环。
预处理
预处理是指在进行编译的第一遍扫描(词法扫描和语法分析)之前所作的工作。
#define指令定义一个宏定义
格式
#define 标识符 文本
将"文本"打包重命名,每当程序识别到"标识符"时,会自动将标识符解压翻译为文本。
注:"文本"可为常量,可为字符串,也可为几段函数。
可以这么理解,假如"文本"为几段函数,那当你引用程序中其他正常函数的函数名时,只能调用一个函数, 而当你引用宏时,会直接将文本中的几段函数从头到尾的运行一遍,此时,宏就相当于其他正常函数的函数名。
作用域
从定义开始到程序结尾或#undef
#undef指令删除一个宏定义
#include指令打开指定的文件,并且把此文件的内容包含到程序中。
#include <文件名>
用于属于C语言自身库的头文件
使用尖括号表示在包含文件目录中去查找,而不在源文件目录去查找。
注:包含目录是由用户在设置环境时设置的。
#include "文件名"
用于所有其他头文件,也包含任何自己编写的文件
使用双引号则表示首先在当前的源文件目录中查找,若未找到才到包含目录中去查找。
注:包含目录是由用户在设置环境时设置的。
#ifdef:检查是否定义了某个宏,如果定义了,则其后的代码段将被编译。
格式
#ifdef 宏 代码段
#ifndef:检查某个宏是否未定义,如果未定义,则其后的代码段将被编译。
格式
#ifndef 宏 代码段
#if:后面跟一个整型常量表达式,该表达式的值为非零(真)时,其后的代码段将被编译。
#else:当上方#if、#ifdef或#ifndef指令之后的整型常量表达式不为真,而此指令之后的整型常量表达式为真时,执行此指令之下的代码段。
注:类if else语句
#elif:前面的#if、#ifdef 或#ifndef 指令的条件评估为假并且#elif 条件评估为真时,程序会执行#elif语句之后的代码段。
#endif 用于结束一个条件编译块。
#error、#line、#pragma
注:指令都以#开始
#符号不需要在一行的行首,只要它之前只有空白字符就行。在#后是指令名,接着是指令所需要的其他信息。
在指令的符号之间可以插入任意数量的空格或水平制表符
指令总是在第一个换行符处结束,除非明确地指明要延续
如果想在下一行延续指令,我们必须在当前行的末尾使用'\'字符。
指令可以出现在程序中的任何地方
但我们通常将#define和#include指令放在文件的开始,其他指令则放在后面,甚至可以放在函数定义的中间。
注释可以与指令放在同一行
实际上,在宏定义的后面加一个注释来解释宏的含义是一种比较好的习惯。
变量
参数
形式参数 (形参)
在定义子函数时,在函数名之后加小括号,在小括号中的变量便称形式参数。
函数定义中指定的形参为局部变量,在未发生函数调用时不占内存, 只有函数调用时,函数中的形参才被分配内存单元。在调用结束后,形参所占的内存单元也被释放。
形参必须指定数据类型,和定义变量一样,因为它本来就是局部变量。
主调函数在调用函数之前,应对被调函数做原型声明。
实际上和在子函数中定义的局部变量一样,就是多了一个可以接收函数之外的值的功能。
实际参数 (实参)
在调用子函数时,在函数名之后加小括号,在小括号中的变量便称实际参数。
实参可以是常量,也可以是简单或者复杂的表达式,但是要求它们必须有确定的值, 在调用发生时将实参的值传递给形参。
实参和形参的数据类型应该相同或者赋值兼容。 和变量赋值一样, 当形参和实参出现不同类型时,按照不同类型数值的赋值规则进行转换。
实参向形参的数据传递是单向传递,不能由形参再回传给实参。 也就是说,实参值传递给形参后,调用结束,形参单元被释放,而实参单元仍保留并且维持原值。
实际参数一般为调用函数中的局部变量或全局变量,其作用为把实际参数所对应的地址保存的值赋给被调用函数的形式参数。
按作用域分
复合语句变量
字节长度
随数据类型
生命周期
所在复合语句执行时,分配空间,执行结束,释放空间
作用域
所在复合语句可用
存储区域
栈空间
局部变量
字节长度
随数据类型
生命周期
所在函数体执行时,分配空间,执行结束,释放空间
作用域
所在函数可用
存储区域
栈空间
全局变量
字节长度
随数据类型
生命周期
所在程序执行时,分配空间,执行结束,释放空间
作用域
从其定义开始到文件结尾
注:在声明之上的函数需要extern外部声明
存储区域
数据段
注:不能用变量或表达式的结果进行初始化,必须用常量进行初始化。
注:若复合语句变量和局部变量同名,则在复合语句变量有效范围内,以复合语句变量为主。
注:若局部变量和全局变量同名,则在局部变量有效范围内,以局部变量为主。
按存储类别分
自动变量
在使用变量时,系统会自动分配存储空间,而在变量使用完毕后,系统会自动释放存储空间。
注:使用auto关键字声明。
静态变量
系统自动分配存储空间,在程序运行的整个过程中都不释放存储空间。
注:使用static关键字声明。
注:静态变量的定义只赋值一次。
不能用变量或表达式的结果进行初始化,必须用常量进行初始化。
注:可用static关键字声明全局变量,使其成为"静态外部变量",静态外部变量和用extern声明的全局变量相比,就像是局部变量和全局变量相比一样。 若在静态外部变量的文件中,静态外部变量和外部变量发生冲突,以静态外部变量为准。
寄存器变量
存放在CPU寄存器中的变量。
注:使用register关键字声明。
注:只有局部自动变量(非静态变量和形参)可以作为寄存器变量
注:自动和静态变量都是存放在内存中的,使用时需要从内存中存取,由于对寄存器的存取速度远高于对内存的存取速度,所以这样做可以提高执行效率。
注:一个计算机系统中的寄存器数目有限,不能定义任意多个寄存器变量
注:现在的编译系统能够识别使用频繁的变量,从而自动的将这些变量存放在寄存器器中。
外部变量
全局变量的一种变式。
注:使用extern关键字声明
注:扩展全局变量的作用域,可在全局变量定义之前用extern声明全局变量,则该全局变量的作用域为从extern声明处至文件结尾。
注:使得全局变量可被其它文件使用。
C语言规定:变量的存储类型默认为自动变量。
数据类型
基本(内置)数据类型
int(基本整型)
short int(短整形)
long int(长整形)
long long int(长长整形)(C99)
signed(前缀)
有符号:意为储存其中的值可以为负数。
例:signed short int的范围为-2^8~2^8
注:singned、int可省略
unsigned(前缀)
无符号:意为储存其中的值只能是正数。
例:unsigned short int的范围为0~2^16
注:int可省略
注:int可省略
注:C标准没有具体规定各种类型数据所占用存储单元的长度,这是由各编译系自行决定的。
通常来说char为1个字节,short为2个字节,int为4个字节,long为8个字节,long long为16个字节。
char(字符型)
字符型数据是以字符的ASCII代码存储在存储单元中的,一般占一个字节。
由于ASCI代码也属于整数形式,因此在C99标准中,把字符类型归纳为整型类型。
_Bool int(布尔值)
在编程中,表示真或假的变量被称为布尔变量(Boolean variable),所以_Boo1是C语言中布尔变量的类型名。
_Boo1类型的变量只能存储1(true)(真)或0(false)(假)。 如果把其他非零数值赋给_Boo1类型的变量,该变量会被设置为1。
要在 C 中使用 bool,必须包含头文件“stdbool.h”。
float(单精度浮点型)
至少能表示六位有效数字,且取值范围至少是-3.4*10^-38∽3.4*10^38。
double(双精度浮点型)
至少能表示十五位有效数字,取值范围为-1.5*10^-308~1.5*10^308。
long double(长双精度浮点型)
表示的有效数字比double多,取值范围比double厂。
复数浮点形
float_Complex(复数类型)
float_Complex 类型的变量应包含两个float类型的值,分别表示复数的实部和虚部。
double_Complex(复数类型)
long double_Complex(复数类型)
float_Imaginary(虚数类型)
double_Imaginary(虚数类型)
long double_Imaginary(虚数类型)
派生(构造)数据类型
数组类型
数组
数组(array)是按顺序存储的一系列类型相同的值,是一组有序数据的集合。
由连续的一块内存单元组成。
用一个数组名来代表整个数组
用于识别数组元素的数字被称为下标、索引或偏移量。下标必须是整数,而且要从0开始计数。
用一个数组名和下标来唯一地确定数组中的元素,
由于计算机键盘只能输入有限的单个字符而无法表示上下标,C语言规定用方括号中的数字来表示下标。
数组格式
数据类型 数组名[数组长度]
数据类型 字符[数字或常量表达式]
调用格式
数组名[下标]
数组名[数字或常量表达式]
注:在调用时,先由数组名的地址找到数组首元素的地址,再由数组首元素的地址找到数组首元素的值,然后再找到你要找的元素的值。(个人感悟,不一定对)
注:定义数组时用到的“数组名[数组长度]”和引用数组元素时用的“数组名[下标]”形式相同,但含义不同。
数组长度从1开始,下标从0开始。
注:数组名是指针常量,地址是整个数组的地址,地址大小为数组的大小,保存的值是数组首元素的地址。
多维数组
二维数组
格式
数据类型 数组名[数字1][数字2]
一个“数组名”大数组中包含“数字1”个小数组,而每个小数组又都能包含“数字2”个变量。
三维数组
格式
数据类型 数组名[数字1][数字2][数字3]
一个“数组名”大数组中包含“数字1”个中数组,而每个中数组又都能包含“数字2”个小数组,而每个小数组又都能包含“数字3”个变量。
……
…
…………
………………………
变长数组 VLA
C99允许数组的[ ]中的值是整形变量或是整形表达式。
注:变长数组是指用整型变量或表达式声明或定义的数组,而不是说数组的长度会随时变化,变长数组在其生存期内的长度同样是固定的。
注:变长数组不等于动态数组,本质还是静态数组,也就是说,数组的长度在变量的整个生命周期中是不可变的
注:变长数组只能是局部变量数组
注:变长数组不能在定义的时候进行初始化
例:int n; scanf("%d",&n); int aaa [n];
注:变长数组必须是自动存储类别,即不能使用extern或static存储类别说明符
注:由于变长数组只能是局部变量,且必须是自动存储类别,因此变长数组分配在栈上
注:可变长数组对于多维数组也适用(如array[a][b] )
注:需编译器支持此特性
注:VS系列编译器均不支持该特性
字符数组
用来存放字符数据的数组是字符数组。在字符数组中的一个元素内存放一个字符。
相当于翻译器,将存储在元素中的数字以ASCII码格式翻译成相应的字符并打印出来。
注:计算机只能识别二进制语言❤️。
定义
char c[] ={'I',' ', 'a','m',' ','h','a','p','p','y','/0'}
char c[]={"I am happy"}
注:花括号可省略
注:用字符数组存放字符串
注:由于字符型数据是以整数形式(ASCII代码)存放的,因此也可以用整型数组来存放字符数据,但比较浪费存储空间。
注:字符数组中的数字也是字符,不是单纯的数字。
字符串
由多个字符组成,其末尾一定有/0。
注:/0为空字符,C语言用它标记字符串的结束。
凡是指向或保存字符串地址的,其指向或保存的都是字符串首字符的地址。
注:C语言中没有字符串类型,也没有字符串变量,字符串是存放在字符型数组中的。
在C语言中,是将字符串作为字符数组来处理的。
注:C语言规定了一个“字符串结束标志”,以字符\’0'作为结束标志。
如果字符数组中存有若干字符,前面9个字符都不是空字符('\0'),而第10个字符是个'\0',则认为数组中有一个字符串,其有效字符为9个。
注:C系统在用字符数组存储字符串常量时会自动加一个0'作为结束符。
指针数组
一个数组的元素值皆为指针则是指针数组。
注:数组名是指针变量,保存数组首元素的地址。
注:数组中的每一个元素都属于同一个数据类型,不能把不同类型的数据放在同一个数组中。
结构体类型
结构体就是一些值的集合,这些值称为成员变量。结构体的每个成员可以是不同类型的变量。
结构体类似一个大包裹,并没有实际改变什么,只是给成员变量加了个前缀,帮助程序员更好地理解程序。
定义
先定义结构体类型,再声明结构体变量。
struct 结构体名 { 成员变量 };
注:前面定义的是结构体数据类型,它只是约定它要声明的变量包括哪些成员,需要分配多大的存储空间。它本身不是变量,不能用于存储数据。 只有用它声明了结构体变量,才能使用结构体变量来进行数据的存储。因此,定义了结构体之后,还要用它声明结构体变量,然后使用结构体变量。
struct 结构体名 结构体变量名
定义结构体类型的同时声明结构体变量
struct 结构体名 { 成员变量 }结构体变量名;
直接声明结构体类型变量
struct { 成员变量 }结构体变量名;
注:由于没有设置结构体的名字,这种显示对结构体类型的的使用是一次性的。
注:使用struct关键字创建。
注:不可在结构体中再包函一个结构体。
共用(联合)体类型
它允许在同一个内存位置存储不同的数据类型,或者说同一个内存空间有多种解释方式。
定义
先定义结构体类型,再声明结构体变量。
struct 结构体名 { 成员变量 };
注:前面定义的是结构体数据类型,它只是约定它要声明的变量包括哪些成员,需要分配多大的存储空间。它本身不是变量,不能用于存储数据。 只有用它声明了结构体变量,才能使用结构体变量来进行数据的存储。因此,定义了结构体之后,还要用它声明结构体变量,然后使用结构体变量。
struct 结构体名 结构体变量名
定义结构体类型的同时声明结构体变量
struct 结构体名 { 成员变量 }结构体变量名;
子主题直接声明结构体类型变量
struct { 成员变量 }结构体变量名;
注:由于没有设置结构体的名字,这种显示对结构体类型的的使用是一次性的。
注:使用union关键字创建。
注:共用体的大小取决于最大的成员的大小。
注:对共用体的任何成员的修改都会影响到其他成员。
注:共用体的成员可以具有不同的数据类型,但它们在内存中的起始位置是相同的。
注:共用体的所有成员共享同一块内存空间,因此同一时间只能存储其中一个成员的值。
枚举类型
为常量赋予有意义的名称,从而增强代码的可读性和可维护性。
使用 enum 关键字定义,通过为一组相关常量命名来创建一种新的数据类型。
格式
不指定值的枚举
enum 枚举名称 { 枚举元素1, 枚举元素2, ... 枚举元素N }
指定部分值的枚举
enum 枚举名称 { 枚举元素1 = 值1, 枚举元素2, ... 枚举元素N }
完全指定值的枚举
enum 枚举名称 { 枚举元素1 = 值1, 枚举元素2 = 值2, ... 枚举元素N = 值N }
注:相当于给常量重命名,注意一个常量可以有多个名字。
唯一性:在同一枚举类型中,每个枚举常量的值必须唯一,即不能有两个或多个枚举常量具有相同的值。
整数类型:枚举常量的值必须是整数类型,虽然在大多数编程语言中枚举常量本身不直接表现为整数,但在底层它们通常由整数(通常是 int 类型)表示。
自定义值:定义枚举时,可以为枚举元素指定自定义的整数值,但需确保这些值的唯一性和整数类型。
自动递增赋值:
如果没有为枚举常量显式指定值,编译器会自动为它们分配值。这些值通常从 0 开始,依次递增。 例如,在一个包含三个枚举常量的枚举类型中,第一个枚举常量会被分配值 0,第二个为 1,第三个为 2。
如果为第一个枚举常量显式赋值,后续的枚举常量没有显式赋值,则后续枚举常量会从该值开始依次递增。 例如,如果给第一个枚举常量显式赋值为 1,则第二个枚举常量的值默认为 2,第三个默认为 3,依此类推。
如果中间的某个枚举常量显式赋了某个值,而下一个常量没有显式赋值,那么其默认值将从上一个枚举常量的值开始依次递增。 例如,如果给第一个枚举常量显式赋值为 1,则第二个枚举常量的值默认为 2,再给第三个枚举常量显式赋值为 10,则第四个枚举常量的值默认为 11,依此类推。
注:枚举名称(通常使用全大写表示)和其中的元素名称均为标识符(需要满足标识符的命名规则)。
注:枚举类型和宏定义是差不多的,只有细微区别,宏运行是在预处理阶段完成的,枚举类型是在与编译阶段完成的。
函数类型
函数类型用来定义函数,描述一个函数的接口,包括函数返回值的数据类型和参数的类型。
基本类型(包括整型和浮点型)和枚举类型变量的值都是数值,统称为算术类型(arithmetic type)。
算术类型和指针类型统称为纯量类型(scalar type),因为其变量的值是以数字来表示的。
数组类型和结构体类型统称为组合类型(aggregate type)
共用体类型不属于组合类型,因为在同一时间内只有一个成员具有值。
指针类型
指针是一种数据类型,是变量地址的别称,它可以用来定义变量,指针变量保存的值是其它变量的地址。
注:由于地址是固定长度,所以指针变量的长度是固定长度,
注:由于不同地址的步长不一样,所以要不同指针类型的变量来保存
注:const修饰的指针,const在*左表示指针指向的内容不可修改,const在*右表示指针的指向不可修改。
格式
数据类型 * 变量名1 = &变量名2
“变量名1”是指向“数据类型”变量的指针。
把变量名2的地址赋给变量名1。
注:指针变量只能保存正常变量的地址,不能保存常量。
运算符
*
间接(解引用)(间接寻址)运算符
给出存储在指针指向地址上的值
设int pooh = 5,ptr = &pooh, val = *ptr,则val = pooh = 5
&
取地址运算符
给出变量的存储地址
如果 pooh是变量名, 那么&pooh是变量的地址。
多级指针
层层转包,单线联系
n级指针
数据类型 (n个*) 变量名1 = (相同的)数据类型 (n-1个*) 变量名2
注:一级指针保存变量的地址,二级以上指针只能保存前一级指针的地址。
注:只有在多级指针前面加上对应多的“*”运算符才能取到最初变量保存的值,不然取到的就只是对应级指针的地址。
设a、b、c、d、e,分别为一/二/三/四/五级指针,int i = 2;int * a = &i; int ** b = &a;……;int ***** e = &d;
*e获取d的地址,**e获取c的地址,***e获取b的地址,****e获取a的地址,*****e获取i的变量值。
数组指针
数组的指针是指数组的首地址。
字符指针
和字符串指针是一个意思,只是偏向强调的重点不同, 字符指针强调指向的是一个字符的地址, 而字符串指针强调的是指向一个字符串的首字符的地址。
字符串指针
指向一个字符串的首字符的地址。
注:指针指向的值保存在数据段的or段,该数据不可被修改。
函数指针
格式
定义
数据类型 (*指针变量名) (实参的数据类型)
初始化
指针变量名 = 函数名
将"函数名"函数的入口地址赋值给函数指针变量"指针变量名"
调用
(*指针变量名) (实参表)
(*指针变量名) (纯数据)
例:(*指针变量名) (6,7,3,9)
(*指针变量名) (kxjdsjskshvsk,dkish7646kdj)
万能指针
void *
可以接收任何类型指针的地址
注:想要对void类型指针变量进行解引用,就要将void类型指针变量进行强制类型转换,从void*强制转换为这个指针中存储的地址指向的值的类型。
文件指针
每个被使用的文件都在内存中开辟了一个相应的文件信息区,用来存放文件的相关信息 (如文件的名字,文件状态及文件当前的位置等)。 这些信息是保存在一个结构体变量中的。该结构体类型是由系统声明的,取名FILE。
格式
FILE* 指针名
注:stdio.h头文件把3个文件指针与3个标准文件相关联,C程序会自动打开这3个标准文件。
标准文件
标准输入
标准输出
标准错误
文件指针
stdin
stdout
stderr
通常使用的设备
键盘
显示器
显示器
void(空)类形
无返回值
系统定义
size_t
类型
typedef 定义的无符号整数类型
用途
用于表示对象的大小(以字节为单位)或数组的索引
注:在各个C标准库中定义。
注:size_t 的具体底层类型取决于编译器和平台。
注:不同类型的数据在内存中占用的存储单元长度是不同的。
注:数据类型相互转换方法
在变量前面加上一个数据类型名,并用小括号将这个类型名括起来。
如:c=(unsigned long)a*b
注:强制类型转换运算符优先级高于算术运算符
注:隐式类型转换
char→short→int→long→float→double
运算符(操作符)
运算符是C语言规范的一部分,由编译器直接支持,不依赖外部文件(如头文件)。
运算符优先级如图0-2-1
图0-2-1
sizeof
计算变量(或数据类型)在当前系统中占用内存的字节数
既是运算符,又是关键字
算术运算符
+
一元
标明或改变一个值的代数符号(正负)
二元
加法运算符
使其两侧的值相加
-
一元
标明或改变一个值的代数符号(正负)
二元
减法运算符
左侧的数减去右侧的数
*
一元
间接(解引用)(间接寻址)运算符
给出存储在指针指向地址上的值
设int pooh = 5,ptr = &pooh, val = *ptr,则val = pooh = 5
二元
乘法运算符
使其两侧的数相乘
/
除法运算符
左侧的数除以右侧的数
在C语言中,整数除法结果的都分被丢弃,这一过程被称为截断(truncation)。
%
求模运算符
给出左侧整数除以右侧整数的余数
只能用于整数
++
递增(自加)运算符
将其运算对象递增(自加)1
只有圆括号的优先级比其高
“++变量名”和“变量名++”的区别
在打印过程中“++变量名”被替换成变量名,“变量名++”被替换成变量的值
“++变量名”先加,也就是说,他先把传到自身的数加1, 再把自身的数传播给下一个数,其自身的数比传到自身的数多1。
“变量名++”后加,也就是说,他把传到自身的数直接传给下一个数, 只是在传到下一个数的前一瞬间才加1,其自身的数和传到自身的数一样。
--
递减(自减)运算符
将其运算对象递减(自减)1
只有圆括号的优先级比其高
“--变量名”和“变量名--”的区别
在打印过程中“--变量名”被替换成变量名,“变量名--”被替换成变量的值
“--变量名”先减,也就是说,他先把传到自身的数减1, 再把自身的数传播给下一个数,其自身的数比传到自身的数少1。
“变量名--”后减,也就是说,他把传到自身的数直接传给下一个数, 只是在传到下一个数的前一瞬间才减1,其自身的数和传到自身的数一样。
移位运算符
<<
左移
高位溢出丢弃,低位不足补0
>>
右移
正数:高位补0,低位溢出舍去
负数:高位补1,低位溢出舍去
位运算符
&
一元
给出变量的存储地址
如果 pooh是变量名,那么&pooh是变量的地址。
二元
按位与,F=A&B。将A、B两个字节中的每一位都进行与运算,再将得到的每一位结果组合为总结果F。
与运算:当两个参与运算的数中有一个数为0,则运算结果为0,都为1结果为1
A=0b11001100 B=0b11110000 F=0b11000000
|
按位或,F=A|B,将A、B两个字节中的每一位都进行或运算,再将得到的每一位结果组合为总结果F。
或运算:当两个参与运算的数中有一个数为1,则运算结果为1,都为0结果为0
A=0b11001100 B=0b11110000 F=0b11111100
~
按位取反,F=~A。将A字节内的每一位进行非运算(就是取反),再将得到的每一位结果组合为总结果F。
非运算:非运算的结果只有两个,非0得1,非(非0)得0, 即:非假即真,非真即假。0表示假,只要不是0的数都表示真
A=0b11001100 F=0b00110011
^
按位异或。F=A^B。异或的意思为,如果运算双方的值不同(即相异),则结果为真,双方值相同则结果为假。
在C语言里没有按变量整体值进行的异或运算,故仅以按位异或为例。
A=0b11001100 B=0b11110000 F=0b00111100
注:运算优先级是非—>与—>或
赋值运算符
=
将右边的值赋给左边
在C语言里,“=”代表赋值,而不是等于❤️
复合算术赋值
+=
x+=y代表x=x+y
-=
x-=y代表x=x-y
*=
x*=y代表x=x*y
/=
x/=y代表x=x/y
%=
x%=y代表x=x%y
复合位运算赋值
&=
x&=y代表x=x&y
|=
x|=y代表x=x|y
^=
x^=y代表x=x^y
>>=
x>>=y代表x转换为二进制后右移y位,结果赋值给x并输出
如x = 2 x >>= 3
结果x = 16
<<=
x<<=y代表x转换为二进制后左移y位,结果赋值给x并输出
如x = 16 x <<= 3
结果x = 2
所有的位运算都先将数值转化为二进制, 在运算完成后再将数值转化为原来的进制。 (无转换要求情况下)
在c语言里,为了语法的简单性,推出了一系列的组合运算符,但核心的思想都是:左边和右边的变量做运算,结果存入左边变量。
关系运算符
>
大于
<
小于
!=
不等于
==
等于
>=
大于等于
<=
小于等于
注:上述符号的意思都是判断,当满足符号意思时,则运行符号语句, 不满足符号意思时则跳过符号语句。
逻辑运算符
&&
逻辑与,F=A&&B。当A、B的值都为真(即非0值,下同)时,其运算结果F为真(具体数值为1,下同); 当A、B值任意一个为假(即0,下同)时,结果为假(具体数值为0,下同)。
||
逻辑或,F=A||B。当A、B值任意一个为真时,其运算结果F为真;当A、B值都为假时,结果F为假。
!
逻辑非,F=!A。当A值为假时,其运算结果F为真;当A值为真时,结果F为假。
条件运算符
?:
格式
exp1?exp2:exp3
先判断exp1是否为真,若为真则exp2计算,计算的结果就是整个表达式的结果; 如果exp1为假则exp3计算,计算结果就是整个表达式的结果。
例:×=( y < 0 ) ? - y : y ;
在=和;之间的内容就是条件表达式,该语句的意思是“如果y小于0,那么x=-y;否则x=y”。
逗号运算符
,
逗号运算符把两个表达式连接成一个表达式,并保证表达式从左向右依次执行。
逗号运算符可以用于在一个语句中计算多个表达式的值,并返回最右边表达式的值。
如图0-0-1
在循环和条件语句中,逗号运算符可以用于简化控制流程。
如图0-0-2
逗号运算符可以在函数调用时传递多个参数,特别是在参数数量不确定的情况下。
如图0-0-3
下标访问
[]
一个数组名 + 一个索引值
例:int arr[10];//创建数组 arr[9] = 10;//实用下标引用操作符
函数调用
()
函数名+实参
结构成员访问运算符
.
直接访问结构体的成员变量
结构体变量名.成员变量名
->
间接访问结构体的成员变量
结构体指针->成员变量名
转义字符
转义字符(Escape character),所有的ASCII码都可以用“\”加数字(一般是8进制数字)来表示。 而C语言中定义了一些字母前加"\"来表示常见的那些不能显示的ASCII字符,如\0,\t,\n等,就称为转义字符;
\0 空字符(NULL) 在c语言中代表“字符串结束符”
\a 警报符(alert) 发出听觉或视觉上的信号
\b 退格符(backspace) 将当前位置移到前一列
\f 换页符(form feed) 换页,将当前位置移到下一页开头
\n 换行符(new line) 换行,将当前位置移到下一行开头
\r 回车符(carriage return) 将当前位置移到本行开头
\t 水平制表符(horizontal tab) 将当前位置移动到下一个水平制表(Tad)位置
\v 垂直制表符(vertical tab) 将当前位置移动到下一个垂直制表位置
\\ 输出字符\
\? 输出字符 ?
\’ 输出字符 ’
\" 输出字符 "
\"三位数字"
将"三位数字"以八进制形式转化为十进制,并输出ASCII码与转化后的数字相同的字符。
\x"两位数字"
将"两位数字"以十六进制形式转化为十进制,并输出ASCII码与转化后的数字相同的字符。
注:转义字符虽然由两个及两个以上的字符构成,但它表示的却是单个字符
函数
函数是实现了某种功能的代码块,是完成特定任务的独立程序代码单元。语法规则定义了函数的结构和使用方式。
声明格式
返回值的数据类型 函数名 (形式参数列表)
注:一般在文件开头声明,为了让此函数之上的函数能够调用此函数。和全局变量要尽量在文件开头定义的原因差不多。
注:函数声明的作用是告诉编译器被调用函数是个函数,以及这个函数的具体信息。
注: 函数声明既可以放在某个函数的内部,也可以放在所有函数的外面。 如果放在函数内部,则被声明函数只能在该函数内部被调用。 如果放在所有函数的外面,则声明后面的所有函数都能调用被声明函数。
注:函数声明又称函数原型。
定义格式
返回值的数据类型 函数名 (形式参数列表) { 函数体 }
在程序中,函数名一般会在三种途径被使用
程序开头函数的声明
函数的调用
函数的定义
注:形式参数列表可以不存在,这也就意味着,此函数不接收任何外来数据。
注:如果没有声明返回值的数据类型,则默认为int类型。
注:当函数的返回值类型为void时,则表示函数不会返回任何值。在这种情况下,函数体内部不能使用 return 语句返回值。
注:在C语言中,一个函数总是占用一段连续的内存区,而函数名就是该函数所占内存区的首地址。
注:每个函数都是相互独立的,可以嵌套调用,但不能嵌套定义。
调用格式
函数名(实际参数列表)
注:被调用函数必须已经被定义,必须在调用语句之前被声明。
注:函数的调用和宏的替换差不多,函数的调用就是将函数的调用语句替换成被调用函数整个本身,在执行完一遍被调用函数之后,再返回函数的调用语句,继续往下执行。
注:可嵌套。
注:形参和实参是变量在特定情况下的特定称谓,如果想了解形参和实参的详细信息,请往上看"变量"。
注:可在函数定义开头添加static关键字,把函数定义成内部(静态)函数,只能被同一个文件内的其它函数调用。
注:可在函数定义开头添加extern关键字,把函数定义成外部函数,可以被其它文件内的函数调用。
注:C语言规定:函数默认为外部函数。
注:各种函数格式中的size_t、int、void*等前缀都是为了方便理解,在实际操作中是不会写出来的。
注:函数从用户使用的角度来说又分为两种
库函数:由C系统提供,用户无须定义,也不必在程序中作类型说明,只需在程序前包含有该函数原型的头文件即可在程序中直接调用。
下列带.h的皆为头文件,前面的CXX为C标准,只有编译器支持该C标准,才能包含该头文件。
用户定义函数:由用户按需要写的函数。对于用户自定义函数,不仅要在程序中定义函数本身,而且在主调函数模块中还必须对该被调函数进行类型说明,然后才能使用。
C89
assert.h断言
仅包含assert宏,它允许我们在程序中插入自我检查。一旦任何检查失败,程序就会被终止。
ctype.h字符分类
对单个字符进行操作,提供用于字符分类及大小写转换的函数。
isalnum
判断参数是否为字母或者数字
格式
int isalnum(int c)
注:字母或者数字,a~z,A~Z,0~9。
注:如果参数符合条件就返回非0的整数(真),不符合条件则返回0(假)。
isalpha
判断参数是否为字母
格式
int isalpha(int c)
注:字母a~z或A~Z。
注:如果参数符合条件就返回非0的整数(真),不符合条件则返回0(假)。
iscntrl
判断参数是否为控制字符
格式
int iscntrl(int c)
注:控制字符:换页‘\f’,换行'\n',回车‘\r’,制表符'\t',垂直制表符'\v’等。
注:如果参数符合条件就返回非0的整数(真),不符合条件则返回0(假)。
isdigit
判断参数是否为十进制数字
格式
int isdigit(int c)
注:十进制数字0~9。
注:如果参数符合条件就返回非0的整数(真),不符合条件则返回0(假)。
isgraph
判断参数是否为任何图形字符
格式
int isgraph(int c)
注:如果参数符合条件就返回非0的整数(真),不符合条件则返回0(假)。
islower
判断参数是否为小写字母
格式
int islower(int c)
注:小写字母a~z。
注:如果参数符合条件就返回非0的整数(真),不符合条件则返回0(假)。
isprint
判断参数是否为任何可打印字符
格式
int isprint(int c)
注:任何可打印字符,包括图形字符和空白字符。
注:如果参数符合条件就返回非0的整数(真),不符合条件则返回0(假)。
ispunct
判断参数是否为标点符号
格式
int ispunct(int c)
注:标点符号,任何不属于数字或者字母的图形字符(可打印)。
注:如果参数符合条件就返回非0的整数(真),不符合条件则返回0(假)。
isspace
判断参数是否为空白字符
格式
int isspace(int c)
注:空白字符:空格‘ ’,换页‘\f’,换行'\n',回车‘\r’,制表符'\t',垂直制表符'\v'等。
注:如果参数符合条件就返回非0的整数(真),不符合条件则返回0(假)。
isupper
判断参数是否为大写字母
格式
int isupper(int c)
注:大写字母A~Z。
注:如果参数符合条件就返回非0的整数(真),不符合条件则返回0(假)。
isxdigit
判断参数是否为十六进制数字
格式
int isxdigit(int c)
注:十六进制数字,包括所有十进制数字,小写字母a~f,大写字母A~F。
注:如果参数符合条件就返回非0的整数(真),不符合条件则返回0(假)。
tolower
将大写字母转换为小写。
格式
int tolower( int c)
toupper
将小写字母转换为大写。
格式
int toupper( int c)
errno.h错误码
提供了errno(“error number”)。errno是一个左值(lvalue),可以在调用特定库函数后进行检测,从而判断调用过程中是否有错误发生。
float.h浮点数宏定义
定义了与浮点数类形相关的宏。这些宏提供了浮点数的特性和限制,例如最大值、最小值、精度等。
FLT_MANT_DIG
float类型的尾数位数
FLT_DIG
float 类型的最少有效数字位数(十进制)
FLT_MIN_10_EXP
带全部有效数字的float类型的最小负指数(以10为底)
FLT_MAX_10_EXP
float 类型的最大正指数(以10为底)
FLT_MIN
保留全部精度的float类型最小正数
FLT_MAX
float类型的最大正数
FLT_EPSILON
1.00和比1.00大的最小float类型值之间的差值
limits.h最大值与最小值
提供int、short、char、long类型最大值与最小值的宏定义,包括有符号与无符号。其中无符号最小值等于0,有符号最小值等于最大值相反数加1。
locale.h本地化
提供一些函数来帮助程序适应针对某个国家或地区的特定行为方式。例如日期、时间、货币、数字格式等。
math.h数学运算
提供常用的数学运算,包括:正弦/余弦/正切的三角函数、指数函数、对数函数、取绝对值、取模运算、向上取整、向下取整等。
基本算术运算函数
fabs
计算浮点数的绝对值
格式
double fabs(double x)
fabs(参数1)
fmod
计算两个浮点数相除的余数
格式
double fmod(double x, double y)
fmod(参数1,参数2)
fmin
返回两个浮点数中的较小值
格式
double fmin(double x, double y)
fmin(参数1,参数2)
fmax
返回两个浮点数中的较大值
格式
double fmax(double x, double y)
fmax(参数1,参数2)
幂函数和指数函数
pow
计算幂
格式
double pow(double x, double y)
pow(参数1,参数2)
计算以"参数1"为底的"参数2"次方值,然后将结果返回。
注:参数1和参数2必须为数字。
注:在使用pow函数时,要注意底数和指数数字的正负,不要违反基本的数学常识。
sqrt
计算平方根
格式
double sqrt(double x)
sqrt(参数1)
cbrt
计算立方根
格式
double cbrt(double x)
cbrt(参数1)
exp
计算自然指数
格式
double exp(double x)
exp(参数1)
计算以"e(自然对数的底数)"为底的"参数1"次方值,然后将结果返回。
对数函数
log
计算自然对数
格式
double log(double x)
log(参数1)
log10
计算以10为底的对数
格式
double log10(double x)
log10(参数1)
log2
计算以2为底的对数
格式
double log2(double x)
log2(参数1)
log1p
计算1 + x的自然对数
格式
double log1p(double x)
log1p(参数1)
三角函数和反三角函数
sin
计算正弦
格式
double sin(double x)
sin(参数1)
cos
计算余弦
格式
double cos(double x)
cos(参数1)
tan
计算正切
格式
double tan(double x)
tan(参数1)
asin
计算反正弦
格式
double asin(double x)
asin(参数1)
acos
计算反余弦
格式
double acos(double x)
acos(参数1)
atan
计算反正切
格式
double atan(double x)
atan(参数1)
atan2
计算两个变量的反正切
格式
double atan2(double y, double x)
atan2(参数1,参数2)
双曲函数和反双曲函数
sinh
计算双曲正弦
格式
double sinh(double x)
sinh(参数1)
cosh
计算双曲余弦
格式
double acosh(double x)
cosh(参数1)
tanh
计算双曲正切
格式
double tanh(double x)
tanh(参数1)
asinh
计算反双曲正弦
格式
double asinh(double x)
asinh(参数1)
acosh
计算反双曲余弦
格式
double acosh(double x)
acosh(参数1)
atanh
计算反双曲正切
格式
double atanh(double x)
atanh(参数1)
其他数学函数
ceil
向上取整
格式
double ceil(double x)
ceil(参数1)
floor
向下取整
格式
double floor(double x)
floor(参数1)
round
四舍五入
格式
double round(double x)
round(参数1)
trunc
截断小数部分
格式
double trunc(double x)
trunc(参数1)
hypot
计算直角三角形的斜边长度
格式
double hypot(double x, double y)
hypot(参数1,参数2)
将参数1和参数2分别平方再相加,再将这个结果平方根。
setjmp.h长跳转
提供长跳转指令功能,与函数内的短跳转goto作用类似。
提供了setjmp函数和longjmp函数。setjmp函数会“标记”程序中的一个位置,随后可以用longjmp返回被标记的位置。 这些函数可以用来从一个函数跳转到另一个(仍然在活动中的)函数中,而绕过正常的函数返回机制。 setjmp函数和longjmp函数主要用来处理程序执行过程中出现的严重问题。
signal.h信号
提供了用于处理异常情况(信号)的函数,包括中断和运行时错误。
stdarg.h可变参数
提供了一些工具用于编写参数个数可变的函数。
提供可变参数的遍历,由va_start()、va_arg()和va_end()三个函数组成,还有一个va_list可变参数列表。
stddef.h常用定义
定义了一些有用的类型和宏,这些类型和宏在C编程中非常常用。
stdio.h输入/输出
提供了大量的输入/输出函数,
注:各编译器提供不同版本的printf和scanf函数,在程序调用printf和scanf时,若无头文件stdio.h,部分编译器会自动生成隐式函数声明来让编译通过,在实际操作中也能使用这两个函数。
C90和C99规定了这两个函数的标准版本,为了程序的稳定性和可移植性着想,最好还是加上头文件stdio.h。
注:各种数据在计算机中存储时都是以二进制形式存储的,转换说明的作用是翻译,将同一组二进制数字以不同的进制或形式输出。
printf
输出(I)函数
转换说明
a
(C99/C11)浮点数、十六进制数和p记数法
A
(C99/C11)浮点数、十六进制数和p记数法
c
以字符形式输出,只输出单个字符。
d
有符号十进制整数
e
浮点数,e记数法
E
浮点数,e记数法
f
浮点数,十进制记数法
g
根据值的不同,自动选择%f或%e。%e格式用于指数小于-4或者大于或等于精度时。
G
根据值的不同,自动选择%f或%E。%E格式用于指数小于-4或者大于或等于精度时。
i
有符号十进制整数(与%d相同)
o
无符号八进制整数
p
指针
s
字符串
u
无符号十进制整数
x
无符号十六进制整数,使用十六进制数0f
X
无符号十六进制整数,使用十六进制数0F
%
打印一个百分号
修饰符
标记
-
待打印项左对齐。即从字段的左侧开始打印该项
示例:"%-20s"
+
有符号值若为正,则在值前面显示加号;若为负,则在值前面显示减号
示例:"%+6.2f"
空格
有符号值若为正,则在值前面显示前导空格(不显示任何符号):若为负,则在值前面显示减号+标记并覆盖空格
示例:"% 6.2f"
#
把结果转换为另一种形式。如果是%o格式,则以0开始;如果是%x或%X格式,则以0x或0X开始; 对于所有的浮点格式,#保证了即使后面没有任何数字,也打印一个小数点字符。对于%g和%G格式,#防止结果后面的0被删除。
示例:"%#o”、"%#8.0f"、"%+#10.3e"
0
对于数值格式,用前导0代替空格填充字段宽度。 对于整数格式,如果出现-标记或指定精度,则忽略该标记。
示例:"%010d"和"%08.3f"
数字
最小字段宽度:如果该字段不能容纳待打印的数字或字符串,系统会使用更宽的字段。
示例:"%4d"
.数字
精度:对于%e、%E和%f转换,表示小数点右边数字的位数。对于%g和%G转换,表示有效数字最大位数。对于%s转换,表示待打印字符的最大数量。 对于整型转换,表示待打印数字的最小位数。如有必要,使用前导0来达到这个位数。只使用.表示其后跟随一个0,所以%.f和%.0f相同。
示例:"%5.2f"打印一个浮点数,字段宽度为5字符,其中小数点后有两位数字
h
和整型转换说明一起使用,表示short int或unsigned short int类型的值。
示例:"%hu"、"%hx"、"%6.4hd"
hh
和整型转换说明一起使用,表示signed char或unsigned char类型的值。
示例:"%hhu”、"%hhx"、"%6.4hhd"
j
和整型转换说明一起使用,表示intmax_t或uintmax_t类型的值。这些类型定义在stdint.h中。
示例:"%jd”、"%8jx"
l
和整型转换说明一起使用,表示long int或unsigned long int类型的值。
示例:"%ld"、"%lu"
ll
(C99)和整型转换说明一起使用,表示long long int或unsigned long long int 类型的值。
示例:"%lld"、"%llu"
L
和浮点转换说明一起使用,表示1ong double类型的值。
示例:"%Lf"、"%10.4Le"
t
(C99)和整型转换说明一起使用,表示ptrdiff_t类型的值。ptrdiff_t是两个指针差值的类型。
示例:"%td"、"%12ti"
z
(C99)和整型转换说明一起使用,表示size_t类型的值。size_t是sizeof返回的类型。
示例:"%zd"、"%12zd"
格式
int printf( const char *format, ... );
printf(“'%''修饰符''格式控制字符串'”,参数1,参数2,…)
格式控制字符串就是转换说明中的字符,告诉编译器你想以什么样的方式打印将相对应的参数打印出来。
例:int a = 5;printf(“%d”,a);运行,输出5
注:打印过程中只修改格式控制字符串
例:int a = 5;printf(“a = %d”,a);运行,输出a = 5
注:每一个参数都必须要对应一个格式控制字符串。
注:转换说明必须严格与相对应的参数的数据类型相匹配,
在调用函数时,数据通过虚实转换按照数据类型保存在一个名为栈的内存空间内,然后printf函数按照转换说明来提取数据并将其打印。
例:4个long形数据被传输到栈空间内,一共32个字节,printf按照%hd打印了4个short数据,使用了32个字节中的前八个字节。 也就相当于第一个lond型数据(用八个字节的二进制数字存储)被分割成了四个short型数据(每个两字节),剩下的24个字节根本没用到。
注:“format”表示格式字符串,“...”表示可变参数列表。
puts
用于输出字符串,并在字符串后附加换行符。
格式
int puts(const char *str)
puts(字符串)
sprintf
将格式化的字符串输出到一个字符数组中。
转换说明
%
印出百分比符号,不转换。
b
整数转成二进位。
c
整数转成对应的 ASCII 字元。
d
整数转成十进位。
f
整数转成浮点数。
默认保留小数点后6 位数字
“%m.nf“
m表示打印的宽度,n表示小数点后的位数。
o
整数转成八进位。
s
整数转成字符串。
x
整数转成小写十六进位。
X
整数转成大写十六进位。
格式
int sprintf(string format, mixed [args]...)
sprintf(参数,“格式控制字符串”,参数1,参数2,…)
将参数1/2/3/…以字符串类型赋给“参数”
注:str:指向要写入的字符串的指针。
format:包含格式控制符的字符串,用于指定输出的格式。
...:可变参数列表,根据格式化字符串中指定的格式进行输出。
fprintf
将数据写入到文件
格式
int fprintf( FILE *stream,const char *format,... )
fprintf(文件指针,格式控制字符串,字符/数字)
将“字符/数字”以“格式控制字符串”所示格式输入到“文件指针中”。
注:写入失败返回EOF
scanf
输入(O)函数
转换说明
c
把输入解释成字符
d
把输入解释成有符号十进制整数
e、f、g、a
(C99标准新增了a)把输入解释成浮点数
E、F、G、A
(C99标准新增了A)把输入解释成浮点数
i
把输入解释成有符号十进制整数
o
把输入解释成有符号八进制整数
p
把输入解释成指针(地址)
s
把输入解释成字符串。从第1个非空白字符开始,到下一个空白字符之前的所有字符都是输入。
u
把输入解释成无符号十进制整数
x、X
把输入解释成有符号十六进制整数
俢饰符
*
抑制赋值(详见后面解释)
数字
最大字段宽度。输入达到最大字段宽度处,或第1次遇到空白字符时停止
hh
把整数作为signed char或unsigned char 类型读取
ll
把整数作为long long或unsigned iong long 类型读取(C99)
h、l或L
"%hd"和"%hi"表明把对应的值存储为short int类型。 "%ho"、"%hx"和"%hu"表明把对应的值存储为 unsigned short int类型。 "%ld"和"%li"表明把对应的值存储为long类型。 “%o”、"%lx"和"%lu"表明把对应的值存储为unsigned long 类型。 "%le"、"%lf"和"%lg"表明把对应的值存储为double 类型。 在e、f和g前面使用L而不是l,表明把对应的值被存储为long double 类型。 如果没有修饰符,d、i、o和x表明对应的值被存储为int类型, f和g表明把对应的值存储为float类型。
j
在整型转换说明后面时,表明使用intmax_t或uintmax_t类型(C99)
z
在整型转换说明后面时,表明使用sizeof的返回类型(C99)
t
在整型转换说明后面时,表明使用表示两个指针差值的类型(C99)
格式
int scanf(const char *format, ...);
scanf(“'%''修饰符''格式控制字符串'”,参数1,参数2,…)
格式控制字符串就是转换说明中的字符,告诉编译器你输入的东西想解释成什么样。
例:int a = 5;scanf(“%d”,a);运行,输入10,回车,a的值就被修改成10了。
注:每一个参数都必须要对应一个格式控制字符串。
注:可以把普通字符放在格式字符串中。但除空格字符外的普通字符必须与输入字符串相同。
例:scanf("%daadd%dhjk,bb",&num1,&num2)
必须输入
数字 aadd 数字 hjk,bb
注:“format”是一个格式字符串,“...”表示可变参数列表。
如果用scanf()读取基本变量类型的值,在变量名前加上一个&;如果用scanf()把字符串读入字符数组中,不要使用&。
????????????
gets
用于从标准输入读取一行文本。
格式
char *gets(char *str)
gets(指向字符数组的指针)
注:读取输入直到遇到换行符,并把换行符替换为字符串结束标志 \0。
注:不会限制输入的字符数,因此存在缓冲区溢出的风险。
注:在 C11 标准中已被弃用。
fscanf
从文件中读取数据
格式
int fscanf(FILE *stream,const char *format,...)
fscanf(文件指针,格式控制字符串,&参数)
从“文件指针”中按“格式控制字符串”所示格式读取数据并存放到“&参数”中
注:不是随机读取,是读取文件中“参数”的数值,如果文件中没有,则为读取失败,返回 EOF(-1)。
fopen
打开文件
打开方式
“r”(只读) 为了输入数据,打开一个已经存在的文本文件,如果指定文件不存在则出错。
“w”(只写) 为了输出数据,打开一个文本文件,如果指定文件不存在则建立一个新的文件。
“a”(追加) 向文本文件尾添加数据,如果指定文件不存在则建立一个新的文件。
“rb”(只读) 为了输入数据,打开一个二进制文件,如果指定文件不存在则出错。
“wb”(只写) 为了输出数据,打开一个二进制文件,如果指定文件不存在则建立一个新的文件。
“ab”(追加) 向一个二进制文件尾添加数据,如果指定文件不存在则出错。
“r+”(读写) 为了读和写,打开一个文本文件,如果指定文件不存在则出错。
“w+”(读写) 为了读和写,建议一个新的文件,如果指定文件不存在则建立一个新的文件。
“a+”(读写) 打开一个文件,在文件尾进行读写,如果指定文件不存在则建立一个新的文件。
“rb+”(读写) 为了读和写打开一个二进制文件,如果指定文件不存在则出错。
“wb+”(读写) 为了读和写,新建一个新的二进制文件,如果指定文件不存在则建立一个新的文件。
“ab+”(读写) 打开一个二进制文件,在文件尾进行读和写,如果指定文件不存在则建立一个新的文件。
格式
FILE *fopen( const char *filename,const char *mode )
fopen( 文件名,打开方式 )
fclose
关闭文件
格式
int fclose ( FILE * stream )
fclose (文件名 )
getc
从 FILE* 指针指向的文件中读取1个字符
格式
int getc(FILE *stream)
getc (文件名)
fgets
从文件中读取数据
格式
int fputs(const char* str,FILE* stream)
fgets(参数, 变量大小, 文件指针)
从“文件指针”中读取最多“变量的大小”个字符到第“参数”中
注:读取失败返回EOF
在这个过程中,在还没读取够第二个参数指定大小的字符前,读取到换行符'\n'或者需要读取的流中已经没有数据了,则提前结束。 并把已经读取到的字符存储进第一个参数指定的容器地址中。
putc
一次写入一个字符到文件中
格式
int putc(int char,FILE *stream)
putc(字符,文件名)
将“字符”写入到“文件名”中
fputc
将字符串写入到指定的文件中
格式
int fputs(const char* str,FILE* stream)
fputc(字符串,文件指针)
将“字符串”写入到“文件指针中”
getchar
从标准输入(通常是键盘)读取一个字符
格式
int getchar (void)
getchar(参数)
注:getchar 读取输入缓冲区中的下一个字符,并在读取成功后将其从缓冲区中删除。
注:getchar返回字符的ASCII码值(整数)。
注:写入失败返回EOF
putchar
将一个字符输出到标准输出(通常是控制台)。
格式
int putchar(int character);
putchar(参数)
stdlib.h通用工具
提供内存分配与释放、随机数的生成、进程退出、执行命令行、字符串转数值类型、二分查找算法、快排算法、求绝对值等操作。
malloc
用于申请一块连续的指定大小的内存块区域,以void*类型返回分配的内存区域地址。
格式
void *malloc(unsigned size)
malloc(数字)
注:实现原理:链表连接所有空闲的空间,组成最终分配的空间。
注:返回类型为无类型(void)指针,也即是通用指针,本质上是内存地址,需要进行类型转换。
注:在使用完malloc函数开辟的空间之后,需要释放(free函数),否则会造成内存泄漏。
注:在使用malloc函数开辟的空间中,不要移动指针,否则可能出现申请的空间和释放的空间大小不一致。
free
释放不需要的内存空间。
格式
void free(void *ptr)
free(参数)
注:在申请内存之后,需要及时将不再使用的内存空间进行释放,否则会导致内存耗尽。
calloc
为n个元素的数组分配内存,每个元素长度为size个字节
格式
void *calloc(unsigned n,unsigned size)
calloc(数字1,数字2)
注:与malloc() 函数的区别:calloc()函数会将所有字节置0来初始化该内存空间
realloc
为数组分配空间之后,若发现数组过大或过小,realloc() 函数可以调整已申请内存的大小。
格式
void *realloc(void *ptr,unsigned size)
realloc(参数,数字)
ptr指向原先通过malloc()、calloc()或realloc()获得的内存块。
size为内存块的新大小。
注:当分配内存减小时,realloc()仅改变索引信息
注:当分配内存扩大时
当前内存后还有空间:直接扩展这段内存空间,realloc()函数返回原指针。
当前内存后空闲字节不够:使用堆中第一个能够满足这一要求的内存块,并将目前的数据复制到新位置,将原来的空间释放掉,返回新的内存块地址。
申请失败:返回NULL,此时,原来指针仍然有效。
注:当ptr为空指针时,realloc()函数作用等同于malloc()函数;
atoi
将数字字符的字符串转换为整型返回
格式
int atoi (const char * str)
atof(参数)
注:如果字符串从开头就有连续的空格字符,则跳过这些连续的空格字符,找到不是空格的字符。
注:如果跳过这些空格字符后的第一个字符不是数字字符,则直接返回0。
注:如果跳过这些空格字符后的第一个字符是数字字符,则从这个数字字符开始转换,并向后找连续的数字字符转换, 如果连续中断,找到不是数字字符的字符,则在此截断寻找,返回前面已经转换好的连续的数字字符字面整型值。 (这里截断向后寻找后,不管后面有没有数字字符函数都不管)
注:如果字符串全部为空格字符,返回0;如果为空字符串,返回0;
atof
把字符串转换成浮点数,直至遇到第一个空格。
格式
double atof(const char *nptr)
atof(参数)
itoa
将整数转换为字符串。
格式
char* itoa(int value,char* string,int radix)
itoa(数字,参数,数字)
value是要转换的整数值。
string是存储转换后值的字符数组。
radix代表进制,radix=10就是十进制,radix=2就是二进制。
注:第二个参数只能是char*,不能是string;
注:第二个参数在使用之前必须提前分配存储空间。
string.h字符串处理
提供了用于进行字符串操作(包括复制、拼接、比较及搜索)的函数以及对任意内存块进行操作的函数。
注:凡是字符串,结尾必须有终止符"/n"
strlen
计算字符串中字符的个数
格式
size_t strlen( const char *string )
strlen(参数)
注:strlen函数的返回值是size_t(unsigned int),是无符号的;
strcpy
将第二个字符串覆写到第一个字符串。
格式
char *strcpy( char *strDestination,const char *strSource )
strcpy (参数1,参数2)
strncpy
将第二个字符串覆写到第一个字符串,但它允许我们指定要覆写的字符数量。
格式
char *strncpy(char *dest,const char *src,size_t n)
strncpy(参数1,参数2,数字)
strcat
将第二个字符串复制添加到第一个字符串末尾。
格式
char *strcat( char *strDestination,const char *strSource )
strcat (参数1,参数2)
注:不可自己追加自己,需要这样做则使用strncat
strncat
将第二个字符串复制添加到第一个字符串末尾,但它允许我们指定要添加的字符数量。
格式
char * strncat ( char * destination,const char * source,size_t num )
strncat(参数1,参数2,参数3)
注:数字表明复制第二个字符串的前“数字”个字符到第一个字符串的末尾。
strcmp
比较字符串1与字符串2的大小
格式
int strcmp( const char *string1,const char *string2 )
strcmp(参数1,参数2 )
注:从字符串1和字符串2的首字符开始比较,比较它们的ASCII值, 如果相等就比较下一个字符的ASCII值(包括\0), 直到得出结果(两者比较到\0处后还相等,那就证明两字符串相等)。
注:如果字符串1大于字符串2,则返回大于0的值 ,如果字符串1小于字符串2,则返回小于0的值, 如果字符串1等于字符串2,则返回0。
strncmp
比较字符串1与字符串2前n个字符的大小
格式
int strncmp(const char* str1,const char* str2,size_t num)
strncmp(参数1,参数2,参数3)
注:数字表明比较字符串1与字符串2前“数字”个字符的大小
注:从字符串1和字符串2的首字符开始比较,比较它们的ASCII值, 如果相等就比较下一个字符的ASCII值(包括\0), 直到得出结果(两者比较到\0处后还相等,那就证明两字符串相等)。
注:如果字符串1大于字符串2,则返回大于0的值,如果字符串1小于字符串2,则返回小于0的值, 如果字符串1等于字符串2,则返回0。
strlwr
将字符串中的字符转换为小写
格式
char *strlwr(char *str)
strlwr(参数)
注:此函数不是标准库函数,只能在windows下(VC、MinGW等)使用,Linux GCC中需要自己定义。
strupr
将字符串中的字符转换为大写
格式
char *strupr(char *str)
strupr(参数)
注:此函数不是标准库函数,只能在windows下(VC、MinGW等)使用,Linux GCC中需要自己定义。
strchr
在一个字符串中查找给定字符的第一个匹配之处,并返回指向该字符的指针。
格式
char *strchr(const char *str,int c)
strchr(参数1,参数2)
str:要查找的字符串,必须是一个以空字符 ‘\0’ 结尾的字符数组,也就是 C 语言中的字符串类型。
c:要查找的字符,必须是一个整型值,通常是一个字符常量或变量,例如 ‘a’ 或 ch。 如果 c 是一个多字节字符,例如汉字,那么只会查找其第一个字节。
注:对于大小写敏感,会区分字符 ‘a’ 和 ‘A’。
注:对于多字节字符的支持不完善,也就是说,它只会查找多字节字符的第一个字节。
注:如果没有找到该字符,则返回 NULL。
strrchr
在给定的字符串中从末尾开始搜索指定的字符,返回一个指向该字符最后一次出现的指针。
格式
char *strrchr(const char *str,int c)
strrchr(参数1,参数2)
const char *str:这是一个指向字符串的指针,表示要搜索的字符串。该字符串以空字符(\0)结尾。
int c:这是要查找的字符的ASCII码值。函数会在字符串str中搜索与该ASCII码值对应的字符。
注:对于大小写敏感,会区分字符 ‘a’ 和 ‘A’。
注:对于多字节字符的支持不完善,也就是说,它只会查找多字节字符的第一个字节。
注:如果字符在字符串中不存在,则返回 NULL。
strpbrk
在目标字符串s1中查找任意一个包含在字符串s2中的字符首次出现的位置,并返回指向该位置的指针;
格式
char *strpbrk(const char *s1,const char *s2)
strpbrk(参数1,参数2)
注:如果没有找到,则返回空指针。
strspn
返回字符串中第一个不在指定字符串中出现的字符下标
格式
size_t strspn(char const *str,char const *grouo)
strspn(参数1,参数2)
注:从str的第一个元素开始往后数,看str中是不是连续往后每个字符都在group中可以找到。到第一个不在gruop的元素为止。
注:返回值类型为整型,大小为停止时在str中已对比过的元素个数。
strstr
用于查找子字符串在主字符串中的第一次出现
格式
char *strstr(const char *haystack,const char *needle)
strstr(参数1,参数2)
haystack:指向要搜索的主字符串的指针。
needle:指向要查找的子字符串的指针。
注:返回一个指向haystack中第一次出现needle的指针。如果未找到子字符串,则返回NULL。
strtok
分解长字符串
格式
char *strtok(char *str, const char *delim)
strtok(参数1,参数2)
str—要被分解的字符串
delim—用作分隔符的字符。 (可以是一个,也可以是集合)
strtok()的第二个参数delim可以是所有分隔符的集合,比如{aaa - bbb ; ccc | ddd},第二个参数delim可以是{-;|},用一个或多个分隔符去分解字符串都可以。
注:该函数返回被分解的第一个子字符串,若无可检索的字符串,则返回空指针。
注:第一次调用strtok(),传入的参数str是要被分割的字符串{aaa - bbb -ccc},而成功后返回的是第一个子字符串{aaa}。
而第二次调用strtok的时候,传入的参数应该为NULL,使得该函数默认使用上一次未分割完的字符串继续分割,就从上一次分割的位置{aaa-}作为本次分割的起始位置,直到分割结束。
注:在这个函数里strtok()在分解字符串的时候,第一个参数str是在不断变化的,这个函数是在改变原字符串, 把原字符串{aaa - bbb - ccc}通过第二个参数delim将所有的分割符{-}替换成字符串结束标志字符{\0},则原字符串变化为{aaa \0 bbb \0 ccc}。
注:当strtok分解的字符串首字符就是分隔符,那么strtok()会忽略首个分隔符,直接从第二个分隔符往下继续分解,例如:{- aaa - bbb - ccc} 那么strtok()会忽略第一个{-},还是以{aaa - bbb - ccc}的字符串形式继续分解。
memset
将已开辟内存空间 s 的首 n 个字节的值设为值 c。
格式
void *memset(void *s,int c,size_t n)
memset(参数,数字1,数字2)
memcpy
从源头指向的内存块拷贝固定字节数的数据到目标指向的内存块.
格式
void * memcpy ( void * destination,const void * source,size_t num )
memcpy(参数1,参数2,数字)
注:源头的内存块与目标内存块不可以重叠。
注:能够作用于任何类型的数据包括(浮点型,结构体等等)。
注:与strcpy()函数不同,不会检查任何终止字符(如'\0'),而总是精确的拷贝参数传入的字节数。
memmove
从源头指向的内存块拷贝固定字节数的数据到目标指向的内存块。
格式
void * memmove ( void * destination,const void * source,size_t num )
memmove(参数1,参数2,数字)
从参数2拷贝“数字”个字节的数据到参数1。
注:源头的内存块与目标内存块可以重叠。
memcmp
比较参数1和参数2前n个字节数据的大小
格式
int memcmp(const void* dest,const void* rsc,size_t count)
memcmp(参数1,参数2,数字)
注:从参数1和参数2的首个字节数据开始比较,比较它们的ASCII值, 如果相等就比较下一个字节数据的ASCII值(包括\0), 直到得出结果(两者比较到\0处后还相等,那就证明两参数相等)。
注:如果参数1大于参数2,则返回大于0的值,如果参数1小于参数2,则返回小于0的值, 如果参数1等于参数2,则返回0。
bzero
将字符串s的前n个字节置为0
格式
void bzero(void *s,int n)
bzero(参数,数字)
注:此函数为废弃函数,部分编译器不支持。
time.h系统时间
提供相应的函数来获取时间(和日期)、操纵时间,以及格式化时间的显示。
C99
complex.h复数运算
定义了一组用于复数操作的宏、类型和函数。
fenv.h浮点数环境
定义了浮点数环境控制函数、异常控制函数、舍入方式控制函数、浮点数异常码和舍入方式等。提供了对浮点状态标志和控制模式的访问。
inttypes.h整数类型
提供整数类型的宏定义,定义了可用于<stdint.h>中声明的整数类型输入/输出的格式化字符串的宏,还提供了处理最大宽度整数的函数。
宏
CHAR_BIT
char 类型的位数
通常为 8
CHAR_MIN
char 类型的最小值(有符号或无符号)
-128 或 0
CHAR_MAX
char 类型的最大值(有符号或无符号)
127 或 255
SCHAR_MIN
signed char 类型的最小值
-128
SCHAR_MAX
signed char 类型的最大值
127
UCHAR_MAX
unsigned char 类型的最大值
255
SHRT_MIN
short 类型的最小值
-32768
SHRT_MAX
short 类型的最大值
32767
USHRT_MAX
unsigned short 类型的最大值
65535
INT_MIN
int 类型的最小值
-2147483648
INT_MAX
int 类型的最大值
2147483647
UINT_MAX
unsigned int 类型的最大值
4294967295
LONG_MIN
long 类型的最小值
-9223372036854775808L
LONG_MAX
long 类型的最大值
9223372036854775807L
ULONG_MAX
unsigned long 类型的最大值
18446744073709551615UL
LLONG_MIN
long long 类型的最小值
-9223372036854775808LL
LLONG_MAX
long long 类型的最大值
9223372036854775807LL
ULLONG_MAX
unsigned long long 类型的最大值
18446744073709551615ULL
iso646.h备选拼写
C99标准新增了可代替逻辑运算符的拼写,它们被定义在ios646.h头文件中。
传统写法
&&
&=
&
||
|
|
~
!
!=
^
^=
ios646.h
and
and_eq
bitor
or
or_eq
bitor
compel
not
not_eq
xor
xor_eq
stdbool.h布尔值
定义了bool代表_Bool,true代表1,false代表0。
stdint.h精确整数
声明了指定位宽的整数类型,并定义了相关的宏(例如指定每种类型的最大值和最小值的宏),同时也定义了用于构建具体类型的整型常量的带参数的宏。
tgmath.h
在C99中,<math.h>和<complex.h>头中的许多数学函数有多个版本。 <tgmath.h>头(27.5节)中的泛型宏可以检测传递给它们的参数类型, 并替代为相应的<math.h>或<complex.h>中函数的调用。
wchar.h
提供了一组函数和宏,用于处理宽字符和多字节字符。
wctype.h
提供了用于分类宽字符和在修改宽字符的函数和宏。
C11
stdalign.h
定义了四个宏
stdatomic.h
定义了现有数据类型的原子类型,并提供了大量的宏用于执行原子类型变量的初始化和读写操作。
stdnoreturn.h
定义了宏noreturn(被定义为_Noreturn)。
threads.h
提供了线程的创建和管理函数,以及互斥锁、条件变量和线程局部存储的功能。
uchar.h
定义了新的宽字符类型char16_t和char32_t,并提供了从多字节字符到这些宽字符类型的转换函数。
windows平台
C 运行时库(C Runtime Library)
包括 msvcrt.lib 和 msvcr*.dll(*代表版本号),提供了 C 语言的基本运行时支持,如内存管理、输入输出、字符串操作等。
Windows API
一组 Microsoft 提供的编程接口,它允许开发人员与 Windows 操作系统进行交互。 这些 API 可用于管理窗口、文件、进程、线程、图形、网络、安全、系统管理等各种功能。
包括一系列的库文件和动态链接库(DLL),提供了与 Windows 操作系统交互的函数和数据结构。
Windows.h
是 Windows API 的主要头文件,它包含了大量的宏、类型定义、函数声明等。 实际上,Windows.h 自身并不直接定义所有函数,而是包含了许多其他头文件,这些头文件中定义了各种 Windows API 函数。
sleep
让当前线程暂停执行一段时间
格式
unsigned int sleep(unsigned int seconds)
sleep(数字)
将程序暂停"数字"秒
注:sleep 函数的参数类型为 unsigned int,而不是浮点数类型,所以无法暂停小于一秒的时间。
其他库
除了 Windows API,还有许多其他的第三方库可以用于 Windows 平台的 C 语言开发,如开源的库、图形库、数据库访问库等。
Linux平台
C 运行时库(C Runtime Library)
Linux 上的 C 运行时库通常称为 GNU C 库(GNU C Library,简称 glibc)。它是标准的 C 库,提供了 C 语言的基本运行时支持。
POSIX 库
POSIX 是一个定义了操作系统接口标准的系列规范,其中包含了许多与操作系统交互的函数和数据结构。
动态链接库(Dynamic Linking Libraries)
Linux 上许多库以动态链接库的形式存在,
开源库
Linux 是开源操作系统,拥有丰富的开源库可以供开发者使用。
注:以下为Linux平台独有的库,但具体是哪一类的就不知道了。
unistd.h
是 C 和 C++ 程序设计语言中提供对 POSIX 操作系统(可移植系统)接口 API 的访问功能的头文件的名称。 Unix和Linux都遵循POSIX这个标准。
为Linux/Unix系统中内置头文件,包含了许多系统服务的函数原型,例如read函数、write函数、getopt函数和getpid函数等。
其作用相当于windows操作系统的"windows.h",是操作系统为用户提供的统一API接口,方便调用系统提供的一些服务。
sleep
让当前线程暂停执行一段时间
格
unsigned int sleep(unsigned int seconds)
sleep(数字)
将程序暂停"数字"秒
注:sleep 函数的参数类型为 unsigned int,而不是浮点数类型,所以无法暂停小于一秒的时间。
fcntl.h
提供了对文件描述符的操作,如打开、关闭、读取和写入文件。
sys/types.h
定义了一些基本的系统数据类型,
sys/stat.h
文件状态的头文件,它提供了一组用于获取和修改文件或文件系统状态信息的接口。 允许程序查询文件或目录的状态信息,包括:设备id、文件大小、文件访问权限、文件修改时间、文件访问时间、序列号、文件链接的数量等。
sys/socket.h
用于网络编程,提供了与套接字相关的函数和数据结构。
sys/time.h
提供了时间和日期相关的函数和定义,如获取当前时间、计时器等。
sys/wait.h
用于进程间通信,提供了等待子进程状态变化的函数和宏。
pthread.h线程操作
POSIX提供的线程,包括线程、互斥锁和条件变量三个部分。
signal.h
用于信号处理,提供了处理进程间通信和异常处理的函数和定义。
dlfcn.h库操作
动态链接库函数库的头文件,它提供了一组用于在运行时动态加载和链接共享库(动态链接库或共享对象)的接口。 允许程序在运行时动态地加载库、获取库中的函数地址以及卸载库。
sys/mmap.h
内存映射的头文件,它提供了一组用于内存映射文件或设备的接口。 允许程序将文件或其他对象映射到进程的地址空间中,从而可以直接通过指针访问这些对象的内容。
语句
表达式语句
表达式语句就是在表达式的后边加上分号。
赋值语句
由一个赋值表达式加一个分号组成。
函数调用语句
函数调用的时候,也会加上分号,就是函数调用语句。
控制语句
控制语句用于控制程序的执行流程,以实现程序的各种结构方式,它们由特定的语句定义符组成。
条件判断语句(分支语句)
if语句
如图0-1-1_1、0-1-1_2、0-1-1_3
switch语句
如图0-1-2
循环执行语句
while语句
如图0-1-3
do while语句
如图0-1-4
for语句
如图0-1-5
注:入口条件循环
在循环的每次迭代之前检查测试条件
注:出口条件循环
在循环的每次迭代之后检查测试条件
转向语句
break语句
执行到此语句时,直接跳出循环,执行循环外的后续语句
goto语句
有,但在C语言中通常不使用
continue语句
执行到此语句时,忽略循环中的后续语句,并重新开始循环。
return语句
return语句的作用是终止一个函数的执行,结束当前代码块中return后的语句, 即return后的语句不再执行,从当前函数退出,为该函数返回一个指定的值。 如果值被省略, 或在函数内没有return语句被执行,则把值赋给调用当前函数的表达式。
复合语句
成对括号中的代码就构成一个代码块,也被称为复合语句。
空语句
本身只包含一个分号。空语句本身并不执行任何任务。它所使用的场合就是语法要求出现一条完整的语句, 但并不需要它执行任务。
注:语句(statement)是C程序的基本构建块。一条语句相当于一条完整的计算机指令。
注:在C中,大部分语句都以分号结尾。
注:可嵌套。
链表
一种常见的数据结构,它通过一系列节点(Node)来存储数据元素。
与数组不同,链表中的元素在内存中不是连续存储的,而是通过指针或引用链接在一起。
注:链表的基本结构包括节点和指针,节点通常包含两部分:数据域和指针域(或称为链接域)。
数据域用于存储数据元素,而指针域则用于指向链表中的下一个节点。
注:可以使用结构体来定义链表节点。
分类
单向链表
每个节点只有一个指针,指向下一个节点。
例
节点定义:使用结构体来定义链表节点。每个节点包含一个数据域(data)和一个指针域(next),其中指针域指向下一个节点。
typedef struct Node { int data; // 数据域 struct Node* next; // 指针域 } Node;
链表初始化:创建一个头节点(head),并将其指针域置为空(NULL),表示链表为空。
Node* initList() { Node* head = (Node*)malloc(sizeof(Node)); // 创建头节点 if (!head) { exit(1); // 内存分配失败,退出程序 } head->next = NULL; // 初始化头节点的指针域为空 return head; }
插入节点:在链表中插入节点时,需要指定插入位置和前一个节点。根据插入位置的不同,可以分为头插法(在链表头部插入节点)和尾插法(在链表尾部插入节点)。以下以尾插法为例,展示插入节点的实现方法。
void insertNode(Node* head, int data) { Node* newNode = (Node*)malloc(sizeof(Node)); // 创建新节点 if (!newNode) { exit(1); // 内存分配失败,退出程序 } newNode->data = data; // 设置新节点的数据域 newNode->next = NULL; // 初始化新节点的指针域为空 // 找到链表的最后一个节点 Node* cur = head; while (cur->next != NULL) { cur = cur->next; } // 将新节点插入到链表尾部 cur->next = newNode; }
删除节点:在链表中删除节点时,需要指定要删除的节点或其前驱节点。以下以删除指定值的节点为例,展示删除节点的实现方法。
void deleteNode(Node* head, int data) { if (head == NULL || head->next == NULL) { // 空链表或只有一个节点的链表 return; } // 如果要删除的节点是头节点 if (head->next->data == data) { Node* temp = head->next; // 暂存要删除的节点 head->next = temp->next; // 修改头节点的指针域,跳过要删除的节点 free(temp); // 释放要删除的节点的内存空间 return; } // 如果要删除的节点不是头节点 Node* cur = head; while (cur->next != NULL && cur->next->data != data) { // 找到要删除的节点的前驱节点 cur = cur->next; } if (cur->next == NULL) { // 未找到要删除的节点 return; } Node* temp = cur->next; // 暂存要删除的节点 cur->next = temp->next; // 修改前驱节点的指针域,跳过要删除的节点 free(temp); // 释放要删除的节点的内存空间 }
遍历链表:遍历链表时,可以从头节点开始,依次访问每个节点的数据域,直到遇到空指针为止。以下是一个简单的遍历链表的函数。
void traverseList(Node* head) { Node* cur = head->next; // 从头节点的下一个节点开始遍历 while (cur != NULL) { printf("%d ", cur->data); // 访问当前节点的数据域 cur = cur->next; // 移动到下一个节点 } printf("\n"); }
查找节点:在链表中查找特定值的节点时,通常需要从头节点开始遍历链表,逐个比较节点的数据域直到找到目标节点或遍历完整个链表。以下是一个简单的查找节点的函数实现:
Node* findNode(Node* head, int data) { Node* cur = head->next; // 从头节点的下一个节点开始遍历 while (cur != NULL && cur->data != data) { cur = cur->next; } return cur; // 如果找到目标节点,返回该节点;否则返回NULL }
反转链表:反转链表是一个常见的链表操作,它将链表中的节点顺序进行反转。以下是反转链表的函数实现:
Node* reverseList(Node* head) { Node* prev = NULL; Node* cur = head->next; Node* next = NULL; while (cur != NULL) { next = cur->next; // 保存当前节点的下一个节点 cur->next = prev; // 将当前节点的指针指向前一个节点 prev = cur; // 移动prev到当前节点 cur = next; // 移动cur到下一个节点 } head->next = prev; // 反转后,新的头节点是prev return head; }
链表排序:链表排序通常使用插入排序、归并排序等算法。由于链表不是连续存储的,所以一些针对数组的排序算法(如快速排序、堆排序)可能不适合直接应用于链表。以下是使用插入排序算法对链表进行排序的示例:
void insertionSortList(Node** head_ref) { Node* sorted = NULL; Node* current = *head_ref; Node* next = NULL; while (current != NULL) { next = current->next; // 保存下一个节点 // 将current节点插入到已排序链表sorted中 if (sorted == NULL || sorted->data >= current->data) { current->next = sorted; sorted = current; } else { Node* prev = NULL; while (sorted != NULL && sorted->data < current->data) { prev = sorted; sorted = sorted->next; } current->next = sorted; prev->next = current; } current = next; // 移动到下一个节点 } *head_ref = sorted; // 排序后的链表 }
双向链表
双向链表则包含两个指针,一个指向前一个节点,一个指向后一个节点;
循环链表
循环链表则是将链表的最后一个节点指向第一个节点,形成一个环形结构。
特点
动态分配:链表中的节点可以在运行时动态分配和释放,因此链表的大小可以根据需要动态调整。
插入和删除操作方便:在链表中插入或删除一个节点时,只需要修改相关节点的指针即可,不需要移动其他节点,因此操作效率较高。
空间利用率高:链表不需要像数组那样预先分配连续的内存空间,因此可以充分利用零散的内存空间。
没有越界问题:由于链表是通过指针链接的,因此不存在数组中的越界问题。
文件
文件是一个有序数据集,数据集的名称叫文件名。
文件分为两种
一种是普通文件,比如txt文件、C语言的源程序文件、头文件等等存在于磁盘上的;
另一种是设备文件,比如鼠标、键盘、显示器等等外部设备,都认为是一个文件。
文件名的含义
用C语言编写程序时,编写的内容被存储在文本文件中,该文件被称为源代码文件(source code file)。 大部分C系统,都要求文件名以.c结尾(如,wordcount.c和budget.c)。
在文件名中,点号(.)前面的部分称为基本名(basename),点号后面的部分称为扩展名(extension)。 因此,budget是基本名,c是扩展名。基本名与扩展名的组合(budget.c)就是文件名。
文件名应该满足特定计算机操作系统的特殊要求。 例如,MS-DOS是IBM PC及其兼容机的操作系统,比较老旧,它要求基本名不能超过8个字符。因此,刚才提到的文件名wordcount.c就是无效的DOS 文件名。 有些UNIX系统限制整个文件名(包括扩展名)不超过14个字符,而有些UNIX系统则允许使用更长的文件名,最多255个字符。Linux、Windows和Macintosh OS都允许使用长文件名。
文件名包含3部分:文件路径+文件名主干+文件后缀
注:文件的后缀名决定了一个文件的默认打开方式。
注:文件路径指的是从盘符到该文件所经历的路径中各符号名的集合。
例: c:\code\mycode\test.txt
这里的c:\code\mycode\就是文件路径,test就是文件名主干,.txt就是文件后缀。
程序文件
子主题
注:后缀为.c
目标文件
注:windows环境后缀为.obj
可执行程序
windows环境后缀为.exe
数据文件
文件的内容不一定是程序,而是程序运行时读写的数据,比如程序运行需要从中读取数据的文件,或者输出内容的文件。
浮动主题
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