C基础班讲义
2020-4-16 2:37 作者:酷帥王子 | c/c++黑客编程 |
C基础班讲义
朱景尧
目 录
1............................ 第一个c语言的hello world
1.1.............................................. include头文件包含
1.2................................................................ main函数
1.3............................................................................ 注释
1.4..................................................... {}括号和代码块
1.5............................................................................ 声明
1.6...................................... C语言自定义名字的要求
1.7............................................................. return语句
2.................................................... C语言的编译
2.1................................................................... 编译过程
2.2........................................................... gcc编译选项
2.3...................................................... printf执行原理
2.4............................................. CPU32位64位简介
3.......................................... C语言中的数据类型
3.1............................................................................ 常量
3.1.1................................................................ #define
3.1.2..................................................................... const
3.2............................................................... 字符串常量
3.3.......................................................... 位,字节,字
3.3.1............................................................................ 位
3.3.2................................................................... 二进制
3.3.3................................................................... 十进制
3.3.4................................................................... 八进制
3.3.5............................................................... 十六进制
3.3.6........................................................................ 字节
3.4.......................................................... sizeof关键字
3.5................ 十进制,二进制,八进制,十六进制
3.6..................................................................... int类型
3.6.1.................................................... int常量,变量
3.6.2............................................... printf输出int值
3.6.3......................... printf输出八进制和十六进制
3.6.4.. short,long,long long,unsigned int
3.6.5............................................................... 整数溢出
3.7................................................................. char类型
3.7.1................................................ char常量,变量
3.7.2................................................. printf输出char
3.7.3....................................... 不可打印char转义符
3.7.4................................... char和unsigned char
3.8.............. 浮点float,double,long double类型
3.8.1.................................................. 浮点常量,变量
3.8.2.............................................. printf输出浮点数
3.9................................................................... 类型限定
3.9.1..................................................................... const
3.9.2................................................................. volatile
3.9.3................................................................ register
4................................... 字符串格式化输出和输入
4.1......................... 字符串在计算机内部的存储方式
4.2................................. printf函数,putchar函数
4.3................................ scanf函数与getchar函数
5.......................................... 运算符表达式和语句
5.1............................................................... 基本运算符
5.1.1............................................................................. =
5.1.2............................................................................. +
5.1.3.............................................................................. –
5.1.4.............................................................................. *
5.1.6........................................................................... %
5.1.7.......................................................................... +=
5.1.8........................................................................... -=
5.1.9.......................................................................... *=
5.1.10.......................................................................... =
5.1.11..................................................................... %=
5.1.12....................................................................... ++
5.1.13.......................................................................... --
5.1.14........................................................ 逗号运算符
5.1.15.................................................... 运算符优先级
5.2................................................................... 复合语句
5.3................................................................... 类型转化
6.................................................... 条件分支语句
6.1............................................................... 关系运算符
6.1.1............................................................................. <
6.1.2.......................................................................... <=
6.1.3............................................................................. >
6.1.4.......................................................................... >=
6.1.5.......................................................................... ==
6.1.6............................................................................ !=
6.2.................................................. 关系运算符优先级
6.3............................................................... 逻辑运算符
6.3.1.......................................................................... &&
6.3.2........................................................................... ||
6.3.3............................................................................... !
6.4.................................................................................. if
6.5........................................................................ if else
6.6.................................................................... if else if
6.7.................................... switch与break,default
6.8.......................................................... 条件运算符?
6.9.................................................... goto语句与标号
7........................................................... 循环语句
7.1......................................................................... while
7.2.................................................................. continue
7.3........................................................................ break
7.4................................................................... do while
7.5.............................................................................. for
7.6................................................................... 循环嵌套
8............................ 整数在计算机内部的存储方式
8.1............................................................................ 原码
8.2............................................................................ 反码
8.3............................................................................ 补码
9.................................................................. 数组
9.1.............................................. 一维数组定义与使用
9.2.......................................... 数组在内存的存储方式
9.3...................................................... 一维数组初始化
9.4.............................................. 二维数组定义与使用
9.5...................................................... 二维数组初始化
10........................................... 字符串与字符数组
10.1........................................................ 字符数组定义
10.2................................................... 字符数组初始化
10.3........................................................ 字符数组使用
10.4....................... 随机数产生函数rand与srand
10.5.......................................... 用scanf输入字符串
10.6............................................... 字符串的结束标志
10.7................................................... 字符串处理函数
10.7.1.................................................................... gets
10.7.2......................................................... fgets函数
10.7.3........................................................... puts函数
10.7.4......................................................... fputs函数
10.7.5........................................ strlen,字符串长度
10.7.6....................................... strcat,字符串追加
10.7.7............................ strncat,字符串有限追加
10.7.8..................................... strcmp,字符串比较
10.7.9.......................... strncmp,字符串有限比较
10.7.10....................................... strcpy字符串拷贝
10.7.11............................ strncpy字符串有限拷贝
10.7.12............................... sprintf,格式化字符串
10.7.13............................................ strchr查找字符
10.7.14............................................. strstr查找子串
10.7.15........................................ strtok分割字符串
10.7.16.............................................. atoi转化为int
10.7.17......................................... atof转化为float
10.7.18........................................... atol转化为long
11................................................................ 函数
11.1............................................... 函数的原型和调用
11.2............................................... 函数的形参与实参
11.3................................... 函数的返回类型与返回值
11.4...................................... main函数与exit函数
11.5............................................................ 函数的递归
11.5.1............................................... 递归的过程分析
11.5.2........................................................ 递归的优点
11.5.3........................................................ 递归的缺点
11.6.............................. 多个源代码文件程序的编译
11.6.1.................................................... 头文件的使用
11.6.2....................... #include与#define的意义
11.6.3........................................... #ifndef与#endif
11.7............................................... 函数的二进制封装
11.7.1........................................ exe加载dll的说明
11.7.2.............. 动态库中代码与位置无关的说明图
11.7.3............................. linux编写so文件的方式
11.7.4.................................................... linux使用so
11.7.5.. 配置profile文件可以在当前目录下查找so文件
11.8................................................................ 作业描述
12................................................................ 指针
12.1......................................................................... 指针
12.1.1........................................................ 指针的概念
12.1.2............................................... 指针变量的定义
12.1.3.................................................................. NULL
12.1.4................................................................ 野指针
12.1.5................................................ &取地址运算符
12.1.6........................................................ 无类型指针
12.1.7.................................................... 指针的兼容性
12.1.8........................................... 指针与数组的关系
12.1.9................................... 通过指针使用数组元素
12.1.10.......................................................... 指针数组
12.1.11.......................................................... 数组指针
12.1.12.................... 指向指针的指针(二级指针)
12.1.13............................ 指针变量做为函数的参数
12.1.14............................ 一维数组名作为函数参数
12.1.15............................ 二维数组名作为函数参数
12.1.16.................................... 指向二维数组的指针
12.1.17........................ 指向常量的指针与指针常量
12.1.18........................ const关键字保护数组内容
12.1.19................................ 指针做为函数的返回值
12.1.20............................................. 指向函数的指针
12.1.21........... 把指向函数的指针做为函数的参数
12.1.22.......................................................... 指针运算
12.1.23.......................................................... 指针小结
13........................................... 字符指针与字符串
13.1........................................................ 指针和字符串
13.2................................... 通过指针访问字符串数组
13.3.......................................... 函数的参数为char *
13.4...................... 指针数组做为main函数的形参
14......................................................... 内存管理
14.1.................................................................... 作用域
14.1.1.................................................. auto自动变量
14.1.2...................................... register寄存器变量
14.1.3............................... 代码块作用域的静态变量
14.1.4.......................... 代码块作用域外的静态变量
14.1.5............................................................ 全局变量
14.1.6........................... 外部变量与extern关键字
14.1.7....................................... 全局函数和静态函数
14.2................................................................ 内存四区
14.2.1................................................................ 代码区
14.2.2................................................................ 静态区
14.2.3..................................................................... 栈区
14.2.4..................................................................... 堆区
14.3................................................... 堆的分配和释放
14.3.1................................................................ malloc
14.3.2..................................................................... free
14.3.3................................................................ calloc:
14.3.4............................................................... realloc
15.................. 结构体,联合体,枚举与typedef
15.1.................................................................... 结构体
15.1.1......................... 定义结构体struct和初始化
15.1.2............................................... 访问结构体成员
15.1.3................................... 结构体的内存对齐模式
15.1.4............................... 指定结构体元素的位字段
15.1.5............................................................ 结构数组
15.1.6............................................................ 嵌套结构
15.1.7.................................................... 结构体的赋值
15.1.8........................................... 指向结构体的指针
15.1.9................................... 指向结构体数组的指针
15.1.10.................... 结构中的数组成员和指针成员
15.1.11.................................... 在堆中创建的结构体
15.1.12.................................... 将结构作为函数参数
15.1.13........................ 结构,还是指向结构的指针
15.1.14............................................. 远指针与近指针
15.2.................................................................... 联合体
15.3................................................................ 枚举类型
15.3.1............................................................ 枚举定义
15.3.2................................................................ 默认值
15.4................................................................. typedef
15.5............................. 通过typedef定义函数指针
16......................................................... 文件操作
16.1...................................................................... fopen
16.2................................... 二进制和文本模式的区别
16.3..................................................................... fclose
16.4............................................... getc和putc函数
16.5................................ EOF与feof函数文件结尾
16.6.................... fprintf,fscanf,fgets,fputs函数
16.7............................................................... stat函数
16.8............................................................ fseek函数
16.9............................................................... ftell函数
16.10.................................... fgetpos,fsetpos函数
16.11......................................................... fflush函数
16.12....................................... fread和fwrite函数
16.13..................................................... fread与feof
16.14...................................................................... 作业
17....................................... 基础数据结构与算法
17.1................................................... 什么是数据结构
17.2............................................................ 什么是算法
17.3......................................................................... 链表
17.3.1.................................................... 单向链表定义
17.3.2................................... 单向链表数据结构定义
17.3.3............................................... 单向链表的实现
17.4......................................................................... 查找
17.4.1............................................................ 顺序查找
17.4.2............................................................ 二分查找
17.5......................................................................... 排序
17.5.1............................................................ 冒泡排序
17.5.2............................................................ 选择排序
1 第一个c语言的hello world
1.1 include头文件包含
头文件包含,写法#include <文件名>,
1.2 main函数
这个就是C语言程序的入口,所有的C程序都是从main开始执行,一个C的源程序必须有一个main函数,也只能有一个main函数
1.3 注释
//注释一行
/* */代表块注释,可以注释多行代码
1.4 {}括号和代码块
代表一个代码单元
1.5 声明
C语言规定,所有的变量和函数必须先声明,然后才能使用.
1.6 C语言自定义名字的要求
可以使用大小写字母,下划线,数字,但第一个字母必须是字母或者下划线
字母区分大小写
变量名最好用英文,而且要有所含义,通过变量的名称就能猜测变量的意思。
1.7 return语句
在C语言当中任何函数遇到return代表这个函数停止,当main函数遇到return,代表整个程序退出
return代表函数的返回值,如果返回类型是void,可以直接写return,而不需要返回任何值
2 C语言的编译
2.1 编译过程
2.2 gcc编译选项
-o代表指定输出文件名
-E代表预编译
预编译处理include的本质就是简单的将include中的文件替换到c文件中
如果include包含的头文件在系统目录下,那么就用#include <>,如果包含的文件在当前目录下,那么用#inlclude “”
-S代表汇编
-c代表编译
2.3 printf执行原理
2.4 CPU32位64位简介
一个字节是8个bit,一个字是两个字节,整型4个字节
8位寄存器
AL 00000000 256
16位寄存器
AX AL AH
0000000000000000
1111111111111111
四个通用寄存器
AX
BX
CX
DX
32位寄存器
EAX 0000000000000000000000000000000000000000000000 1111111111111111111111111111111111
EBX
ECX
EDX
64位寄存器
REAX
REBX
RECX
REDX
3 C语言中的数据类型
重点:**
3.1 常量
常量就是在程序中不可变化的量,常量是不可被赋值的。
3.1.1 #define
#define的本质就是简单的文本替换
通过#define定义的常量,在C语言里面一般叫宏定义
3.1.2 const
const定义一个变量,但是这个变量的值只能在定义的时候赋予,之后就不可以修改。
对于const类型的变量,一定要在定义的时候给变量赋初值,不然定义之后就无法赋值了。
3.2 字符串常量
在C语言当中“”引用的字符串都是字符串常量,常量一旦定义也是不可以被修改的。
3.3 位,字节,字
3.3.1 位
计算机内部都是二进制的,一个二进制的位,就叫做一个bit,就是一位
3.3.2 二进制
逢二加1,在二进制表达数的时候是只有0和1,而没有2这个数的
二进制最大表示的数,就是2几次幂
对于8位的CPU来讲,最大表达的数是2的8次幂
3.3.3 十进制
逢10加1,只有从0 到9的数,没有10这个数,
3.3.4 八进制
从0到7,逢8加1
在C语言中八进制是数字前面加0
3.3.5 十六进制
0,1,2,3,4,5,6,7,8,9,a,b,c,d,e,f,10
逢16加1,
在C语言当中表达一个十六进制数的方式,数字前面加0x前缀
3.3.6 字节
8个bit为代表一个字节
3.4 sizeof关键字
sizeof与size_t类型
sizeof是计算数据在内存当中占多大空间的,单位字节
由于sizeof永远返回的是一个大于等于0的整数,所以如果用int来表示sizeof的返回值就不合适,size_t一般就是一个无符号的整数.
3.5 十进制,二进制,八进制,十六进制
3.6 int类型
3.6.1 int常量,变量
一个int型数据占据4个字节的内存大小,在16位操作系统下,int是2个字节,在32和64位操作系统下,int是4个字节。
int a;//代表在内存当中开辟一个4个字节大小的空间
a = 10;//代表4个字节的空间内容是常量10
小端对齐和大端对齐
高地址放高位,底地址放低位---小端对齐
高地址放低位,底地址放高位—大端对齐
对于大型unix CPU都是按照大端对齐方式处理int,
但对于x86构架CPU,还有ARM,是小端对齐的
3.6.2 printf输出int值
int a = 0x100;//十六进制
printf(“%d”,a);%d的意思是按照10进制打印一个整数
%x
%X,输出十六进制的时候是用大写的ABCDEF还是小写的abcdef,
3.6.3 printf输出八进制和十六进制
%o
3.6.4 short,long,long long,unsigned int
在32位系统下:
short = 2个字节
long和int一样,是4字节
long long 是8个字节
在64位操作系统下
int,4个字节
long 在大多数64位系统下8个字节
unsigned int//无符号整数
unsigned long//无符号的长整数
unsigned short//无符号短整数
9l,9L,9ll,9LL,9u,9ull,9ULL
3.6.5 整数溢出
当把一个大的整数赋值给小的整数,叫溢出。
int I = 0x12345678
short a = I;
当一个int赋值给short,会将高位抛弃,
3.7 char类型
3.7.1 char常量,变量
char是字符型,代表一个字节的内存
char在内存当中,有符号最大7f,
无符号,最大ff
unsigned char
char的本质就是一个字节,一个BYTE
3.7.2 printf输出char
%c
3.7.3 不可打印char转义符
\a,警报
\b退格
\n换行
\r回车
\t制表符
\\斜杠
\’单引号
\”双引号
\?问号
3.7.4 char和unsigned char
char取值范围为-128到127
unsigned char为0-255
3.8 浮点float,double,long double类型
3.8.1 浮点常量,变量
float f = 2.5;
double f1 = 3.1415926
3.8.2 printf输出浮点数
%f,%Lf
3.9 类型限定
3.9.1 const
const限定一个变量的值不可以改变
3.9.2 volatile
告诉编译器不要自作聪明的给我优化代码,把我的变量优化的寄存器里面计算,只要是volatile类型变量,每一步都需要从内存当中读取。
3.9.3 register
register告诉编译器,这个变量只是用寄存器就好,提高效率,所以说register只是一个建议,而不是必须的结果。
4 字符串格式化输出和输入
重点:*
4.1 字符串在计算机内部的存储方式
字符串是内存中一段连续的char空间,以’\0’结尾
字符串就是0结尾的连续char的内存
4.2 printf函数,putchar函数
printf格式字符
字符 |
对应数据类型 |
含义 |
d |
int |
接受整数值并将它表示为有符号的十进制整数 |
hd |
Short int |
短整数 |
hu |
Unsigned short int |
无符号短整数 |
o |
unsigned int |
无符号8进制整数 |
u |
unsigned int |
无符号10进制整数 |
x / X |
unsigned int |
无符号16进制整数,x对应的是abcdef,X对应的是ABCDEF |
f |
float或double |
|
e / E |
double |
科学计数法表示的数,此处"e"的大小写代表在输出时用的“e”的大小写 |
c |
char |
|
s / S |
char * / wchar_t * |
字符串。输出字符串中的字符直至字符串中的空字符(字符串以'\0‘结尾,这个'\0'即空字符) |
p |
void * |
以16进制形式输出指针 |
% |
% |
输出一个百分号 |
printf附加格式
字符 |
含义 |
l |
附加在d,u,x,o前面,表示长整数 |
- |
左对齐 |
m(代表一个整数) |
数据最小宽度 |
0 |
将输出的前面补上0直到占满指定列宽为止不可以搭配使用- |
N(代表一个整数) |
宽度至少为n位不够以空格填充 |
printf是打印一个字符串
putchar是打印一个字符
4.3 scanf函数与getchar函数
5 运算符表达式和语句
重点:***
5.1 基本运算符
5.1.1 =
数据对象:泛指数据在内存的存储区域
左值:表示可以被更改的数据对象
右值:能赋给左值的量
5.1.2 +
5.1.3 –
5.1.4 *
5.1.5 /
5.1.6 %
取模,取余数
5.1.7 +=
int a = 10;
a = a + 5;
可以简写成a += 5;
5.1.8 -=
a = a – 5;a -= 5;
5.1.9 *=
a = a * 5;a *= 5;
5.1.10 /=
5.1.11 %=
5.1.12 ++
笔试面试特别容易考,也是特别容易出错的地方
5.1.13 --
5.1.14 逗号运算符
int I = 6 + 5, 3 + 2
逗号表达式先求逗号左边的值,然后求右边的值,整个语句的值是逗号右边的值。
5.1.15 运算符优先级
优先级 |
运算符 |
结合性 |
1 |
++(后缀),--(后缀),()(调用函数),{}(语句块),.,-> |
从左到右 |
2 |
++(前缀),--(前缀),+(前缀),-(前缀),!(前缀),~(前缀),sizeof,*(取指针值),&(取地址),(type)(类型转化) |
从右到左 |
3 |
*, /, % |
从左到右 |
4 |
+,- |
从左到右 |
5 |
<< >> |
从左到右 |
6 |
< > <= >= |
从左到右 |
7 |
== != |
从左到右 |
8 |
& |
从左到右 |
9 |
^ |
从左到右 |
10 |
| |
从左到右 |
11 |
&& |
从左到右 |
12 |
|| |
从左到右 |
13 |
? |
从右到左 |
14 |
=,*=,%=,+=,-=,<<=,>>=,&=,|=,^= |
从右到左 |
15 |
,(逗号运算符) |
从左到右 |
5.2 复合语句
{}代码块
5.3 类型转化
double d = 10.2;
int a = d;//隐式转换
int a1 = 3;
int a2 = 2;
double mid = (double)a1 / (double)a2;//显示的强转
printf("mid = %f\n", mid);
6 条件分支语句
重点:***
6.1 关系运算符
在C语言中0代表假,非0代表真,
在计算机程序判断是根据一个条件返回是真还是假来做不同的操作。
6.1.1 <
6.1.2 <=
6.1.3 >
6.1.4 >=
6.1.5 ==
一个=号在C语言里面是赋值的,不是比较的,但是很多初学者爱犯一个严重的错误,就是用=号来比较两个数是否相等
6.1.6 !=
!=
6.2 关系运算符优先级
前四种相同,后两种相同,前四种高于后两种优先级
6.3 逻辑运算符
6.3.1 &&
逻辑与
6.3.2 ||
逻辑或
6.3.3 !
逻辑非
6.4 if
6.5 if else
两路分支if语句,只可能执行一路,不可能同时执行两路,也不可能两路都没有被执行
6.6 if else if
if else if 这种于语法可以实现多路的分支,但只有一路被执行。
else永远是和最近的一条if语句配对。
6.7 switch与break,default
switch是为多重选择准备的,遇到break语句,switch就终端执行
6.8 条件运算符?
一个求绝对值的例子
int i = -8; int x = (i < 0) ? -i: i; |
?号用法
当?号 前面括号内容为真的时候,执行?号之后冒号之前的语句,否则执行冒号之后的语句
6.9 goto语句与标号
尽量不要在程序当中使用goto语句,
7 循环语句
重点:***
7.1 while
while(条件),如果条件为真,那么循环就执行,否则循环退出
7.2 continue
continue意思是跳过下面语句,继续执行循环
7.3 break
break中断循环,
7.4 do while
do
语句
while (条件);
对于do while来讲,至少能执行一次,
7.5 for
可以指定循环次数的语句
7.6 循环嵌套
特别需要掌握的技术,
8 整数在计算机内部的存储方式
重点:****
8.1 原码
将最高位做为符号位(0代表正,1代表负),其余各位代表数值本身的绝对值
+7的原码是00000111 -7的原码是10000111 +0的原码是00000000 -0的原码是10000000
|
8.2 反码
一个数如果值为正,那么反码和原码相同
一个数如果为负,那么符号位为1,其他各位与原码相反
+7的反码00000111 -7的反码11111000 -0的反码11111111 |
8.3 补码
原码和反码都不利于计算机的运算,如:原码表示的7和-7相加,还需要判断符号位。
正数:原码,反码补码都相同
负数:最高位为1,其余各位原码取反,最后对整个数 + 1
-7的补码:= 10000111(原码) 111111000(反码) 11111001(补码) +0的补码为00000000 -0的补码也是00000000 |
补码符号位不动,其他位求反,最后整个数 + 1,得到原码
用补码进行运算,减法可以通过加法实现 |
7-6=1 7的补码和-6的补码相加:00000111 + 11111010 = 100000001 进位舍弃后,剩下的00000001就是1的补码 |
-7+6 = -1 -7的补码和6的补码相加:11111001 + 00000110 = 11111111 11111111是-1的补码 |
9 数组
重点:**
内存连续,并且是同一种数据类型的变量,C语言的数组小标好是从0开始的,到n-1.
9.1 一维数组定义与使用
类型 变量名称[数组元素的个数];
9.2 数组在内存的存储方式
在内存当中是连续的内存空间地址。
9.3 一维数组初始化
int array[10] = {0};//将数组所有元素都初始化为0
int array[10] = {0,1,2,3,4,5,6,7,8,9}
数组中找最大值思路
数组中找第二大值思路
逆置数组思路
测量楼宇高度的说明
测量地球太阳距离的说明
测量太阳木星距离的说明
9.4 二维数组定义与使用
int array[3][4];//12个元素的二维数组
9.5 二维数组初始化
int a[3][4] = { { 1, 2, 3, 4 }, { 5, 6, 7, 8 }, { 9, 10, 11, 12 } }; |
10 字符串与字符数组
重点:***
10.1 字符数组定义
char buf[100];
对于C语言字符串其实就是一个最后一个元素为’\0’的char数组.
10.2 字符数组初始化
char buf[] = “hello world”;
10.3 字符数组使用
10.4 随机数产生函数rand与srand
头文件stdlib.h
#include <time.h> int t = (int)time(NULL); srand(t); for (int i = 0; i < 10; i++) { printf("%d\n", rand()); } |
|
10.5 用scanf输入字符串
char buf[100] = {0};
scanf(“%s”, buf);
scanf("请输入i的值%d", &i);
10.6 字符串的结束标志
10.7 字符串处理函数
10.7.1 gets
gets没有解决缓冲区溢出的问题.
10.7.2 fgets函数
gets函数不检查预留缓冲区是否能够容纳用户实际输入的数据。多出来的字符会导致内存溢出,fgets函数改进了这个问题。
由于fgets函数是为读取文件设计的,所以读取键盘时没有gets那么方便
char s[100] = { 0 }; fgets(s, sizeof(s), stdin); |
10.7.3 puts函数
puts函数打印字符串,与printf不同,puts会在最后自动添加一个’\n’
char s[] = "hello world"; puts(s); |
10.7.4 fputs函数
fputs是puts的文件操作版本,
char s[] = "hello world"; fputs(s, stdout); |
10.7.5 strlen,字符串长度
strlen返回字符串的长度,但是不包含字符串结尾的’\0’
char buf[10]
sizeof(buf);//返回的是数组buf一共占据了多少字节的内存空间.
10.7.6 strcat,字符串追加
char str1[100];
char str2[100];
strcat(str1, str2);//把str2追加到str1的后面
str1一定要有足够的空间来放str2,不然会内存溢出.
10.7.7 strncat,字符串有限追加
strncat(str1, str2, sizeof(str1) – strlen(str1) - 1);
10.7.8 strcmp,字符串比较
strcmp(a, “str”);//如果两个参数所指的字符串内容相同,函数返回0
10.7.9 strncmp,字符串有限比较
strncmp(str, “exit”, 4);
10.7.10 strcpy字符串拷贝
strcpy(str, “hello world”);//存在溢出的问题,
10.7.11 strncpy字符串有限拷贝
strcpy(str, “hello world”, 7);
10.7.12 sprintf,格式化字符串
printf是向屏幕输出一个字符串
sprintf是向char数组输出一个字符串,其他行为和printf一模一样
sprintf也存在缓冲区溢出的问题
10.7.13 strchr查找字符
strchr(str, ‘c’);
返回值是字符’c’在字符串str中的位置
10.7.14 strstr查找子串
10.7.15 strtok分割字符串
字符在第一次调用时strtok()必需给予参数s字符串,往后的调用则将参数s设置成NULL每次调用成功则返回指向被分割出片段的指针
char buf[] = "abc@defg@igk"; char *p = strtok(buf, "@");; while (p) { printf("%s\n", p); p = strtok(NULL, "@"); } |
10.7.16 atoi转化为int
10.7.17 atof转化为float
10.7.18 atol转化为long
作业说明:
不可以用任何已有的函数,完全自己写代码,完成十进制字符串转化为十进制的整数
作业思路:
11 函数
重点***
11.1 函数的原型和调用
在使用函数前必须定义或者声明函数
double circle(double r); int main() { double length = circle(10); printf("length = %f\n", length); return 0; }
double circle(double r) { return 2 * 3.14 * r; } |
11.2 函数的形参与实参
在调用函数的时候,函数大多数都有参数,主调函数和被调用函数之间需要传递数据。
在定义函数时函数名后面括弧中的变量名称为“形式参数”,简称形参。在调用函数时,函数名后面括号中的变量或表达式称为“实际参数”,简称实参。
1形参在未出现函数调用时,他们并不占用内存单元,只有在发生函数调用的时候形参才被分配内存,函数调用完成后,形参所占的内存被释放
2实参可以是变量,常量或者表达式
3在定义函数时,一定要指定形参的数据类型
4形参与实参的数据类型一定要可兼容
5在C语言中,实参与形参的数据传递是“值传递”,即单向传递,只由实参传递给形参,而不能由形参传递给实参。
11.3 函数的返回类型与返回值
1函数的返回值通过函数中的return获得,如果函数的返回值为void可以不需要return语句。
2函数return语句中的返回值数据类型应该与函数定义时相同。
3如果函数中没有return语句,那么函数将返回一个不确定的值。
11.4 main函数与exit函数
在main函数当中遇到return语句,代表整个程序结束。在子函数当中遇到return代表子函数结束。
不论程序的任何位置调用exit函数,代表整个程序退出。
函数的学习难点是递归
11.5 函数的递归
函数可以调用自己,这就叫函数的递归
void recurse(int i) { if (i > 0) { recurse(i - 1); } printf("i = %d\n", i); }
int main() { recurse(10); return 0; } |
11.5.1 递归的过程分析
void up_down(int n) { printf("in %d, location %p\n", n, &n); if (n < 4) up_down((n + 1)); printf("out %d, location %p\n", n, &n); }
int main() { up_down(1); return 0; } |
有n个人排成一队,问第n个人多少岁,他回答比前面一个人大2岁,再问前面一个人多少岁,他回答比前面一个人大2岁,一直问到最后问第一个人,他回答10岁
int age(int n) { int i; if (n == 1) i = 10; else i = age(n - 1) + 2; return i; } |
将10进制数转化为二进制数的例子
234在十进制下为2 * 10的2次方 + 3 * 10的1次方 + 4*10的0次方。
奇数的二进制最后一位一定是1,偶数的二进制最后一位一定是0。
可以通过 number % 2 得到二进制形式的最后一位,如果要将一个完整的整数转化为二进制就需要用到递归函数。
在递归调用之前,计算 number % 2的值,然后在递归调用语句之后进行输出,这样计算出的第一个数值反而在最后一个输出。
为了得出下一个数,需要把原数除以2,这种计算相当于十进制下把小数点左移一位,如果此时得出的数是偶数,,则下一个二进制的数值是0,如果得出的是奇数,那么下一个二进制数为1。
直到被2除的结果小于2,就停止递归。
void to_binary(unsigned int n) { unsigned int i = n % 2; if (n >= 2) to_binary(n / 2); printf("%c", i + 0x30); } |
斐波那契数列例子
斐波那契数列指的是这样一个数列 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, ...
第0项是0,第1项是第一个1。
这个数列从第2项开始,每一项都等于前两项之和。
int fib(int n) { if (n == 0) return 0; if (n == 1) return 1; if (n > 1) return fib(n - 1) + fib(n - 2); } |
汉诺塔的例子
有三根针1,2,3。1针上有4个盘子,盘子大小不等,大的在下,小的在上,
要求把这4个盘子从1针一到3针,在移动过程中可以借助2针,每次只允许移动一个盘子,并且在移动过程中在三根针上都要保持大盘子在下,小盘子在上。
可以分为以下步骤:
1、将1针上n-1个盘借助3针移动到2针上;
2、将1针上剩下的一个盘移动到3针上;
3、将n-1个盘从2针借助1针移动到3针上。
void hanoi(int n, int one, int two, int three) { if (n == 1) printf("%d->%d\n", one, three); else { hanoi(n - 1, one, three, two); printf("%d->%d\n", one, three); hanoi(n - 1, two, one, three); } } |
11.5.2 递归的优点
递归给某些编程问题提供了最简单的方法
11.5.3 递归的缺点
一个有缺陷的递归会很快耗尽计算机的资源,递归的程序难以理解和维护。
11.6 多个源代码文件程序的编译
11.6.1 头文件的使用
如果把main函数放在第一个文件中,而把自定义函数放在第二个文件中,那么就需要在第一个文件中声明函数原型。
如果把函数原型包含在一个头文件里,那么就不必每次使用函数的时候都声明其原型了。把函数声明放入头文件是很好的习惯。
11.6.2 #include与#define的意义
11.6.3 #ifndef与#endif
11.7 函数的二进制封装
11.7.1 exe加载dll的说明
11.7.2 动态库中代码与位置无关的说明图
11.7.3 linux编写so文件的方式
1首先gcc编译的时候要加-fPIC选项,-fPIC是告诉gcc生成一个与位置无关的代码
2gcc链接的时候要加-shared选项,意思是生成一个so共享库。
对于linux或者unix,一个so文件,文件扩展名必须是so,文件名的前三个字母必须是lib
11.7.4 linux使用so
gcc链接的时候需要加-L. 代表从当前目录下找相关的so文件,-l文件名(但不包括文件名开头的lib和扩展名so)
例如编译一个main.o文件,要用到当前目录下的liba.so库
gcc –o main.out –L. –la main.o
11.7.5 配置profile文件可以在当前目录下查找so文件
linux不在当前目录下寻找可执行程序,同时也不早当前目录下找so库文件
修改用户配置文件的方法
1
cd
2
vi .bash_profile
3
export LD_LIBRARY_PATH = $LD_LIBRARY_PATH:.
4
保存退出
5
. .bash_profile
11.8 作业描述
int sum1(int n);//n = 10 0,1,2,3,4,5,6,7,8,9,10的和
要求不可以用循环,只可以用递归
int sum2(int n);//n = 10 从0到n范围内所有素数的和
要求不可以用循环,只可以用递归
12 指针
重点:*****
12.1 指针
12.1.1 指针的概念
指针也是一个变量,做为指针变量的值是另一个变量的地址。
指针存放的内容是一个地址,该地址指向一块内存空间
12.1.2 指针变量的定义
可以定义一个指向一个变量的指针变量。
int *p;//表示定义一个指针变量。
*p;//代表指针所指内存的实际数据
切记,指针变量只能存放地址,不能将一个int型变量直接赋值给一个指针。
int *p = 100;
12.1.3 NULL
一个指向NULL的指针,我们称之为空指针,意味着这个指针不指向任何一个变量。
12.1.4 野指针
定义之后没有初始化值的指针
12.1.5 &取地址运算符
12.1.6 无类型指针
定义一个指针变量,但不指定它指向具体哪种数据类型。可以通过强制转化将void *转化为其他类型指针,也可以用(void *)将其他类型指针强制转化为void类型指针。
void *p
在C语言当中,可以将任何一种地址赋值给void *指针
12.1.7 指针的兼容性
指针之间赋值比普通数据类型赋值检查更为严格,例如:不可以把一个double *赋值给int *
12.1.8 指针与数组的关系
一个变量有地址,一个数组包含若干个元素,每个元素在内存中都有地址。
int a[10];
int *p = a;
比较p和&a[0]的地址是否相同
在C语言当中数组的名称代表数组的首地址,如果取数组名称的地址,C语言认为就是取数组的首地址。
12.1.9 通过指针使用数组元素
通过指针计算,不是把指针当做一个整数,计算结果,而是指针在内存当中移动
p + 1代表&a[1],也可以直接使用p[1]表示a[5]
p + 5 代表&a[5]
p++
在C语言里面数组名称是个常量,值是不可改变的
12.1.10 指针数组
int *p[5];
12.1.11 数组指针
int (*P)[5];
12.1.12 指向指针的指针(二级指针)
指针就是一个变量,既然是变量就也存在内存地址,所以可以定义一个指向指针的指针。
int i = 10;
int *p1 = &i;
int **p2 = &p1;
printf("%d\n", **p2);
以此类推可以定义3级甚至多级指针。
12.1.13 指针变量做为函数的参数
函数的参数可以是指针类型。,它的作用是将一个变量的地址传送给另一个函数。
12.1.14 一维数组名作为函数参数
当数组名作为函数参数时,C语言将数组名解释为指针
当数组名作为函数参数传递给被调用函数时,被调用函数是不知道数组有多少元素的
int func(int array[10]);
相当于传递是一个地址,那么就可以通过地址来修改实参的值。
只要传递是数组名,那么形参一定可以通过地址修改实参的值。
12.1.15 二维数组名作为函数参数
二维数组做函数参数时可以不指定第一个下标。
int func(int array[][10]);
12.1.16 指向二维数组的指针
int a[3][5]
a |
二维数组名称,数组首地址 |
a[0], *(a + 0), *a |
0行,0列元素地址 |
a + 1 |
第1行首地址 |
a[1], *(a + 1) |
第1行,0列元素地址 |
a[1] + 2, *(a + 1) + 2, &a[1][2] |
第1行,2列元素地址 |
*(a[1] + 2), *(*(a + 1) + 2), a[1][2] |
第1行,2列元素的值 |
12.1.17 指向常量的指针与指针常量
const char *p;//定义一个指向常量的指针
char *const p;//定义一个指针常量,一旦初始化之后其内容不可改变
12.1.18 const关键字保护数组内容
如果将一个数组做为函数的形参传递,那么数组内容可以在被调用函数内部修改,有时候不希望这样的事情发生,所以要对形参采用const参数
func(const int array[])
12.1.19 指针做为函数的返回值
char *func();//返回值为char *类型的函数
12.1.20 指向函数的指针
指针可以指向变量,数组,也可以指向一个函数。
一个函数在编译的时候会分配一个入口地址,这个入口地址就是函数的指针,函数名称就代表函数的入口地址。
函数指针的定义方式:int (*p)(int);//定义了一个指向int func(int n)类型函数地址的指针。
1定义函数指针变量的形式为:函数返回类型(*指针变量名称)(参数列表)
2函数可以通过函数指针调用
3int( * P)()代表指向一个函数,但不是固定哪一个函数。
void man() { printf("抽烟\n"); printf("喝酒\n"); printf("打牌\n"); }
void woman() { printf("化妆\n"); printf("逛街\n"); printf("网购\n"); }
int main() { void(*p)(); int i = 0; scanf("%d", &i); if (i == 0) p = man; else p = woman; p(); return 0; }
|
12.1.21 把指向函数的指针做为函数的参数
将函数指针做为另一个函数的参数称为回调函数
int max(int a, int b) { if (a > b) return a; else return b; }
int add(int a, int b) { return a + b; }
void func(int(*p)(int, int), int a, int b) { int res = p(a, b); printf("%d\n", res);
}
int main() { int i = 0; scanf("%d", &i); if (i == 0) func(max, 10, 20); else func(add, 10, 20); return 0; }
|
12.1.22 指针运算
赋值:int *p = &a;
求值:int I = *p;
取指针地址 int **pp = &p;
将一个整数加(减)给指针:p + 3; p – 3;
增加(减少)指针值 p++,p--
求差值 ,p1 – p2,通常用于同一个数组内求两个元素之间的距离
比较 p1 == p2,通常用来比较两个指针是否指向同一个位置。
12.1.23 指针小结
定义 |
说明 |
Int i |
定义整形变量 |
int *p |
定义一个指向int的指针变量 |
Int a[10] |
定义一个int数组 |
Int *p[10] |
定义一个指针数组,其中每个数组元素指向一个int型变量的地址 |
Int func() |
定义一个函数,返回值为int型 |
Int *func() |
定义一个函数,返回值为int *型 |
Int (*p)() |
定义一个指向函数的指针,函数的原型为无参数,返回值为int |
Int **p |
定义一个指向int的指针的指针,二级指针 |
13 字符指针与字符串
重点:****
13.1 指针和字符串
在C语言当中,大多数字符串操作其实就是指针操作。
char s[] = "hello word"; char *p = s; p[0] = 'a'; |
13.2 通过指针访问字符串数组
13.3 函数的参数为char *
13.4 指针数组做为main函数的形参
int main(int argc, char *argv[]);
作业
//不可以使用任何C语言库函数,只可以自己写函数实现int 到字符串的转化
void itoa(int i, char *s)
{
}
14 内存管理
重点:*****
14.1 作用域
一个C语言变量的作用域可以是代码块 作用域,函数作用域或者文件作用域。
代码块是{}之间的一段代码。
14.1.1 auto自动变量
一般情况下代码块内部定义的变量都是自动变量。当然也可以显示的使用aotu关键字
14.1.2 register寄存器变量
通常变量在内存当中,如果能把变量放到CPU的寄存器里面,代码执行效率会更高
register int I;
14.1.3 代码块作用域的静态变量
静态变量是指内存位置在程序执行期间一直不改变的变量,一个代码块内部的静态变量只能被这个代码块内部访问。
14.1.4 代码块作用域外的静态变量
代码块之外的静态变量在程序执行期间一直存在,但只能被定义这个变量的文件访问
14.1.5 全局变量
全局变量的存储方式和静态变量相同,但可以被多个文件访问
14.1.6 外部变量与extern关键字
extern int I;
14.1.7 全局函数和静态函数
在C语言中函数默认都是全局的,使用关键字static可以将函数声明为静态
14.2 内存四区
14.2.1 代码区
代码区code,程序被操作系统加载到内存的时候,所有的可执行代码都加载到代码区,也叫代码段,这块内存是不可以在运行期间修改的。
14.2.2 静态区
所有的全局变量以及程序中的静态变量都存储到静态区,比较如下两段代码的区别
int a = 0; int main() { static int b = 0; printf("%p, %p\n", &a, &b); return 0; } |
int a = 0; static int b = 0; int main() { printf("%p, %p\n", &a, &b); return 0; } |
14.2.3 栈区
栈stack是一种先进后出的内存结构,所有的自动变量,函数的形参都是由编译器自动放出栈中,当一个自动变量超出其作用域时,自动从栈中弹出。
14.2.4 堆区
堆heap和栈一样,也是一种在程序运行过程中可以随时修改的内存区域,但没有栈那样先进后出的顺序。
堆是一个大容器,它的容量要远远大于栈,但是在C语言中,堆内存空间的申请和释放需要手动通过代码来完成。
14.3 堆的分配和释放
14.3.1 malloc
14.3.2 free
14.3.3 calloc:
第一个参数是所需内存单元数量,第二个参数是每个内存单元的大小(单位:字节),calloc自动将分配的内存置0
int *p = (int *)calloc(100, sizeof(int));//分配100个int |
14.3.4 realloc
重新 分配大小。
在C语言当中,内存初始化是个好习惯,
除了全局变量和静态变量以外,其他变量C语言编译不会帮你初始化,只有自己通过显示的初始化。
15 结构体,联合体,枚举与typedef
重点:****
15.1 结构体
15.1.1 定义结构体struct和初始化
struct man { char name[100]; int age; }; struct man m = { "tom", 12 }; struct man m = { .name = "tom", .age = 12 }; |
15.1.2 访问结构体成员
.操作符
15.1.3 结构体的内存对齐模式
结构在内存的大小是和结构成员最长的那个元素相关的
编译器在编译一个结构的时候采用内存对齐模式
struct man{ char a; int b; }; |
15.1.4 指定结构体元素的位字段
定义一个结构体的时候可以指定具体元素的位长
struct test{ char a : 2;//指定元素为2位长,不是2个字节长 }; |
15.1.5 结构数组
struct man m[10] = { { "tom", 12 }, { "marry", 10 }, { "jack", 9 } }; |
15.1.6 嵌套结构
一个结构的成员还可以是另一个结构类型
struct names{ char first[100]; char last[100]; };
struct man{ struct names name; int age; }; struct man m = { { "wang", "wu" }, 20 }; |
15.1.7 结构体的赋值
struct name a = b;
结构的赋值其实就是两个结构内存的拷贝
如果结构体成员有指针元素,那么就不能直接赋值,
15.1.8 指向结构体的指针
–>操作符
15.1.9 指向结构体数组的指针
15.1.10 结构中的数组成员和指针成员
一个结构中可以有数组成员,也可以有指针成员,如果是指针成员结构体成员在初始化和赋值的时候就需要提前为指针成员分配内存。
struct man { char name[100]; int age; }; |
struct man { char *name; int age; }; |
15.1.11 在堆中创建的结构体
如果结构体有指针类型成员,同时结构体在堆中创建,那么释放堆中的结构体之前需要提前释放结构体中的指针成员指向的内存。
struct man { char *name; int age; }; struct man *s = malloc(sizeof(struct man) * 2); s[0].name = malloc(10 * sizeof(char)); s[1].name = malloc(10 * sizeof(char)); |
15.1.12 将结构作为函数参数
将结构作为函数参数
将结构指针作为函数参数
15.1.13 结构,还是指向结构的指针
在定义一个和结构有关的函数,到底是使用结构,还是结构的指针?
指针作为参数,只需要传递一个地址,所以代码效率高
如果一个结构体变量做为函数的参数,效率极低。同时老的C编译器都不支持传递结构变量,只支持传递结构指针。
15.1.14 远指针与近指针
15.2 联合体
联合union是一个能在同一个存储空间存储不同类型数据的类型。
联合体所占的内存长度等于其最长成员的长度,也有叫做共用体。
联合体虽然可以有多个成员,但同一时间只能存放其中一种。
对于联合体来讲最基本的原则是,一次只操作一个成员变量,如果这个变量是指针,那么一定是处理完指针对应的内存之后再来使用其他成员。
union variant{ int ivalue; char cvalue; double dvalue; };
int main() { union variant var; var.cvalue = 12; printf("%d\n", var.ivalue); printf("%p, %p, %p\n", &(var.cvalue), &(var.ivalue), &(var.dvalue)); return 0; } |
15.3 枚举类型
15.3.1 枚举定义
可以使用枚举(enumerated type)声明代表整数常量的符号名称,关键字enum创建一个新的枚举类型。
实际上,enum常量是int类型的。
枚举的本质就是int型的常量。
enum spectrum { red, yellow, green, blue, white, black }; enum spectrum color; color = black; if (color != red) |
15.3.2 默认值
默认时,枚举列表中的常量被指定为0,1,2等
enum spectrum { red, yellow, green, blue, white, black }; printf("%d, %d\n", red, black); |
指定值
可以指定枚举中具体元素的值
enum spectrum { red = 10, yellow = 20, green, blue, white, black }; printf("%d, %d\n", red, black); |
15.4 typedef
typedef是一种高级数据特性,它能使某一类型创建自己的名字
typedef unsigned char BYTE |
1与#define不同,typedef仅限于数据类型,而不是能是表达式或具体的值
2typedef是编译器处理的,而不是预编译指令
3typedef比#define更灵活
直接看typedef好像没什么用处,使用BYTE定义一个unsigned char。使用typedef可以增加程序的可移植性。
15.5 通过typedef定义函数指针
typedef const char *(*SUBSTR)(const char *, const char *); const char *getsubstr(const char *src, const char *str) { return strstr(src, str); } const char *func(const char *(*s)(const char *, const char *), const char *src, const char *str) |
const char *(*p[3])(const char *, const char *); |
在程序当中如果是定义一个可读的常量,适合用#define
如果定义的是一个具体的数据类型,那么typedef更加适合。
如果是定义一个函数指针,那么基本就typedef吧.
16 文件操作
重点:***
不论操作什么类型的文件,第一步先打开一个文件,第二步,读写文件,第三步关闭文件。
16.1 fopen
r 以只读方式打开文件,该文件必须存在。
r+ 以可读写方式打开文件,该文件必须存在。用r+写文件时候,从文件开始位置写入
rb+ 读写打开一个二进制文件,允许读写数据,文件必须存在。
rw+ 读写打开一个文本文件,允许读和写。
w 打开只写文件,若文件存在则文件长度清为0,即该文件内容会消失。若文件不存在则建立该文件。
wb
w+ 打开可读写文件,若文件存在则文件长度清为零,即该文件内容会消失。若文件不存在则建立该文件。
a 以附加的方式打开只写文件。若文件不存在,则会建立该文件,如果文件存在,写入的数据会被加到文件尾,即文件原先的内容会被保留。(EOF符保留),如果文件不存在,a的行为和w是一样的
a+ 以附加方式打开可读写的文件。若文件不存在,则会建立该文件,如果文件存在,写入的数据会被加到文件尾后,即文件原先的内容会被保留。 (原来的EOF符不保留)
“b”只对windows有效,对于unix来讲是无效,
16.2 二进制和文本模式的区别
1.在windows系统中,文本模式下,文件以"\r\n"代表换行。若以文本模式打开文件,并用fputs等函数写入换行符"\n"时,函数会自动在"\n"前面加上"\r"。即实际写入文件的是"\r\n" 。
2.在类Unix/Linux系统中文本模式下,文件以"\n"代表换行。所以Linux系统中在文本模式和二进制模式下并无区别。
对于GBK编码的汉字,一个汉字两个字节,对于utf8来讲一个汉字3个字节,但如果英文字母都是一个字节
16.3 fclose
fclose关闭fopen打开的文件
16.4 getc和putc函数
int main() { FILE *fp = fopen("a.txt", "r"); char c; while ((c = getc(fp)) != EOF) { printf("%c", c); } fclose(fp); return 0; } |
int main() { FILE *fp = fopen("a.txt", "w"); const char *s = "hello world"; int i; for (i = 0; i < strlen(s); i++) { putc(s[i], fp); } fclose(fp); return 0; } |
16.5 EOF与feof函数文件结尾
程序怎么才能知道是否已经到达文件结尾了呢?EOF代表文件结尾
如果已经是文件尾,feof函数返回true。
16.6 fprintf,fscanf,fgets,fputs函数
这些函数都是通过FILE *来对文件进行读写。
fscanf不会读取行尾的’\n’,fgets 会将行尾的’\n’读取到buf里面
不论fprintf还是fputs都不会自动向行尾添加\n,需要代码中往buf的行尾写\n才可以达到换行的目录
16.7 stat函数
#include <sys/stat.h>
函数的第一个参数代表文件名,第二个参数是struct stat结构。
得到文件的属性,包括文件建立时间,文件大小等信息。
16.8 fseek函数
函数设置文件指针stream的位置。如果执行成功,stream将指向以fromwhere为基准,偏移offset(指针偏移量)个字节的位置,函数返回0。如果执行失败则不改变stream指向的位置,函数返回一个非0值。
实验得出,超出文件末尾位置,还是返回0。往回偏移超出首位置,还是返回0,请小心使用。
第二个参数负数代表向前移动,整数代表向后移动。
第一个参数stream为文件指针
第二个参数offset为偏移量,单位:字节,正数表示正向偏移,负数表示负向偏移
第三个参数origin设定从文件的哪里开始偏移,可能取值为:SEEK_CUR、 SEEK_END 或 SEEK_SET
SEEK_SET: 文件开头
SEEK_CUR: 当前位置
SEEK_END: 文件结尾
fseek(fp, 3, SEEK_SET); |
16.9 ftell函数
函数 ftell 用于得到文件位置指针当前位置相对于文件首的偏移字节数。在随机方式存取文件时,由于文件位置频繁的前后移动,程序不容易确定文件的当前位置。
long len = ftell(fp) |
16.10 fgetpos,fsetpos函数
fseek与ftell返回的是long类型,如果文件很大,超过long的范围,那么该函数会有问题,fgetpos与fsetpos函数可以处理更大的文件类型
返回值:成功返回0,否则返回非0
fpos_t ps = 0; fgetpos(fp, &ps); |
fpos_t ps = 2; fsetpos(fp, &ps); |
16.11 fflush函数
fflush函数可以将缓冲区中任何未写入的数据写入文件中。
修改配置文件,希望修改实时生效,那么每次修改完成之后我们fflush一次
16.12 fread和fwrite函数
size_t fread ( void *buffer, size_t size, size_t count, FILE *stream) ;
size_t fwrite(const void* buffer, size_t size, size_t count, FILE* stream);
注意:这个函数以二进制形式对文件进行操作,不局限于文本文件
返回值:返回实际写入或读取的数据块数目
只要读取到文件最后,没有完整的读取一个数据块出来,fread就返回0
第一个参数代表void *,写入或者读取的缓冲区
第二个参数是代表写入或读取的时候一个单位的大小
第三个参数是代表写入或读取几个单位
第四个参数是FILE *
16.13 fread与feof
注意以下两段代码的区别
while (!feof(p)) { fread(&buf, 1, sizeof(buf), p); } |
while (fread(&buf, 1, sizeof(buf), p)) |
16.14 作业
有一个文本a.txt,内容一行是一个数字
有多少随机的
要求你写程序读取a.txt,将文件内容小到大排序,写入b.txt
17 基础数据结构与算法
重点:****,如果基础数据结构还不扎实的话,建议这段时间仔细的复习一下。
17.1 什么是数据结构
数据(data)是对客观事物符号表示,在计算机中是指所有能输入的计算机并被计算机程序处理的数据总称。
数据元素(data element)是数据的基本单位,在计算机中通常做为一个整体进行处理。
数据对象(data object)是性质相同的数据元素的集合,是数据的一个子集。
数据结构(data structure)是相互之间存在一种或多种特定关系的数据元素的集合。
数据类型(data type)是和数据结构密切关系的一个概念,在计算机语言中,每个变量、常量或者表达式都有一个所属的数据类型。
抽象数据类型(abstract data type ADT)是指一个数据模型以及定义在该模型上的一组操作,抽象数据类型的定义仅取决于它的一组逻辑性,与其在计算机内部如何表示以及实现无关。
17.2 什么是算法
算法是对特定问题求解的一种描述,它是指令的有限序列,其每一条指令表示一个或多个操作,算法还有以下特性:
Ø 有穷性
一个算法必须总是在执行有限步骤后的结果,而且每一步都可以在有限时间内完成。
Ø 确定性
算法中每一条指令都有确切的含义,读者理解时不会产生二义性,在任何条件下,算法只有唯一的一条执行路径,即相同的输入只能得出相同的输出。
Ø 可行性
一个算法是可行的,即算法中描述的操作都是可以通过已经实现的基本运算来实现的。
Ø 输入
一个算法有零个或者多个输入,这些输入取自与某个特定对象的集合。
Ø 输出
一个算法有一个或多个输出,这些输出是和输入有某些特定关系的量。
17.3 链表
17.3.1 单向链表定义
对于数组,逻辑关系上相邻的连个元素的物理位置也是相邻的,这种结构的优点是可以随机存储任意位置的元素,但缺点是如果从数组中间删除或插入元素时候,需要大量移动元素,效率不高。
数组的优缺点
链式存储结构的特点,元素的存储单元可以是连续的,也可以是不连续的,因此为了表示每个元素a,与其接后的元素a+1之间的关系,对于元素a,除了存储其本身信息外,还需要存储一个指示其接后元素的位置。这两部分数据成为结点(node)。
一个结点中存储的数据元素被成为数据域。存储接后存储位置的域叫做指针域。n个结点(ai(1<=i<=n)的存储映像链接成一个链表。
整个链表必须从头结点开始进行,头结点的指针指向下一个结点的位置,最后一个结点的指针指向NULL。
在链表中,通过指向接后结点位置的指针实现将链表中每个结点“链”到一起。链表中第一个结点称之为头结点。
17.3.2 单向链表数据结构定义
struct list { int data;//链表数据域 struct list *next;//链表指针域 }; |
17.3.3 单向链表的实现
struct list *create_list()//建立一个节点 void traverse(struct list *ls)//循环遍历链表 struct list *insert_list(struct list *ls, int n, int data)//在指定位置插入元素 int delete_list(struct list *ls, int n)//删除指定位置元素 int count_list(struct list *ls)//返回链表元素个数 void clear_list(struct list *ls)//清空链表,只保留首节点 int empty_list(struct list *ls)//返回链表是否为空 struct list *locale_list(struct list *ls, int n)//返回链表指定位置的节点 struct list *elem_locale(struct list *ls, int data)//返回数据域等于data的节点 int elem_pos(struct list *ls, int data)//返回数据域等于data的节点位置 struct list *last_list(struct list *ls)//得到链表最后一个节点 void merge_list(struct list *st1, struct list *ls2)//合并两个链表,结果放入st1中 void reverse(struct list *ls)//链表逆置 |
删除元素操作
逆置链表-方式1,移动指针域
逆置链表-方式2,移动数据域
逆置操作
1. 判断首节点的next是否为NULL;
2. 判断首节点next的next是否为空,如果为空证明链表除首节点之外只有一个节点,所以不需要逆置;
3. 定义一个指针last,指向首节点的next域,因为逆置之后,该域为链表尾节点;
4. 定义三个指针,分别代表前一个节点,当前节点,下一个节点;
5. 前节点指向链表首节点;
6. 当前节点指向链表首节点的next域;
7. 下一个节点为NULL;
8. 循环条件判断当前节点是否为NULL,如果为NULL退出循环;
a) 下一个节点指向当前节点的下一个节点;
b) 当前节点的下一个节点指向前一个节点;
c) 前一个节点指向当前节点;
d) 当前节点指向下一个节点;
9. 循环完成;
10. 设置last节点的next为NULL;
11. 设置链表首节点的next为前一个节点。
17.4 查找
17.4.1 顺序查找
顺序查找的过程为:从表的最后一个记录开始,逐个进行记录的关键字和给定值比较,如果某个记录的关键字与给定值相等,则查找成功,反之则表明表中没有所查找记录,查找失败。
17.4.2 二分查找
在一个已经排序的顺序表中查找,可以使用二分查找来实现。
二分查找的过程是:先确定待查记录所在的范围(区间),然后逐步缩小查找范围,直到找到或者找不到该记录为止。
假设指针low和high分别指示待查找的范围下届和上届,指针mid指示区间的中间值,即 mid=(low + high) / 2。
17.5 排序
17.5.1 冒泡排序
冒泡排序首先将一个记录的关键字和第二个记录的关键字进行比较,如果为逆序(elem[1] > elem[2]),则两个记录交换之,然后比较第二个记录和第三个记录的关键字,以此类推,直到第n-1个记录和第n个记录的关键字进行过比较为止。
上述过程称作第一次冒泡排序,其结果是将关键字最大的记录被安排到最后一个记录的位置上。然后进行第二次冒泡排序,对前n-1个记录进行同样操作,其结果是使关键字第二大记录被安置到第n-1位置上。直到将所有记录都完成冒泡排序为止。
17.5.2 选择排序
选择排序是每一次在n – I + 1(i=1,2,…n)个记录中选取关键字,最小的记录作为有序序列中第i个记录。
通过n-i次关键字间的比较,从n-i+1个记录中选取出关键字最小的记录,并 和第i(1<=i<=n)个记录交换之。
文章作者:酷帥王子
文章地址:https://www.2k8.org:443/post-170.html
版权所有 © 转载时必须以链接形式注明作者和原始出处!
发表评论: