导图社区 编程C语言知识框架学习笔记总结
编程C语言知识框架学习笔记总结,本图知识梳理清楚,有助于帮助您熟悉知识要点,加强记忆。
编辑于2022-11-02 11:23:47 广东编程C语言知识框架学习笔记总结
2基本概念
2.1编译和链接

库
标准输入/输出
stdio.h
函数
printf
scanf
函数
主函数
main()
2.2通用形式
指令
函数
语句
语法
语句以分号结尾
printf
字符串
双引号
换行
\n
2.3注释
/*text*/
 占用多行  注释符号单起一行  使用“盒形”  忽略盒形三边  “翼型注释” 
注释嵌套为非法

2.4变量与赋值
变量类型
int
float
比int
优
可以存储小数位
缺
比int存储空间大
比int算术运算慢
近似值
声明变量

变量类型
变量名
以分号结尾
声明与定义的区别
定义为变量预分配空间
声明可以重复使用
赋值
显示变量值
占位符
打印时显示多个变量值 
作用
打印时用来指明变量值显示的位值
int占位符
%d
float占位符
%f
默认显示小数位6位
%.nf显示小数点后n位
自定义变量值所显示的小数位数 
程序:计算箱子的空间重量

变量初始化

显示表达式的值
先列出表达式,再显示其值  在打印语句里直接显示表达式的值 
2.5scanf
读入整型量  读入浮点数据 
2.6常量
常量命名
宏定义
#define
宏定义常量

宏定义表达式

标识符
元素
字母
数字
下划线
开头规则
以字母开头
以下划线开头
特性
区分大小写
没有长度限制
关键字
2.8书写规范
记号

间隔符
不限制间隔符数量
空格符
制表符
换行符
布局原则

1. 语句可以划分在任意多行内
2. 记号间的空格应便于识别
3. 缩进有助于识别嵌套
4. 空行可以所程序划分成逻辑单元
3格式化输入输出
3.2printf
显示格式串
普通字符
转换说明
编译器限制
不检测转换说明与输出项的数量匹配

不检测转换占位符是否也输出项类型相符合
 
转换说明
%m.pX
m指最小字符宽度
如果要显示的数值比m个字符多,字符宽度会自动调整为需的在宽度
+m 向右靠齐
%4d将以“ 123”显示数“123”
-m 向左靠齐
%-4d将以“123 ”显示数字“123”
0m 剩余空位以0填充
.p指精度
依赖转换说明符
d表示十进制形式的整数
p说明可以显示的数字的最小个数
%.5d将以“00123”显示数字”123“
e表示指数形式的浮点数
p说明小数点后的数字位数
123.123 %.5e将以“1.2312e+02”显示数字“123.123”
f表示”定点十进制“式的浮点数
p说明小数点后的数字位数
1.23 %.5f将以”1.23000“显示数字”1.23"
g表示指数形式或定点十进制式的浮点数
p说明可以显示的有效数的最大位数
与f的差异
不显示尾随0
小数点后无数不显示小数点
转义序列
响铃符
\a
输出\a会发出一声鸣响
回退符
\b
输出\b光标往后退一个位置
制表符
\t
输出\t光标跳到下一个制表符停止的位置
换行符
\n
输出\n光标跳到新起一行的起始位置
自定义符
\
\"
\\
其它格式化输出
fprintf
输出到文件
sprintf
输出到连续空间或数组
3.3scanf
工作方法

工作本质
模式匹配函数
读入的格式串
转换说明
普通字符
空白符

其它字符
输入中必有面要有与格式串中相符的“其它字符”字符数据才可以正常读入 格式串是"%d/%d" 输入必须是" 12/ 34" “其它字符”前的空格不能被跳过 
结束标志
空格,如回车或跳格
指定宽度结束
遇到非法输入
其它格式化输入
fscanf
从文件读取
sscanf
从连续空间或数组输入
流
stdin
0
stdout
 
1
stderr
 
2
stdprn
4
文件描述符
4表达式
4.0算术运算符
一元运算符
一远算符仅一个操作数
+正号
-负号
二远运算符
二元运算符要求有两个操作数
加法类
+加法
-减法
乘法类
*乘法
/除法
%取余
/与%的注意
两个整数相/,结果去掉分数部份
1/2的值不是0.5,而是0
%要求操作数为整数
%要求操作数为整数,否则编译器无法编译
当/和%用于负数操作时,结果与具体实现有关
/ 如果两个数有一个为负,相除结果既可以向下取整,也可以向上取整 如:-9/7既可以等于-1,也可以等于-2 % 如果两个数有一个为负,取余的结果既可以为正,也可以为负 如:-9%7既可以等于-2,也可以等于2
4.1运算符的优先级及结合
优先级
最高优先级 + -(一元运算符) 次级 * / % 最低级 + -(二元运算符) 
分组
括号()
结合
当表达示中出现多个同级运算符时,使用结合规则
左结合
二元运算符

右结合
一元远算符

简单赋值运算符=
复合赋值运算符+=

4.2赋值运算符
简单赋值v=e
表达球v=e的赋值效果是求出表达式e的值,并将该值复值给v e可以是常量、变量和比较复杂的表达式
类型转换

嵌入式赋值

左值
左值是存储在内存中的对象,既不是常量也不是计算结果  “变量左值”
复合赋值
v+=e
"v += e"含义:先将右侧的表达式e的值与左侧的变量v相加,再把结果复制给左侧的变量v
v-=e
v*=e
v/=e
v%=e
4.3自增与自减运算符
自增++
前缀自增++i
立即自增
后缀自增i++
稍后自增
稍后自增 读到该表达式后,执行下一条语句前自增
例子
i = 1, j = 2, k = ++i + j++ 执行以上语句后i,j,k分别为2,3,4 i = 1, j = 2, k = i++ + j++ 执行以上语句后i,j,k分别为2,3,3
自减--
4.4表达式
a = b += c++ - d + --e / -f

子表达式
表达式的值依赖子表达式的计算顺序
a = 5 c = (b = a + 2) - (a = 1) 如果先计算(b = a + 2), c得出的结果为6 如果先计算(a = 1), c得出的结果为3 如何避免? 不要在子表达式中使用赋值运算符 使用一串分离的赋值表达式
位运算符
位逻辑反

~
位逻辑与

&
位逻辑或

|
位逻辑异或

^
右移
>>
如 a>>1 a的值向右移一位
左移
<<
如 b b的值向左移4位
5选择语句
5.0语句类型
简单语句
选择语句
if语句
switch语句
循环语句
while语句
do语句
for语句
跳转语句
break语句
continue语句
goto语句
复合语句
由几条语句组合成一条
空语句
不执行任何操作
5.1逻辑表达式
关系运算符
运算符号
大于>
小于<
大于或等于>=
小于或等于<=
结果值
0或1
优先级
低于算术运算符
结合方式
左结合
x 使用左结合等价于 (x
判等运算符
符号
等于==
不等于!=
结果值
0或1
优先级
低于关系运算符
结合方式
左结合
逻辑运算符
符号
一元
逻辑非!
二元
逻辑与&&
逻辑或||
结果值
0或1
1
表达式等于0,!表达式
表达式1和表达式2都非零,表达式1&&表达式2
表达式1或2中任一不为0,表达式1 || 表达式2
0
除以下三种为1之外的所有情况
优级先级
!的优先级与一元运算符一至
&&和||优先级低于关系与判等运算符
“短路计算”
“短路计算”即: 这些运算符首先计算左侧操作数的值,然后是右侧操作数的值; 如果表达式的值可以由左侧操作数的值单独推导出来,那么将不再计算右侧表达式的值。 例如: (i != 0)&& (j / i > 0) 右侧表达式的值基出左侧操作数的值,所以计算整个表达式的值得首先得出左侧操作数的值。 如果i不等于0,那么需要计算右侧操作数的值,以便确定整个表达式的真假 如果i等于0,则直接得出整个表达式为假,那就没必要计算右侧操作数的值了。
5.2if语句

通用格式
if (表达式) 语句 括号是必需的,它是if语句的组成部分,非表达式的。
==与=的区别
if (i == 0) 先测试i是否等于0 if (i = 0) 先赋值,再测试表达式的结果
判定变量范围
   
复合语句
 
else语句
 
if嵌套
嵌套任意层数
级联式if语句

悬空else
 else属于离它最近且还未有else匹配的if语句,所以此例中else属于最内屋的if语句。 为了使以下例子中的else属于最外层的if语句,可用{}将最外层之外的if语句全套起来,如下 
条件表达式
条件运算符
条件运算符是C语言中惟一要求三个操作数的运算符,所以也称为“三元运算符”
三元运算符
由?和:组成
条件运算符:由符号? 符号: 组成,两个符号必须按如下格式一起使用  (以上表达式的含意:如果表达式1成立,则表达式2,否则表达式3.   条件表达式可以使代码简小,但同时增加了阅读难度 例:简写return  例:简写printf函数 
switch语句

语句构成
控制表达式
可以是
整型
字符
不可以是
浮点
字符串
情况标号
常量表达式
语句
break
作用
跑出switch语句,执行switch后面的语句
没有break的情况
switch实际上是一种“基于计算的跳转”,没有break语句,在执行完一种情况后,程序控制“向下跳转”到一下种情况的第一句,而忽略下一种情况的情况标号。 
6循环
构成
循环体
控制表达式
语句类型
循环语句
while
先while再循环体
do
先循环体再do
for
6.1while语句

格式

多语句{}

无限循环
while(1)
除非循环体内包含跳出循环控制的语句(break、goto、return),否则while语句将永远循环下去
6.2do语句

格式

6.3for语句
格式

3个控制表达式
表达式1是初始化步骤,只执行一次
表达式2控制循环的终止
表达式3在每次循环的最后执行一个操作
惯用法
1,向下加 2,向下减 
省略表达式
省略第一个赋值控制表达式  省略第三个控制表达式  同时省略第一、第三个表达式 
逗号表达式
 
逗号运算符的优先级低于其它所有运算符
逗号表达式的值为最右边表达式的值
6.4退出循环
break
惯用法
循环中设置退出
例子:求素数(在循环体中间设置退出)  
只能跳出一层
continue
与break的区别
break
跳到循环体末尾之后
跳出循环
可用于switch和循环(while,do,for)
continu
跳到循环体结束前的一点
留在循环内
只能用于循环
例子
”要求程序在读入10个非零数后循环终止“  
goto

可以跳转到任意位置
可以跳出多层嵌套
空语句

语句只有分号

7基本类型
 
7.1整型
数值类型
整型
6种整型的每一种类型的表示范围因机不同而不同,但是所有编译器都要遵守以下标准: 1.每一种整型都要覆盖一个确定的最小取值范围 2.long int >= int >= short int 
短整型short int
无符号短整型unsigned short int
整型int
无符号整型unsigned int
长整型long int
无符号长整型unsigned long int
数值范围
16位机

32位机

浮点型
整型常量
类型
八进制常量
不能以0开头
十进制常量
必须以0开头
十六进制常量
以0x开头
常量中的字母可以是大小写
后缀

U
L
读/写整数
转换说明符
%m.pYX
X

%o
表示八进制
%u
表示十进制
%x
表示十六进制
Y

l
长型
h
短型
7.2浮点数
类型
单精度浮点数float
双精度浮点数double
扩展双精度浮点数long double
范围

float
1.17e-38~3.40e38
精度5位
double
2.22e-308~1.79e308
精度16位
浮点常量
必须包含小数点或指数
指数指明衡10的幂
读/写浮点数
转换说明符

float
%e
%f
%g
double float
%le
%lf
%lg
long double float
%Le
%Lf
%Lg
7.3字符型
字符集
ASCII
7位代码
128个字符
Unicode
16位代码
65536个字符
char
char赋值

单引号
字符操作
C语言按小整数处理字符
字符拥有和数相同的属性
 
字符有符号

转义序列

类型
字符转义序列
数字转义序列
可以表达任意字符
八进制转义序列

十六进制转义序列

转义序列作为常量
必须用单引号括起来

define转义序列常量

字符处理函数
toupper库函数

读写单字符
printf/scanf

scanf不跳过空白字符
  
强制scanf跳过空格
  
转换说明符
%c
putchar/getchar函数
快于printf/scanf
      
putchar/getchar设计简单,printf/scanf针于不同格式
putchar/getchar作为宏来实现
惯用法
跳过剩于行  跳过剩于空白符 
缓冲
全缓冲
非缓冲
7.4sizeof运算符
作用
充许程序确定用来存储指定数据类型值所需空间的大小
格式

值
无符号整数
strlen()函数
作用
求字符串的长度
用法
strlen("hello")
所属库
include<string>
7.5类型转换
隐式转换
算术或逻辑表达式中的操作数不同
常用算述转换策略
把操作数转换成可以安全适用于两个数值的“最狭小的”数据类型 (粗略的说,哪果某种类型要求的存储字节比另一种少,那这种类型就比另一种更狭小) 
提升
有任一浮点型操作数

无浮点型操作数

赋值运算符左右侧类型不同
赋值中的转换策略
将赋值运算右边的表达式转成左边变量的类型
变量类型至少和表达式一样宽的转换

其它情况则有问题
把浮点值赋值给整型变量,将丢失小数部份  取值在变量类型范围以外,赋值结果出错 
函数调用的参数与对应参数类型不同
return表达式的值与函数返回值类型不同
显示转换
强制转换
强制转换表达式

强制转换符优先级
当作一元运算符

使用强制转换避免溢出

7.6类型定义
typedef
可以用宏定义类型  也可以用typedef进行类型定义   编译器会把Bool看成是int的同义词
好处
便于理解
  
容易修改
提高可移植性
8数组
8.0变量
标量
保存单一数据项
聚合变量
存储数值的集合
数组
一维数组
多维数组
结构
8.1一维数组
定义及特性
定义
多个数据值的结构
所有数据值数据类型相同
特性
元素
元素定位选择
元素可以是任意类型
数组长度可以用任意(整数)常量表达式
变长数组
长度可以使用变量
允许动态分配存储单元
不允许声明初始化
数组长度的变化
  
声明数组
int a[10]
数组下标
又称数组索引 
数组是左值
数组的表达式格式是左值,所以数组可以和普通变量一样使用 
与for结合使用

数组初始化
赋值
常量表达式列表
  
列表用大括号括起来
列表数值以逗号分开
如果初始化式比数组短,剩余元素全为0
将所有元素赋同值
初始化式为空非法
初始化式过长非法
部分赋值
C99特性 int a[10] = {[7] = 7, [9] = 9}
指定初始化
用例
1.如果在一个指定初始化值项目的后面跟有不只一个值,则这些值将用来对指定初始化项目后续的数组元素初始化 2.如果多次对一个元素进行初始化,则最后一个有效  
sizeof数组
sizeof可以确定数组的大小(字节数)
求数组长度
数组清零  sizeof和宏定义 
8.2多维数组
基本格式
m[i][j]
多维数组赋值
初始化
   
多维数组变量赋值
内存中的存储方式
按照行主序存储

与for的合作
使用for嵌套处理多维数组
8.3柔性数组(零长数组)(C99)

编程
发牌
库函数
time函数
返回当前的时间,且这个时间被编码成单独的一个数
来自<time.h>
srand函数
初始化C语言的随机生成器
来自<stdlib.h>
rand函数
在每次调用时会产生一个显然随机的数
来自<stdlib.h>
9函数
9.1函数定义与调用
函数定义
  
声明不带返回值的函数

调用

void函数的调用
仅作为语句不能作为表达式
无形参的函数调用
  
9.2函数声明

另称“原型”
函数声明在main之前
函数定义在main之后
9.3实际参数
与形参的差异
形参
出现在函数定义中
假名
形参由实参初始化
形参跟实参是独立的
实参
出现在函数调用中
参数传递
从右到左
最右边的参数先入栈
实际参数的转换
编译器在调用前遇到原型
隐式的转换成相应形式参数的类型
编译器在调用前没有遇到原型
默认的实际参数提升
float升为double
整数提升
char升int
short升int
数组型实参
传递数组时只传递数组的首元素地址
一维数组
可以忽略长度
把数组长度作为额外的实际参数提供出来
定义、声明、调用
声明时可以忽略形式参数的名字

调用
     
重要论点

多维数组
只能忽略第一维的长度

传参
赋值传递
不能改变实参的值
地址传递
可以改变实参值
需要操作的实参
基本类型变量
改变实参值
形参定义
指针变量类型
如int *i
不改变实参值
形参定义
基本类型
如int i
指针变量
改变实参值
形参定义
二级指针变量
如int **i
不改变实参值
形参定义
指针变量
如int *i
9.4return语句
非void函数必须有return语句
return语句格式

表达式
常量
变量
更复杂的表达式
返回值与函数返回值不匹配
自动转换成函数返回值的类型
9.5程序中止
状态码
正常终止
0
非正常终止
非0
exit函数
属于stdlib.h
中止状态
正常终止
exit(0);
exit(EXIT_SUCESS);
异常终止
exit(1);
exit(EXIT_FAILURE);
9.6递归函数
如果函数调用它本身,那么这个函数就是递归的
 
递归函数组成
边界条件
边界出口
条件满足
递归返回
条件不满足
递归前进
递归前进段
递归返回段
用例
 
快速排序算法
使用递归函数
数组
分割数组
9.7可变参函数
定义
参数个数可变的函数
编写变参涵数涉及到的宏定义
va_list ap
创建一个指向涵数变参的指针
va_start(ap, A)
使指针指向第一个变参
va_arg(ap, T)
获取指针当前所指变参的值并使指针指向下一个变参
va_end(ap)
提示提取参数结束
变参函数示例

9.8回调函数

定义
维基百科: 回调函数,或简称回调,是指通过函数参数传递到其它代码的,某一块可执行代码的引用。 百度百科: 回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用为调用它所指向的函数时,我们就说这是回调函数。回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。
典型例子
void (*signal(int sig, void (*func)(int)))(int);
9.9内联函数
定义
内联函数(有时称作在线函数或编译时期展开函数)是一种编程语言结构,用来建议编译器对一些特殊函数进行内联扩展(有时称作在线扩展);也就是说建议编译器将指定的函数体插入并取代每一处调用该函数的地方(上下文),从而节省了每次调用函数带来的额外时间开支。但在选择使用内联函数时,必须在程序占用空间和程序执行效率之间进行权衡,因为过多的对较复杂的函数进行内联扩展将带来很大的存储资源开支。另外还需要非常注意的是对递归函数的内联扩展可能带来部分编译器的无穷编译。
用途
消除函数调用的时间开销
适用
频繁调用的小函数
11指针
11.1指针变量
变量的第一个地址为变量的地址
  
指针与指针变量的关系
指针就是指向变量的地址
指针变量只是存储地址的变量
声明指针变量
int *p
int *p 以上声明说明p是指向int型变量的指针变量 
作用
声明指针变量是为指针留出空间,并没有把它指向对象
初始化指针
int *p = &i
p = &i
  把&i赋值给p不会影响i的值
void *p
表示该指针不指向任何类型

11.2运算符
取地址运算符
&
作用
找到变量的地址
如果x是变量,那么&x就是x在内存中的地址
间接寻址运算符
*
作用
访问指针所指对象的存储内容
如果p指向i 那么*p等于i的值,也不是i的地址
读取
改写
不要
不要把*用于未初始化的指针变量
未初始化也不要给*p赋值
11.3指针赋值
针指复制
p = q
必须类型相同
p = &i
给*p赋新值可以改变i的变量值
赋值语句*p = *q是把q指向的值复制给p指向的内存单元
11.4指针作为实参
调用函数时传递指针变量的指针(即地址)
可以改变实参的值
11.5指针作为返回值
12指针和数组
12.1指针的算术运算
指针可以指哪儿
指向普通变量
指向数组元素
  
使用指针访问数组元素
int a[10], *p; p = &a[0]; *p = 5; (等同于a[0] = 5;) 
另称(地址算术运算)
指针加上整数
  
如果p指向a[i],那么p+j指向a[i+j]
指针减去整数
   
如果p指向a[i],那么p-j指向a[i-j]
两个指针相减
指针相减为指针间的距离
如果p指向a[i],q指向a[j],那么p-q = i -j
 
两指针指向同一数组时方可相减
指针比较
可用的比较运算符
关系运算符
<
<=
>
>=
判等运算符
==
!=
可用前提
两指针指向同一数组
结果依赖两指针所指元素在数组中的相对位置
  
12.2指针用于数组处理
重复访问数组元素
数组初始化
指针自增

*和++的组合使用
*p++(等同于*(p++))
把值存入数组元素,然后使用下标自增推进到下一个元素 可以使用: a[i++] = j; 同样也可以使用指针,假设指针p指向数组a 上面的语句等同于: *p++ = j;   
其它组合格式

*和--的组合使用
类似于*和++的组合使用
12.3用数组名作为指针
当然指针也可以作为数组名
p[i]等同于*(p+i)
 
数组名
值
首元素的首地址
类型
其元素的指针类型
可以用数组名作为指向数组第一个元素的指针
    
惯用法
   
12.4指针和多维数组
处理多维数组的元素
二维数组
第一维表示行
第二维表示列
C语言以行为主顺序存储二维数组

多维数组初始化
嵌套的for循环
 
使用指针
将多维数组看成一维数组
 
处理多维数组中的行
只在多维数组中的一行处理元素
使用指针指向二维数组a中的i行的第0元素
p = &a[i][0];
Subtopic
13字符串
13.1字符串字面量
字符串面量可以包含转义序列
8、16进制转义序列出现在字符串面量中合法,但不常用
延续字符串面量
以\结尾

同一行\末尾不跟其它字符
\可以分割任何长的符号
缺点
必须从下一行的起始位置开始
另一种方法

字符串面量的存储
C语言存储方式
作为字符数组
当C语言遇到长度为n的字符串字面量时,它会为字符串分配长度为n+1的内存空间,这块空间将用来存储该字符串字面量和额外的一个字符--空字符('\0')    
编译器将字符串字面量当作char *类型的指针
字符串字面量的操作
char *
 
添加下标
   例子 
单字符串字面量与字符常量的区别
字符串字面量"a"是用指针来表示的, 表示指向"a"的内存单元 而字符常量'a'是用整数来表示的。
“a"用指针表示
'a'用整数表示(ASCII)
13.2字符串变量
声明字符串变量用法
C语言
空以字符结尾的一维数组
 这里声明的字符串变量最大可以存储80个字符, 而这里定义的STR_LEN为80,然后对宏加1以便该变量可以容下80个字符
其它语言
使用string类型
初始化变量
声明+初始化
  以上声明看起来像是对字符串进行初始化,其实C语言编译器将其看作是数组初始化的缩写 
长度
小于声明长度
剩余的空位以\0填充

大于声明长度
非法
等于声明长度
不作为字符串使用
字符数组与字符指针
指向数组的指针变量才可以作为字符串变量

两者有差别
声明为数组date是数组,可以修改数组中的字符
声明为指针date是指针变量,不可以修改字符串字面量
13.3字符串的读写
写
printf
转换说明
%s
 printf逐个写字符直到遇到空字符,如果空字符丢失,越过字符串的末尾继续读直到在内存的某个地方找到空字符。
%m.ps
m指定宽度
p表示打印字符的数量

puts
只有字符串变量一个参数
没有格式串
写完即换行
读
scanf
转换说明
%s
不需要&
 因为str是数组名,scanf会自动将其作为指针(即地址)来处理
跳过字符串前的空白字符
使用scanf读取字符串将永远不会包含空白字符
读取终止
读到字符串后的空白符终止
读到换行符终止
读到制表符终止
读到空格终止
在末尾存储一个空白字符
gets
与scanf的差别
不同
gets不跳过字符串前的空白字符
gets读到换行符才终止
scanf无法读取整行,gets可以
相同
都在末尾存储一个空白字符
设计逐个字符读字符串的函数
例子

设计时要考虑的问题
是否跳过空白字符?
何时终止?
字符串太长无法存储怎么办?
13.4访问字符串中的字符
使用数组下标
统计字符串中的空格 
使用指针跟踪

13.5C语言的字符库
字符串的操作
不能用语C语言的运算符对字符串进行复制和比较的操作
复制
strcpy
原型
原型  1.函数将字符串s2复制给s1 2.函数返回s1 3.函数将不修改s2
用例
用例一  用例二 
比较
strcmp
原型
 1.如果s1小于s2,函数小于0 2.如果s1等于s2,函数等于0 3.如果s1大于s2函数大于0
拼接
strcat
原型
 1.函数将字符串s2追加到字符串s1的末尾 2.返回字符串s1
用例

求字符串长度
strlen
原型
 1.函数返回字符串s的长度
用例

size_t
无符号整型类型
#include<string.h>
13.6字符串惯用法
搜索字符串的结尾
复制字符串
本书中对此两函数的定义讲解很经典
13.7字符串数组
二维数组
 
浪费空间
指针
 
14预处理器
14.1预处理器的工作方式

预处理器的行为是由指令控制的
指令是以#开头的命令
#define
定义一个宏
别名
#include
告诉程序打开一个特定的文件
14.2预处理指令
处理指令类型
宏定义
#define
定义一个宏
#undef
删除一个宏定义
文件包含
#include
条件编译
#if
#indef
#ifndef
#elif
#else
#endif
作用
根据编译器可测试的条件来将一段文本块包含到或排除程序
指令规则
指令都以#开头
#前面可以有空格
指令符号间可插入任意数量的空格和横向制表符

指令总是在第一个换行符处结束,除非以/指明要继续

指令可以出现在程序的任何地方
通常放在文件开始的地方
注释可以与指令放在同一行

14.3宏定义
简单的宏定义

注意
不要在宏定义的任何位置放入额外的符号,否则它将成为宏定义的一部分
宏定义的优点
使程序更易读
使程序更易修改
避免前后不一致或键盘输入错误
自定义C语言语法
对类型重命名
控制条件编译
带参数的宏定义

用例

宏定义中的运算符
#
作用
将宏的参数转换成字符串字面量
使用范围
仅带参数的宏定义可使用
用例
 
##
作用
将两个记号粘贴成一个
用例
1
   
2
  
宏的通用属性
预处理器只替换完整的记号,而忽略记号的片断
预处理器会忽略包含在标识符名、字符常量、字符串字面量之中的宏  
宏定义作用范围到文件尾
宏不可以被定义两次,除非两次对宏的定义是一样的
宏可以使用#undef指令取消定义
 该指令会删除当前文件中对宏N的定义,如果N之前没有被定义,则 #undef没有作用
宏定义中的圆圆括号
必不可少的情形
宏带参数
替换列表中有运算符
创建较长的宏
使用逗号运算符;

使用{}

预定义宏

用例

14.4条件编译
作用
根据预处理器的测试结果来包含或排除程序片断
#if指令和#endif指令
如果常量表达式为0 那么#if到#endif之间的行在预处理过程中被删除 如果常量表达式为非0 #if和#endif之间的行被保留,#if和#endif对程序没有影响 
defined运算符
当#defined应用于标识符时 如果标识符是被定义过的宏,则返回1,否则返回0 #define经常与#if和#endif使用 
#ifdef和#ifndef
#ifdef
 
测试一个标识符是否已被定义为宏
#ifndef

测试一个标识符是否没有被定义为宏
使用条件编译
编写在多台机器或多种操作系统下移植的程序

编写可以使用不同编译器编译的程序

为宏提供默认定义
条件编译可以检测一个标识符是否被定义为宏,如果没有,则可以为它默认定义一个宏 
临时屏蔽包含注释的代码
由于标准C中注释不能嵌套,所以不能通过注释“注释掉”包含注释的代码 
15编写大规模程序
15.1源文件
程序
源文件
main涵数作为起点
把程序分割成多个源文件的好处
把相关的函数和变量集合在单独一个文件中有助于明了程序结构
可以单独对每一个文件进行编译
把函数集合在单独的源文件中方使在其它地方调用
15.2头文件
#include指令
作用
共享
外部变量
宏定义
函数原型
类型定义
........
指令格式
#include <文件名>
用于属于C语言自身库的头文件
#include <文件名>
用于所有其他文件(包括自己写的)
共享宏定义和类定义

共享函数原型

共享变量声明
extern
 extern提示编译器变最i是在程序中的其它位置定义的,不用为其分配空间 
extern可以用于所有类型变量
extern声明数组时可忽略长度,因为不必为其分配空间
Subtopic
嵌套包含
头文件中包含#include 指令
保护头文件
危险
一个源文件可能包含某头文件一次以上

保护头文件
使用#ifndef和#endif将头文件的内容包含起来

16结构、联合和枚举
16.1结构变量
结构定义
可能具有不同类型值的集合
结构与数组的区别
相同
都是聚合变量
不同
元素构成不同
数组成员数据类型相同
结构成员数据类型可能不同
成员定位方式不同
数组使用下标定位该成员位置
结构使用成员名定位某成员
结构与类的区别

结构
结构只可以包含数据
类
类可以包含方法
结构变量声明

结构变量在内存中的存储
 以下是抽像后的表示  
命名域
不同结构内成员名相同不冲突
结构变量初始化
声明时即可初始化
 
初始化式的表达式必须为常量
初始化式可以短于它初始化的结构
剩余成员值初始化为0
指定成员初始化(C99)

结构类型的大小
例1  例2 
sizeof(struct tom)
自然对齐
如果一个变量的超始地址是其长度的整数部,称为自然对齐
CPU的字长
CPU在单位时间内(同一时间)能一次处理的二进制数的位数叫字长
32bit
4字节
64bit
8字节
最小对齐格式(m值)

类型长度 < CPU字长:m=自然对齐
类型长度 >= CPU字长:m=CPU字长
结构类型的最小对齐格式m值
等于m值最大的成员的m值
对结构的操作
结构成员表示方式
结构名.成员名

句点运算符
优级先级
等同于后缀++和后缀--
结构成员的值为左值
可以赋值

可以自增自减

结构变量间的赋值
part1 = part2;
前提
类型一致的结构

同时声明的结构是一致的
使用同样“结构标记”和同样类型名声明的结构
注意
两个结构不可以使用==或!=进行判等运算
16.2结构类型
结构标记

用于标记某种特定类型结构的名称
使用结构标记声明结构变量
错误的写法
不能漏掉“struct"
结构类型的定义

结构类型的实际参数和返回值
结构类型作为函数的实际参数

给函数传递结构
结构类型作为函数的返回值

从函数返回结构
需使用结构所有成员的副本
可传递指向结构的指针 代替传递结构本身
16.3数组与结构的嵌套
数组与结构的组合没有限制
数组元素可以包含结构
结构成员可以包含数组
嵌套的结构
成员是结构的结构
 
结构数组
定义
具有结构元素的数组
用处
作为简单的数据库
声明
 声明具有结构part的数组  以上表示数组有100个结构为part类型的结构元素 下面访问数组中的某个结构元素,即某种零件:  下面访问(赋值)该数组中某个结构元素(即零件)的某个成员:  下面访问(赋值)该数组中某个结构元素(即零件)的某个成员中的单独字符 
结构数组的初始化

16.4联合
定义
一个或多个成员共用一个内存空间的数据类型
与结构的比较
相同
一个或多个成员构成,且数据类型可能不同
访问成员的方法相同
有成员名
使用句点操作符
声明方法一样
可用使用=进行复制操作
可以在函数中传递,也可以作为函数的返回值
不同
联合只为最大的成员分配内存空间

联合的初始化式与结构相同,但只有第一个成员可以获得初值

改变一个成员的值将改变其它所有成员的值
使用处
回头再看
互斥
16.5枚举
定义
枚举是由一种由程序列出值的类型,且必须为所有值命名(枚举常量)
作用
针对仅有少量可用值的变量特殊数据类型
声明
 
枚举的标记和类型
声明枚举类型
   
枚举作为整数
枚举是整型
C语言把枚举变量和常量作为整数处理
枚举常量赋值
默认赋值规则
如果枚举常量未赋值 第个枚举常量默认为0 未指定值的枚举常量的值是一个大于前一个常量的值
每一个枚举常量默认为0
未指定的枚举常量的值为比前一常量大1的值
枚举常量可以是任意整数
枚举常量的值不用按照特定顺序
枚举常量可以相同
枚举常量与整数的互用

枚举常量当作整数
把整数当作枚举常量
危险
合法
17指针的高级应用
17.1动态存储分配基础
内存分配函数
malloc
分配内存块,不初始化
calloc
分配内存块,清除内存块内容,并初始化
realloc
调整已分配的内存块
函数返回void *(即通用指针)
当调用内存分配函数时,因为不知道计划存储的数据为什么类型,所以返回的不是int型、char型或是其它类型指针,取而代之的是void *指针,该指针类型为通用指针,本质上只是内存地址。
空指针
当无法定位足够大的内存块时,返回空指针
判数返回值是否为空
  
17.2动态分配字符串
为字符串分配内存块
malloc
函数调用原型

用例
 char *p  执行复制操作时,会把malloc函数返回的通用指针自动转换成任意需要的指针类型 
注意
不要忘了给字符串末尾的空字符分配空间
p指向未初化的数组

字符串函数中使用动态存储分配
编写指向“新”字符串的函数
 
与strcat的差别
strcat将字符串s2拼接到s1上,原s1发生改变
这里的concat将s2和s2顺序复制到新字符串p里,原s1,s2不改变
17.3动态分配数组
好处
允许程序执行时为数组分配大小
通过指针访问数组
使用malloc为数组分配空间
        
calloc
原型

与malloc的区别
calloc将分配给数组的内存初始化为0
可以为非空数组分配空间
用例
  
将第一个参数设为1可使calloc给任何数据类型分配空间
    
realloc
作用
重新调整已分配内存空间的大小
原型

realloc规则
扩展内存块时,realloc不会初始化新添加的字节
realloc不能按要求扩展内存块,将返回空指针,原内存块中数据不变
realloc函数第一个参数为空指针时,其行为同malloc
realloc函数第二个参数为0时,释放该内存
17.4释放存储
堆
内存块的存储池
free函数
作用
释放并回收不再使用的内存块
原型

用例

“悬空指针”问题

17.5链表
定义
链表是一连串结构(结点)组成的,其中每个结点包含指向链中下一结点的指针  
作用
存储数据项的集合
与数组相比
优点
可以在链表中插入、删出结点
可以根据需要扩大或缩小
缺点
无法像数组“随机访问”链中的结点
数组通过下标访问元素,访问任何元素所需的时间相同 而对于链表来说,越靠近起点的结点访问越快,越靠近末尾访问越慢
声明结点类型
       
声明结点类型的前提
需要一个表示表中单个结点的结构
创建结点
三步
为了创建一个结点,需要一个变量临时指向该结点, 直到该结点插入链表为止。 把此变量设为new_node:  使用malloc为新结点分配内存单元,并且把返回值保存到new_node中  现在new_node指向了一个内存块,且该内存块正好放下一个struct node型结构    
->右箭头选择运算符
        
1. 为结点分配内存单元
2. 把数据存储到结点中
3. 把结点插入到链表中
插入结点
在链表起始处插入结点

插入结点的函数
 
搜索链表
使用循环
for

链表搜索函数
 另一种写法,去掉指针变量p,使用list本身来跟踪当前结点 因为list是原始链表指针的副本,所以list在函数中发生改变不影响原链表  因为list链表末尾本身为空,所以找不到n返回list函数同样正确  使用while精简该函数 
while
删除结点
步骤
定位结点

改变前一个结点,绕过“删除”结点

释放“删除”结点的内存空间

17.6指向指针的指针
用途
17.7指针与函数
函数指针
指向函数的指针
定义及声明
函数就是指针
(*p)(char, float)

引用

作用
实现面向对象的思想
实现软件分层设计(回调函数)
指针函数
定义
int *f(void)

17.8指针与数组
数组指针

指针数组

17.9函数指针数组

18.声明
18.1声明的语法
 函数 
构成
存储类型
auto
static
extern
register
最多允许使用一个
类型限定符
const
volatile
允许使用一个、两个或不使用
类型说明符
声明符
初始化式
18.2存储
变量的特性
存储期限
变量的存储期限决定了为变量预留和释放内存的时间
自动存储期限
变量所属块运行时获得内存
块运行结束释放空间,值丢失
静态存储期限
无限期保留值
作用域
指引用变量的那部份程序文本
块作用域
从变量声明到闭合块的末尾
文件作用域
从变量声明到闭合文件的末尾
链接
变量的链接决定了程序的不同部分可以共享此变量的范围
外部链接
具有外部链接的变量可以被几个或许全部文件共享
内部链接
具有内部链接的变量只尾于单个文件,该文件内的函数可共享此变量。如果其它文件中有相同名称的变量,系统将其作为不同的变量处理
无链接
无链接的变量仅属于某一函数,该变量不可以被共享
声明变量的默认特性

块内部声明的变量
自动存储期限
块作用域
无链接
任意块外部声明的变量
静态存储期限
文件作用域
外部链接
存储类型

auto
作用范围
只对属于块的变量有效
变量特性
自动存储类型基本上不用特地声明,因为它为作块变量的默认存储类型
自动存储期限
块作用域
无链接
static
作用范围
可用于全部变量
变量特性
块外部声明
static的“隐身作用”
在外部块声明时,static本质上隐藏了它所在文件内声明的变量,使其仅被文件内函数所见。 如下面的例子,仅函数f1和f2可访问变量i,其它文件无法访问变量i(如果不使用static,该变量对其它文件是可见的)   
静态存储期限
文件作用域
内部链接
块内部声明
有趣的性质
 
静态存储期限
块作用域
无链接
差异

extern
作用
使多个文件共享一个变量
告诉编译器要访问的变量的定义在其它的文件
变量的extern声明不作为变量的定义
extern声明变量不为变量分配地址空间 程序变量只能定义一次,可多次声明
例外
初始化变量的extern声明可作为定义

变量特性

存储期限
全部具有静态存储期限
作用域
块内部声明
块作用域
块外部声明
文件作用域
链接
Subtopic
register
21标准库
标准头
函数原型
类型定义
宏定义
21.1标准库的使用
包含标准头的限制规则
该文件不能再使用头文件中定义过的宏的名字
具有文件作用域的库名也不可以在文件层次再次定义
其它限制