千锋_2020最新_C语言视频教程(全家桶)代码+笔记+课件

资源下载
  1. 二一教育资源

千锋_2020最新_C语言视频教程(全家桶)代码+笔记+课件

资源简介

英雄:德玛西亚 防御:80 攻击:60
英雄:盲僧 防御:90 攻击:80
英雄:小法 防御:40 攻击:85
英雄:小炮 防御:50 攻击:90(共34张PPT)
预处理、二进制的概述和操作
2
1
2
3
4
5
C语言编译过程
#include
#define
条件编译
二进制的概述和操作
C语言编译过程
gcc -E hello.c -o hello.i 1、预处理
gcc -S hello.i –o hello.s 2、编译
gcc -c hello.s -o hello.o 3、汇编
gcc hello.o -o hello_elf 4、链接
3
C语言编译过程
预编译
将.c 中的头文件展开、宏展开生成的文件是.i文件
编译
将预处理之后的.i 文件生成 .s 汇编文件
汇编
将.s汇编文件生成.o 目标文件
链接
将.o 文件链接成目标文件
4
预处理的基本概念
C语言对源程序处理的四个步骤
预处理、编译、汇编、链接
预处理
1. 预处理是在程序源代码被编译之前,由预处理器(Preprocessor)对程序源代码进行的处理。
2. 这个过程并不对程序的源代码语法进行解析,但它会把源代码分割或处理成为特定的符号为下一步的编译做准备工作。
5
预编译命令
预处理
C编译器提供的预处理功能主要有以下四种:
1)文件包含 #include
2)宏定义 #define
3)条件编译 #if #endif ..
4)一些特殊作用的预定义宏
6
文件包含处理
文件包含处理
“文件包含处理”是指一个源文件可以将另外一个文件的全部内容包含进来
C语言提供了#include命令用来实现“文件包含”的操作
7
文件包含处理
#include < > 与 #include ""
1.""表示系统先在file1.c所在的当前目录找file1.h
如果找不到,再按系统指定的目录检索.
2.< >表示系统直接按系统指定的目录检索.
8
文件包含处理
注意:
1.#include <>常用于包含库函数的头文件
2.#include ""常用于包含自定义的头文件
3.理论上#include可以包含任意格式的文件(.c .h等)
但我们一般用于头文件的包含
9
文件包含处理
案例:
10
main.c:
fun.h
编译: gcc main.c fun.c –o main
fun.c
宏定义
宏定义
在源程序中,允许一个标识符(宏名)来表示一个语言符号字符串
用指定的符号代替指定的信息
11
宏定义
分类:
在C语言中,“宏”分为:无参数的宏和有参数的宏
12
宏定义
1.无参数的宏定义
#define 宏名 字符串
例:#define PI 3.141926
在编译预处理时,将程序中在该语句以后出现的所有的PI都用3.1415926代替
这种方法使用户能以一个简单的名字代替一个长的字符串
在预编译时将宏名替换成字符串的过程称为“宏展开”
宏定义,只在宏定义的文件中起作用
13
宏定义
案例1:
14
宏定义
案例2:
15
宏定义
说明:
1)宏名一般用大写,以便于与变量区别.
2)字符串可以是常数、表达式等.
3)宏定义不作语法检查,只有在编译被宏展开后的源程序才会报错
16
宏定义
说明:
4)宏定义不是C语言,不在行末加分号.
5)宏名有效范围为从定义到本源文件结束.
6)可以用#undef命令终止宏定义的作用域.
7)在宏定义中,可以引用已定义的宏名.
17
宏定义
2.带参数的宏定义
1)格式:#define 宏名(形参表) 字符串
2)调用:宏名(形参表)
3)宏展开:进行宏替换
18
宏定义
案例1:
19
宏定义
案例1:
20
宏定义
说明:
用3和2分别代替宏定义中的形式参数a和 b,
用3*2代替S(3,2).
因此赋值语句展开为:Area = 3*2;
21
宏定义
使用带参的宏定义最好加上括号(避免宏的副作用)
22
案例:
条件编译
条件编译
一般情况下,源程序中所有的行都参加编译.但有时希望对部分源程序行只在满足一定条件时才编译,即对这部分源程序行指定编译条件.
23
条件编译
测试存在:
24
#ifdef 标识符
程序段1
#else
程序段2
#endif
条件编译
25
#ifndef 标识符
程序段1
#else
程序段2
#endif
测试不存在:
根据表达式定义:
#if 标识符
程序段1
#else
程序段2
#endif
条件编译
条件编译的作用
1、防止头文件被重复包含引用
#ifndef __LCD_H__
#define __LCD_H__
需要声明的变量、函数
宏定义
结构体
#endif
2、软件裁剪
同样的C源代码,条件选项不同可以编译出不同的可执行程序
26
二进制的概述和操作
27
二进制(binary)的概念
在数学和数字电路中指以2为基数的记数系统,以2为基数代表系统是二进位制的。这一系统中,通常用两个不同的符号0(代表零)和1(代表一)来表示
二进制的概述和操作
28
二进制的原码、反码、补码
原码:是最简单的机器数表示法。用最高位表示符号位,‘1’表示负号,‘0’表示正号。其他位存放该数的二进制的绝对值
反码:正数的反码还是等于原码,负数的反码就是他的原码除符号位外,按位取反
补码:正数的补码等于他的原码,负数的补码等于反码+1
二进制的概述和操作
29
unsigned char a = 100;
存储时:
原码:0110 0100
反码:0110 0100
补码:0110 0100
取出时:
补码:0110 0100
反码:0110 0100
原码:0110 0100 = 100
unsigned char a = -1;
存储时:
原码:1000 0001
反码:1111 1110
补码:1111 1111
取出时:
补码:1111 1111
反码:1111 1111
原码:1111 1111 = 255
二进制的概述和操作
30
char a = -5;
存储时:
原码:1000 0101
反码:1111 1010
补码:1111 1011
取出时:
补码:1111 1011
反码:1111 1010
原码:1000 0101 = -5
char a = 129;
存储时:
原码:1000 0001
反码:1000 0001
补码:1000 0001
取出时:
补码:1000 0001
反码:1000 0000
原码:1111 1111 = -127
二进制的概述和操作
31
二进制的位移
左移 <<
右移 >>
注意右移分:逻辑右移、算数右移
二进制的概述和操作
32
右移
逻辑右移 高位补0,低位溢出
算数右移 高位补符号位,低位溢出 (有符号数)
逻辑右移:低位溢出、高位补0
0101 1010 >>3
0000 1011
算数右移:对有符号数来说低位溢出、高位补符号位
1010 1101 >> 3
1111 010 1
0101 0011 >>3
0000 101 0
二进制的概述和操作
33
左移
左移<< 高位溢出,低位补0
5<<1
THANK YOU知识点1【写代码的过程】
编辑器:程序员写代码的过程(记事本、vc6.0、vim)(让程序员看懂)
编译器:查看代码的语法错误,生成汇编语言。
汇编器:将生成好汇编语言 生成 二进制语言(目标文件)
连接器:将生成好 二进制语言+用到的库+启动代码 ==>可执行文件
知识点2【完整的c代码分析】
1、案例1:hello iot
1 //行注释:
2 #include//std 标准 i输入 o输出(标准的输入输出头文件)
3
4 /* 块注释 不能嵌套
5 main 是程序的入口 有且仅有一个
6 main左边的int 代表的是函数的返回值类型
7 ()里面函数的形参(函数外部将数据传递到函数内部的桥梁)
8 */
9 int main(int argc,char *argv[])
10 {//函数的功能都在{}里面实现
11 //使用任何东西 必先存在
12 //printf:将""中的字符串 输出到 终端上
13 printf("hello iot\n");//来至系统库文件
14 //;c语言的语句结束标记
15
16 //如果你的代码 一闪而过 可以用带阻塞的代码
17 getchar();
18 return 0;
19 }
20 //int char return 都是后面要讲的关键字
总结:
1、main有且只有一个
2、printf的头文件 是stdio.h
3、注释 行注释 块注释(不嵌套)
案例2:求圆的面积
知道的条件:半径r
算法:面积= π*r*r
解析步骤:
1、r通过 键盘输入 或者 特定的值
2、定义一个面积变量 area = π*r*r
3、将圆的面积输出到终端上
1 #include
2 //定义一个宏 建议大写 和普通变量区分开
3 #define PI 3.14
4
5 int main(int argc,char *argv[])
6 {
7 float r=0.0f;//定义一个r变量 系统给r开辟4字节空间
8 float area = 0.0f;//定义一个面积变量
9
10 //获得半径 从键盘获得scanf
11 printf("请输入圆的半径r:");
12 scanf("%f", &r);//带阻塞
13
14 //算法:计算面积area = 3.14 * r * r
15 area = PI*r*r;
16
17 //将面积输出%.2f中的.2表示小数部分保留两位
18 printf("area = %.2f\n",area);
19
20 return 0;
21 }
运行结果:
案例:用分函数的方式 求两个数的和
步骤分析:
1、定义两个变量data1 data2 获取键盘输入
2、定义一个函数 去 计算上面两个变量data1 data2的和
在 函数的内部 计算(怎么将数据 传递 到函数内部呢?)
需要用形参 将data1 data2传递到 函数内部
3、得到函数内部的计算结果(怎么得到呢?)
通过函数的返回值 得到函数的计算结果
1 #include
2
3 int add_fun(int a,int b)
4 {
5 return a+b;
6 }
7
8 int main(int argc,char *argv[])
9 {
10 //定义两个变量 获取键盘输入
11 //int data1 = 0;
12 //int data2 = 0;
13 int data1 = 0, data2 = 0;
14 int ret = 0;//存放函数的结果
15
16 printf("请输入两个int变量:");
17 scanf("%d %d",&data1,&data2);
18
19 //函数的调用
20 ret = add_fun(data1,data2);//a = data1 b = data2
21
22 //输出ret的结果
23 printf("ret = %d\n",ret);
24
25 return 0;
26 }
结果:知识点1【内存的分区】(了解)
知识点2【普通局部变量、普通全局变量、静态局部变量、静态全局变量】
1、普通局部变量
2、普通全局变量
3、静态局部变量
4、静态全局变量
知识点3【全局函数(普通函数) 和 静态函数(局部函数)】
1、全局函数:普通函数
2、静态函数(局部函数)
知识点4【gcc编译过程】(了解)
知识点5【头文件包含<>,""】(了解)
知识点6【define 宏】、
1、不带参数的宏
2、带参数的宏 (宏 函数)
3、带参数的宏(宏函数) 和 普通函数 有啥区别
知识点7【条件编译】
案例1:测试不存在
案例2:测试存在
案例3:判断表达式
综合案例:通过条件编译 控制大小写的转换
知识点1【内存的分区】(了解)
知识点2【普通局部变量、普通全局变量、静态局部
变量、静态全局变量】
1、普通局部变量
定义形式:在{}里面定义的普通变量 就是普通局部变量。
作用范围:离它最近的{}之间有效
生命周期:离它最近的{}之间有效,离开{}的局部变量 系统自动回收
存储区域:栈区
注意事项:
a、普通局部变量不初始化 内容不确定
b、普通局部变量 同名 就近原则
1 void test01()
2 {
3 //局部变量 同名 就近原则
4 int data = 100;
5 {
6 int data = 200;
7 printf("A:data = %d\n",data);//200
8 }
9
10 printf("B:data = %d\n",data);//100
11 }
2、普通全局变量
定义形式:定义在函数外边的变量 就是普通全局变量
1 int data;//普通全局变量 在函数外边定义
2 void test02()
3 {
4
5 }
作用范围:
a、当前源文件 都有效
1 #include
2 extern int data;//声明一下 不要赋值
3 void test01()
4 {
5 printf("test01 中 data = %d\n",data);//100
6 }
7
8 int data=100;//普通全局变量 在函数外边定义
9
10 void test02()
11 {
12 printf("test02 中 data = %d\n",data);//100
13 }
14 int main(int argc,char *argv[])
15 {
16 printf("main 中 data = %d\n",data);//100
17 test01();
18 test02();
19
20 }
b、其他源文件使用全局变量时 必须加extern声明。
//extern 本质:告诉编译器 变量/函数 来至其他源文件,请通过编译
代码:
main.c
1 #include
2 extern void my_printf(void);
3 extern int data;//声明
4 int main(int argc,char *argv[])
5 {
6 printf("main 中 data = %d\n",data);//100
7
8 my_printf();
9
10 }
11 int data = 100;
fun.c
1 #include
2 extern int data;//声明data来至其他源文件
3 void my_printf(void)
4 {
5 printf("在fun.c中 data = %d\n",data);
6 }
运行结果:
生命周期:整个进程 都有效(程序结束的时候 全局变量 才被释放)
存储区域:全局区
注意事项
a、全局变量 不初始化 内容为0。
b、如果全局变量 要在其他源文件中使用 必须在所使用的源文件中加extern声明。
c、如果全局变量 和 局部变量 同名 在{}中优先使用局部变量
3、静态局部变量
定义形式:在{}中定义 前面 必须加static 修饰 这样的变量 叫 静态局部变量。
1 void test01()
2 {
3 static int num;//静态局部变量
4 return;
5 }
作用范围:离它最近的{}之间有效。
1 void test01()
2 {
3 {
4 static int num;//静态局部变量
5 }
6 //说明 静态局部变量 只在离它最近的{}有效
7 printf("num = %d\n",num);//err 不识别num
8 return;
9 }
生命周期:整个进程 ,(程序结束的时候 静态局部变量 才被释放)
-----------------案例:普通局部变量----------------------
1 #include
2 void fun1(void)
3 {
4 int num = 10;//普通的局部变量
5 num++;
6
7 printf("num = %d\n",num);
8 return;
9 }
10 int main(int argc,char *argv[])
11 {
12 fun1();//11
13 fun1();//11
14 fun1();//11
15 fun1();//11
16 return 0;
17 }
-----------案例:静态局部变量--------------------------
1 #include
2 void fun1(void)
3 {
4 //静态局部变量 只能被初始化一次
5 //静态局部变量 生命周期 是整个进程
6 static int num = 10;//静态局部变量
7 num++;
8 printf("num = %d\n",num);
9 return;
10 }
11 int main(int argc,char *argv[])
12 {
13 fun1();//11
14 fun1();//12
15 fun1();//13
16 fun1();//14
17 return 0;
18 }
存储区域:全局区
注意事项:
a、静态局部变量 不初始化 内容为0.(全局区)
b、只能被定义一次(重要)
4、静态全局变量
定义形式:在函数外边定义 同时加static 这样的变量就是 静态全局变量
1 #include
2 static int data = 10;//静态全局变量
3 int main(int argc,char *argv[])
4 {
5
6 return 0;
7 }
作用范围:当前源文件 有效 不能在其他源文件中使用。
生命周期:整个进程,(程序结束 静态全局变量才被释放)
存储区域:全局区
注意事项:
1、静态全局变量 不初始化 内容为0
2、静态全局变量 只在当前源文件 有效
知识点3【全局函数(普通函数) 和 静态函数
(局部函数)】
1、全局函数:普通函数
1 void my_fun(void)
2 {
3 printf("(全局函数)普通函数\n");
4 return;
5 }
特点:其他源文件 可以使用 全局函数,必须加extern 声明
2、静态函数(局部函数)
1 static void my_static_fun(void)
2 {
3 printf("(静态函数)局部函数\n");
4 return;
5 }
特点:只能在当前源文件使用 不能在其他源文件使用。
注意:如果想在其他源文件 调用 静态函数 需要将静态函数 封装在 全局函数中。同时全局
函数 和静态函数 必须是同一个源文件。
fun.c
1
2 static void my_static_fun(void)
3 {
4 printf("(静态函数)局部函数\n");
5 return;
6 }
7
8 void my_fun(void)
9 {
10 printf("(全局函数)普通函数\n");
11 my_static_fun();
12 return;
13 }
main.c
1 #include
2 extern void my_fun(void);
3 //static void my_static_fun(void);
4 int main(int argc,char *argv[])
5 {
6 my_fun();
7 //my_static_fun();
8 return 0;
9 }
案例:
知识点4【gcc编译过程】(了解)
1 #include
预处理:头文件包含、宏替换、条件编译、删除注释 不做语法检查
编译:将预处理后的文件 生成 汇编文件 语法检查
汇编:将汇编文件 编译 二进制文件
链接:将众多的二进制文件+库+启动代码 生成 可执行文件
总结:一步到位 编译: gcc 源文件 -o 可执行文件
知识点5【头文件包含<>,""】(了解)
#include 表示从系统的指定目录下寻找hehe.h
#include "hehe.h" 表示 先从源文件 所在的目录寻找 如果找不到 再到系统指定的目录下
找。
my_fun.h
1 #define PI 3.14f
main.c
1 #include//用于包含系统的头文件
2 #include"my_fun.h"//用户包含 用户自定义的 头文件
3
4 int main(int argc,char *argv[])
5 {
6 printf("PI = %f\n",PI);
7 return 0;
8 }
知识点6【define 宏】、
宏只在当前源文件有效
宏 一般为 大写。(将宏 和 普通变量 区分开)
1、不带参数的宏
1 #include
2
3 //宏 后面不要加;
4 #define N "hehe"
5 void test01()
6 {
7 //在预处理阶段 "hehe" 替换 代码中所有出现的N (宏展开)
8 printf("%s\n",N);
9 return;
10 }
11 int main(int argc,char *argv[])
12 {
13 test01();
14 return 0;
15 }
终止宏 的作用范围: #undef 宏名
1 #include
2
3 //宏 后面不要加;
4 #define N "hehe"
5 void test01()
6 {
7 printf("%s\n",N);//OK 识别的
8
9 //使用#undef N终止 N的作用
10 #undef N
11
12 //printf("%s\n",N);//err 不识别N
13
14 return;
15 }
16 int main(int argc,char *argv[])
17 {
18 test01();
19 return 0;
20 }
2、带参数的宏 (宏 函数)
#define 宏名(参数1,参数2,...) 字符串
1 //宏的参数 a b 不能写类型
2 //#define MY_ADD(int a, int b) a+b //错误
3 #define MY_ADD(a,b) a+b
4
5 //调用 宏名(参数)
6 MY_ADD(10,20);// 10+20
案例1:
1 //宏展开 本质 就是替换
2 #define MY_MUL1(a,b) a*b
3 #define MY_MUL2(a,b) ((a)*(b))
4 void test01()
5 {
6 printf("%d\n", MY_MUL1(10,20));//MY_MUL1(10,20) == 10*20
7
8 //MY_MUL1( 10+10, 20+20 )==10+10*20+20
9 printf("%d\n", MY_MUL1(10+10,20+20));//230 不能保证完整性
10
11 //MY_MUL2(10+10,20+20) == ((10+10)*(20+20))
12 printf("%d\n", MY_MUL2(10+10,20+20));//800 保证完整性
13
14 return;
15 }
16 int main(int argc,char *argv[])
17 {
18 test01();
19 return 0;
20 }
3、带参数的宏(宏函数) 和 普通函数 有啥区别
带参数的宏(宏函数) 调用多少次 就会展开多少次,执行代码的时候 没有函数调用的过
程,也不需要函数的出入栈,所以带参数的宏 浪费空间 节省了时间。
代参的函数:代码只有一份,存在代码段, 调用的时候去代码段读取函数指令,调用的时
候 要压栈(保存调用函数前的相关信息),调用完出栈(恢复调用函数前的相关信息),
所以函数浪费了时间 节省空间。
1 #include
2
3 //宏展开 本质 就是替换
4 //宏函数
5 #define MY_MUL1(a,b) a*b
6 #define MY_MUL2(a,b) ((a)*(b))
7
8 //普通函数
9 int my_mul(int a,int b)//a=10+10=20 b=20+20=40
10 {
11 printf("a = %d\n",a);//20
12 printf("b = %d\n",b);//40
13
14 return a*b;
15 }
16 void test01()
17 {
18 //预处理阶段完成 宏的替换
19 printf("%d\n", MY_MUL1(10,20));//MY_MUL1(10,20) == 10*20
20 printf("%d\n", MY_MUL1(10+10,20+20));//10+10*20+20=230 不能保证完整性
21 printf("%d\n", MY_MUL2(10+10,20+20));//((10+10)*(20+20))=800 保证完整性
22
23 //执行
24 printf("%d\n", my_mul(10+10, 20+20));//
25
26 return;
27 }
28 int main(int argc,char *argv[])
29 {
30 test01();
31 return 0;
32 }
案例:
1 #define MY_ADD(a,b) a+b
2 #define MY_MUL1(a,b) a*b
3
4 printf("%d\n",MY_MUL( MY_ADD(10+10, 20+20), MY_MUL(10+10,20+20) ) );
知识点7【条件编译】
案例1:测试不存在
运行结果:
案例2:测试存在
运行结果:
案例3:判断表达式
综合案例:通过条件编译 控制大小写的转换
1 #include
2
3 int main()
4 {
5 char buf[128]="";
6 int i=0;
7 printf("请输入字符串:");
8 //fgets 会获取 换行符 '\n'
9 fgets(buf,sizeof(buf), stdin);
10 //去掉换行符 strlen返回的是字符串是长度 不包含'\0'
11 //strlen(buf)‐1 这是 换行符 的下标位置
12 buf[strlen(buf)‐1] = 0;
13
14 //while(buf[i] != '\0')
15
16 //char buf[128];
17 //buf[i]是取数组中的第i个元素的值。
18
19 while(buf[i])//最后一个元素是'\0' == 0==假 循环进不去
20 {
21 #if 1
22 if(buf[i]>='A' && buf[i]<='Z')
23 buf[i] = buf[i]+32;
24 #else
25 if(buf[i]>='a' && buf[i]<='z')
26 buf[i] = buf[i]‐32;
27 #endif
28 i++;
29 }
30
31
32 printf("buf = %s\n",buf);
33 return 0;
34 }德玛西亚P<盲僧ZP小法(U小炮2Z知识点1【数据类型】
知识点2【unsigned 和 signed】(重要)
1、无符号数 unsigned
2、有符号数 signed 默认一般省略
知识点3【结构体 struct和共用体union】
1、struct 结构体 中的成员 拥有独立的空间
2、union 共用体 中的成员 共享同一份空间
知识点4【enum 和 void】(了解)
1、enum 枚举 将变量要赋值的值 一一列举出来
2、void 无类型 (重要)
知识点5【其他关键字】(了解)
1、register 寄存器变量
2、typedef 为已有的类型取个别名
3、volatile 防止编译器优化 强制访问内存操作
知识点6【常量与变量】(重要)
1、常量 值不能被修改 (千万不能反过来说:值不能修改就是常量)
2、变量 系统根据变量的类型 开辟对应的空间 其值可以被修改
知识点7【八进制 十进制 十六进制】
知识点9【整型变量的操作-读、写】取值、赋值
2、整形的输出形式
知识点10【实型 常量】
知识点11【字符常量和变量】(重要)
1、字符常量 char
知识点12【转义字符】
知识点13【字符串】"" 双引号作用
知识点14【输出的格式回顾】(了解)
知识点15【typedef】
知识点1【数据类型】
数据的不同类型 目的 合理的利用空间
计算机存储的是二进制。一位二进制 只能存放0或1 1b
1字节 == 8b(八位二进制) 0000 0000 ~ 1111 1111
1B == 8b
1KB == 1024B 10月24号程序员节日
1MB == 1024 KB
1GB == 1024 MB
1TB == 1024 GB
1PB == 1024 TB
1EB == 1024 PB
-------------------------在32位平台---------------------------------
char 字符类型 占1字节的空间(8位二进制位)
short 短整型 占2字节的空间(16位二进制位)
int 整型 占4字节的空间(32位二进制位)
long 长整型 占4字节的空间(32位二进制位)
float 单精度浮点型 占4字节的空间(32位二进制位)
double 双精度浮点型 占8字节的空间(64位二进制位)
案例:验证数据类型的长度
-------------------------在64位平台---------------------------------
char 字符类型 占1字节的空间(8位二进制位)
short 短整型 占2字节的空间(16位二进制位)
int 整型 占4字节的空间(32位二进制位)
long 长整型 占8字节的空间(64位二进制位)
float 单精度浮点型 占4字节的空间(32位二进制位)
double 双精度浮点型 占8字节的空间(64位二进制位)
案例:验证数据类型的长度
sizeof测量类型的长度
1 #include
2 int main(int argc,char *argv[])
3 {
4 printf("sizeof(char)=%d\n",sizeof(char));
5 printf("sizeof(short)=%d\n",sizeof(short));
6 printf("sizeof(int)=%d\n",sizeof(int));
7 printf("sizeof(long)=%d\n",sizeof(long));
8 printf("sizeof(float)=%d\n",sizeof(float));
9 printf("sizeof(double)=%d\n",sizeof(double));
10 return 0;
11 }
运行结果:
知识点2【unsigned 和 signed】(重要)
1、无符号数 unsigned
数据没有符号位,自身的所有二进制位 都是数据位
比如:unsigned char 0000 0000 ~ 1111 1111
2、有符号数 signed 默认一般省略
二进制最高位 为符号位,其他位 为数据位。
signed char xxxx xxxx (x:0~1)
最高位为1表示负数 最高位为0 表示正数
负数:1xxx xxxx 正数:0xxx xxxx
signed char 他的表示范围:1111 1111~1000 0000 ~0000 0000 ~ 0111 1111
案例:一个字节:-10 == 1000 1010
案例:
1 #include
2 int main(int argc,char *argv)
3 {
4 //定义一个有符号int
5 signed int num1 = 10;//num1有符号int
6
7 //signed 默认是省略(推荐)
8 int num2 = 10;//num2也是有符号int
9
10 //unsigned 表示无符号数 不能省略
11 unsigned int num3 = 10;
12
13 return 0;
14 }
知识点3【结构体 struct和共用体union】
1、struct 结构体 中的成员 拥有独立的空间
1 struct data1{
2 char a;
3 short b;
4 int c;
5 };
6 a b c就是结构体data1中的成员
2、union 共用体 中的成员 共享同一份空间
1 union data2{
2 char a;
3 short b;
4 int c;
5 };
知识点4【enum 和 void】(了解)
1、enum 枚举 将变量要赋值的值 一一列举出来
enum BOOL{false,true};
enum BOOL bool = false;
2、void 无类型 (重要)
不能用void 定义变量
int num;//√
void num;//× 系统不能确定给num开辟多大的空间
知识点5【其他关键字】(了解)
auto自动类型,register 寄存器变量,static静态变量,const只读
sizeof 测类型大小
typedef为已有的类型 重新取个别名
volatile 防止编译器优化
1、register 寄存器变量
总结:
1、如果没显示标明 register ,就类似int num,如果num被高频繁使用 系统也会放入
寄存器中
2、register int num;//显示的将num放入寄存器中
3、寄存器的变量 不能取地址 &num
1 #include
2 int main(int argc,char *argv[])
3 {
4 register int num = 10;
5 //%p输出地址
6 printf("%p\n",&num);//错误 不能对寄存器变量取地址
7
8 return 0;
9 }
2、typedef 为已有的类型取个别名
1 //给已有的int类型取个别名 INT32
2 typedef int INT32;
3 void test02()
4 {
5 int num1 = 10;
6 INT32 num2 = 20;
7 printf("num1 = %d\n",num1);
8 printf("num2 = %d\n",num2);
9 }
10 int main(int argc,char *argv[])
11 {
12 test02();
13
14 return 0;
15 }
结果:
3、volatile 防止编译器优化 强制访问内存操作
知识点6【常量与变量】(重要)
1、常量 值不能被修改 (千万不能反过来说:值不能修改就是常量)
10 20 4.14 ‘a’ "abcd"
2、变量 系统根据变量的类型 开辟对应的空间 其值可以被修改
变量名 代表的是 空间的内容 (重要)
操作变量 就是 对空间内容的操作
变量名的命名规则:由数值、字母、下划线组成但不能以数字开头。
A: num_1 B: _num1 C: 1_num D: num
知识点7【八进制 十进制 十六进制】
十进制:0~9
八进制:0~7
十六进制:0~9 a~f
八进制 十进制 十六进制 都是整型的输出形式。
不同进制 仅仅是数据的表现形式 不会修改数据本身(重要)
1 void test03()
2 {
3 int num = 100;
4
5 //十进制 输出 %d %u %ld %lu
6 printf("十进制:num=%d\n", num);//100
7 //八进制 输出 %o 以0开头
8 printf("八进制:num=%#o\n", num);//0144
9 //十六进制 输出 %x 以0x开头
10 printf("十六进制:num=%#x\n", num);//0x64
11
12 //不同进制 仅仅是数据的表现形式 不会修改数据本身
13 }
14 int main(int argc,char *argv[])
15 {
16 test03();
17
18 return 0;
19 }
运行结果:
知识点9【整型变量的操作-读、写】取值、赋值
1 void test04()
2 {
3 //局部变量不初始化 内容随机
4 //int num;
5 int data = 0;
6 int num = 0;
7 printf("num = %d\n",num);//读 取值
8
9 num = 100;//写 赋值
10 printf("num = %d\n",num);
11
12 data = num;//num读 data写
13 printf("data = %d\n",data);
14
15 //获取键盘输入
16 printf("请输入一个int数据:");
17 scanf("%d", &data);//&data 代表是data对应空间的起始地址
18 printf("data = %d\n",data);
19
20 }
运行结果:
2、整形的输出形式
1 void test05()
2 {
3 int num1 = 0;
4 //%d 有符号int数据输出
5 printf("num1 = %d\n", num1);
6
7 unsigned int num2 = 0;
8 //%u 无符号int数据输出
9 printf("num2 = %u\n", num2);
10
11 long num3 = 0;
12 //%ld 有符号long数据输出
13 printf("num3 = %ld\n", num3);
14
15 unsigned long num4 = 0;
16 //%lu 有符号long数据输出
17 printf("num4 = %lu\n", num4);
18
19 short num5 = 0;
20 //%hd 有符号short数据 输出
21 printf("num5 = %hd\n",num5);
22
23 unsigned short num6 = 0;
24 //%hu 无符号short数据 输出
25 printf("num6 = %hu\n",num6);
26 }
unsigned short num6=0;
scanf("%hu",&num6);//输入
知识点10【实型 常量】
1 void test06()
2 {
3 //赋值语句 = 两边的类型 尽量保证一致
4 float f = 3.14f;//有没有问题
5 double d = 3.14;
6
7 //不以f结尾的 实型常量 为double类型
8 printf("sizeof(3.14) =%d\n",sizeof(3.14));//8
9
10 //以f结尾的 实型常量 为float类型
11 printf("sizeof(3.14f) =%d\n",sizeof(3.14f));//4
12
13 //%f 输出 float数据
14 printf("f = %f\n",f);
15
16 //%lf 输出 double数据
17 printf("d = %lf\n", d);
18
19 scanf("%f", &f);
20 scanf("%lf", &d);
21 }
知识点11【字符常量和变量】(重要)
1、字符常量 char
1 void test07()
2 {
3 //%c 输出的字符
4 //printf("%c\n",'a');
5
6 //字符变量
7 //ch 存储的是 ‘a’的ASCII值
8 char ch = 'a';
9 printf("ch = %c\n",ch);
10 //%d 输出的字符的ASCII值
11 printf("ch = %d\n",ch);
12
13 //'a' 单引号 表示的取 字符的ASCII
14 //'a' == 97是完全等价
15 ch = 97;
16 printf("ch = %c\n",ch);
17 //%d 输出的字符的ASCII值
18 printf("ch = %d\n",ch);
19
20 ch = ch+1;
21 printf("ch = %c\n",ch);
22 //%d 输出的字符的ASCII值
23 printf("ch = %d\n",ch);
24
25 }
26 int main(int argc,char *argv[])
27 {
28 test07();
29
30 return 0;
31 }
总结:
1、'a' 单引号 表示取a的ASCII值
2、字符在计算机及存储的是ASCII
3、'a' ==97
1 void test08()
2 {
3 char ch = 'a';
4 printf("%d\n",sizeof('a'));//4字节
5 printf("%d\n",sizeof(char));//1 字节 为啥?
6 }
7 int main(int argc,char *argv[])
8 {
9 test08();
10
11 return 0;
12 }
字符获取键盘输入:
1 void test09()
2 {
3 char ch;
4 printf("请输入一个字符:");
5 //scanf中%c只能提取一个字符
6 //scanf("%c", &ch);
7 ch = getchar();//获取一个字符
8
9 printf("ch = %c\n",ch);
10 printf("ch = %d\n",ch);
11 }
12 int main(int argc,char *argv[])
13 {
14 test09();
15
16 return 0;
17 }
运行结果:
案例:提高 键盘输入 “abc” 只取 'a'和‘c’
1 void test09()
2 {
3 char ch1,ch2;
4 printf("请输入abc:");
5
6 //提取a
7 ch1 = getchar();
8 getchar();//丢弃一个字符
9 ch2 = getchar();
10
11 printf("ch1 = %c\n", ch1);
12 printf("ch2 = %c\n", ch2);
13 }
14 int main(int argc,char *argv[])
15 {
16 test09();
17
18 return 0;
19 }
运行结果:
知识点12【转义字符】
1 void test10()
2 {
3 // '\n' 换行 '\t' tab
4 //printf("##%c##\n", '\\');//两\\输出一个\
5 printf("##%%##\n");
6
7 char ch = '\0';
8 printf("A:%d\n", '0');//48
9 printf("B:%d\n", '\0');//0
10 printf("C:%d\n", 0);//0
11 printf("D:%d\n", "0");//字符‘0’地址
12
13 }
14 int main(int argc,char *argv[])
15 {
16 test10();
17
18 return 0;
19 }
知识点13【字符串】"" 双引号作用
1 void test11()
2 {
3 //%s 就是输出字符串
4 //''取的是字符的ASCII值 ""取的是字符串的首元素的地址
5 //%s 从字符串的首元素 逐个字符输出 遇到'\0' (重要)
6 printf("%s\n", "hello");
7 //系统会在字符串末尾自动添加一个结束字符'\0'
8 printf("%d\n",sizeof("hello"));
9
10 printf("%s\n","hello world");
11 printf("%s\n","hello\0world");
12 printf("##%s##\n","\0hello\0world");
13 }
14 int main(int argc,char *argv[])
15 {
16 test11();
17
18 return 0;
19 }
运行结果:
请描述:'a' 和 "a"的区别?
知识点14【输出的格式回顾】(了解)
1 void test12()
2 {
3 printf("##############\n");
4 //%5d 表示占5个终端位宽 右对齐
5 printf("##%5d##\n",123);
6
7 //%‐5d 表示占5个终端位宽 左对齐
8 printf("##%‐5d##\n",123);
9
10 //%05d 表示占5个终端位宽 右对齐 不足补0
11 printf("##%05d##\n",123);
12
13 //千万不能写 %‐05d
14 //printf("##%‐05d##\n",123);
15
16 //%5.2f 5表示总位宽为5 .2表示小数位保留2位
17 printf("##%5.2f##\n", 3.14159f);
18
19 }
20 int main(int argc,char *argv[])
21 {
22 test12();
23
24 return 0;
25 }
运行结果:
知识点15【typedef】
为已有的类型重新 取个别名。
1、用已有的类型 定义 一个变量
2、用别名 替换 变量名
3、在整个表达式的前方加上 typedef.
1 案例1:
2 int INT32
3 typedef int INT32;
4
5 案例2:给int arr[5] 取个别名 ARR
6 typedef int ARR[5];
7 ARR arr;//arr就是一个拥有5个int元素的数组知识点1【指针数组】(了解)
知识点2【数组指针】(了解)
总结:
知识点3【二维数组的分析】(了解)
知识点4【数组指针 与 二维数组的关系】(了解)
知识点6【任何维度的数组 在物理存储上 都是一维的】(了解)
知识点7【多级指针】(了解)
知识点8【指针变量作为函数的参数】
1、如果想在函数内部 修改 外部变量的值 就需要将外部变量的地址 传递给函数(以指针变
量作为函数的参数)(重要!!!!)
知识点引入:
知识点9【一维数组名作为函数的参数】
1、如果函数内部想操作(读、写)外部数组的元素,请将外部数组的数组名传递函数。(重
要!!!)
2、一维数组作为函数的形参 会被优化成一级指针变量。
知识点10【二维数组名作为函数的参数】(了解)
1、如果函数内部想操作(读、写)外部数组的元素,请将外部数组的数组名传递函数。(重
要!!!)
2、二维数组名 作为函数的形参 会被优化成 数组指针。
知识点11【指针作为函数的返回值】
1、函数不要返回普通局部变量的地址。
2、解决上述问题:
知识点12【函数名 代表的是函数的入口地址】
知识点1【指针数组】(了解)
指针数组:本质是数组 只是数组的每个元素 是指针。
案例:
1 void test01()
2 {
3 int num1 = 10;
4 int num2 = 20;
5 int num3 = 30;
6 //指针数组
7 int *arr[3]={&num1, &num2, &num3};
8 char *arr2[3];
9 //arr[0] = &num1, arr[1]=&num2, arr[2]=&num3
10
11 printf("%d\n", *arr[1]);//*arr[1]=*&num2=num2
12
13 printf("%d\n",sizeof(arr));//12
14 printf("%d\n",sizeof(arr2));//12
15 return;
16 }
案例:
1 void test02()
2 {
3 char *arr[3]={"hehehe","hahaha","heiheihei"};
4 printf("%d\n", sizeof(arr));//12
5
6 printf("%s\n", arr[1]);
7 printf("%c\n", *(arr[1]+1));
8 }
运行结果:
知识点2【数组指针】(了解)
案例:
1 void test03()
2 {
3 int arr[5]={10,20,30,40};
4 int (*p)[5];//数组指针:本质是一个指针变量 只是该变量 保存的是数组的首地址
5
6 printf("%d\n",sizeof(p));//4
7
8 printf("p=%u\n", p);
9 printf("p+1=%u\n", p+1);
10
11 p = &arr;//&arr 才代表数组的首地址
12
13 printf("%d\n", *(*p+3));//40
14 //*(*p+3) == *(*(p+0)+3)==*(p[0]+3)==p[0][3]
15 printf("%d\n", p[0][3]);//40
16
17 }
运行结果:
总结:
指针数组:本质是数组 只是数组的每个元素是指针
数组指针:本质是指针变量 只是保存的是 数组的首地址
知识点3【二维数组的分析】(了解)
案例:int arr[3][4];
*arr+2 ://第0行 第2列的列地址
arr[1]://*(arr+1) 第1行第0列的列地址
&arr[0]+2: &*(arr+0)+2==arr+2 表示的是第2行的行地址
**arr://**arr == *(*(arr+0)+0)==arr[0][0]
知识点4【数组指针 与 二维数组的关系】(了解)
1 void test04()
2 {
3 int arr[3][4]={{1,2,3,4},{5,6,7,8},{9,10,11,12}};
4 int i=0,j=0;
5 //数组指针 本质是指针变量
6 int (*p)[4] =arr;
7 printf("%d\n",sizeof(p));
8
9 for(i=0;i<3;i++)
10 {
11 for(j=0;j<4;j++)
12 {
13 //printf("%d ", *(*(p+i)+j));
14 printf("%d ", p[i][j]);
15 }
16 printf("\n");
17 }
18 }
运行结果:
知识点6【任何维度的数组 在物理存储上 都是一维
的】(了解)
1 void test05()
2 {
3 int arr[3][4]={{1,2,3,4},{5,6,7,8},{9,10,11,12}};
4 int i=0;
5 int *p = &arr[0][0];
6 for(i=0;i<3*4; i++)
7 {
8 //printf("%d ", *(p+i));
9 printf("%d ", p[i]);
10
11 }
12 printf("\n");
13 }
运行结果:
知识点7【多级指针】(了解)
知识点8【指针变量作为函数的参数】
1、如果想在函数内部 修改 外部变量的值 就需要将外部变量的地址 传
递给函数(以指针变量作为函数的参数)(重要!!!!)
知识点引入:
1 void my_swap(int a,int b)//a=data1, b=data2
2 {
3 int tmp=0;
4 tmp = a;
5 a = b;
6 b = tmp;
7 }
8 void test06()
9 {
10 int data1 = 10,data2 = 20;
11 printf("data1=%d,data2=%d\n",data1,data2);
12 //如果传递的是值 函数内部 是无法修改外部变量值
13 my_swap(data1,data2);
14 printf("data1=%d,data2=%d\n",data1,data2);
15 }
16 int main(int argc,char *argv[])
17 {
18 test06();
19 return 0;
20 }
上面的代码 是无法修改data1 data2的值
解决上面代码的问题 就需要将data1 data2的地址 传递到函数。
1 void my_swap2(int *p1,int *p2)//p1=&data1 p2=&data2
2 {
3 int tmp;
4 tmp = *p1;
5 *p1 = *p2;
6 *p2 = tmp;
7 }
8 void test07()
9 {
10 int data1 = 10,data2 = 20;
11 printf("data1=%d,data2=%d\n",data1,data2);
12
13 //函数内部 修改外部变量的值 需传递外部变量的地址
14 my_swap2(&data1,&data2);
15 printf("data1=%d,data2=%d\n",data1,data2);
16 }
17 int main(int argc,char *argv[])
18 {
19 test07();
20 return 0;
21 }
运行结果:
案例:
1 void my_set_p(int **tmp_p)//tmp_p = &p;
2 {
3 static int num = 100;
4 //*tmp_p == p
5 *tmp_p = #//p = & num;
6
7 }
8 void test08()
9 {
10 int *p = NULL;
11
12 //在函数内部 更改p的指向(在函数内部 给p赋值 就必须传递p的地址)
13 my_set_p(&p);
14
15 printf("*p = %d\n", *p);//100
16
17 }
18 int main(int argc,char *argv[])
19 {
20 test08();
21 return 0;
22 }
运行结果:
总结:函数内部修改外部变量的值 请传外部变量的地址.
外部变量为0级指针 函数的形参为1级指针
外部变量为1级指针 函数的形参为2级指针
外部变量为2级指针 函数的形参为3级指针
。。。。。。
外部变量为n-1级指针 函数的形参为n级指针
知识点9【一维数组名作为函数的参数】
1、如果函数内部想操作(读、写)外部数组的元素,请将外部数组的数
组名传递函数。(重要!!!)
2、一维数组作为函数的形参 会被优化成一级指针变量。
案例:
1 //void my_input_array(int arr[5], int n)
2 //void my_input_array(int arr[], int n)
3 //一维数组 作为函数的形参会被优化成 指针变量
4 void my_input_array(int *arr, int n)
5 {
6 int i=0;
7 printf("B:%d\n",sizeof(arr));//4
8 printf("请输入%d个int数据\n",n);
9 for(i=0;i10 {
11 scanf("%d", arr+i);
12 }
13 return;
14 }
15 void my_print_array(int *arr, int n)
16 {
17 int i=0;
18 for(i=0;i19 {
20 //printf("%d ", *(arr+i));
21 printf("%d ", arr[i]);
22
23 }
24 printf("\n");
25 }
26 void test09()
27 {
28 //arr作为类型 代表的是数组的总大小
29 //arr作为地址 代表的是数组的首元素地址
30 int arr[5]={0};
31 int n = sizeof(arr)/sizeof(arr[0]);
32
33 printf("A:%d\n",sizeof(arr));//20
34
35 //定义一个函数 给arr获取键盘输入
36 my_input_array(arr, n);
37
38 //定义一个函数 遍历数组元素
39 my_print_array(arr, n);
40 }
41 int main(int argc,char *argv[])
42 {
43 test09();
44 return 0;
45 }
运行结果:
知识点10【二维数组名作为函数的参数】(了解)
1、如果函数内部想操作(读、写)外部数组的元素,请将外部数组的数
组名传递函数。(重要!!!)
2、二维数组名 作为函数的形参 会被优化成 数组指针。
int arr1[5] ------> int *p;
int arr2[3][4]----->int (*p1)[4];
int arr3[3][4][5]---->int (*p2)[4][5];
int arr4[3][4][5][6]--->int (*p3)[4][5][6];
1 //当二维数组作为函数形参 会被优化成 数组指针
2 void my_print_two_array(int (*arr)[4], int row, int col)
3 {
4 int i=0,j=0;
5 printf("B:%d\n",sizeof(arr));//4
6 for(i=0;i7 {
8 for(j=0;j9 {
10 printf("%d ",arr[i][j]);
11 }
12 printf("\n");
13 }
14 }
15 void test10()
16 {
17 int arr[3][4]={1,2,3,4,5,6,7,8,9,10,11,12};
18 int row = sizeof(arr)/sizeof(arr[0]);//行数
19 int col = sizeof(arr[0])/sizeof(arr[0][0]);//列数
20
21 printf("A:%d\n",sizeof(arr));//48
22
23 my_print_two_array(arr, row, col);
24
25 }
26 int main(int argc,char *argv[])
27 {
28 test10();
29 return 0;
30 }
运行结果:
知识点11【指针作为函数的返回值】
1、函数不要返回普通局部变量的地址。
1 int* get_addr(void)
2 {
3 int num = 1000;
4
5 return #//不要返回普通局部变量地址
6 }
7 void test11()
8 {
9 int *p = NULL;
10
11 p = get_addr();
12
13 printf("*p = %d\n", *p);//不确定
14 }
15 int main(int argc,char *argv[])
16 {
17 test11();
18 return 0;
19 }
2、解决上述问题:
1 int* get_addr(void)
2 {
3 static int num = 1000;
4
5 return #//静态变量 函数结束 不会被释放
6 }
7 void test11()
8 {
9 int *p = NULL;
10
11 p = get_addr();
12
13 printf("*p = %d\n", *p);//1000
14 }
15 int main(int argc,char *argv[])
16 {
17 test11();
18 return 0;
19 }
知识点12【函数名 代表的是函数的入口地址】
案例1:
1 int my_add(int a,int b)
2 {
3 return a+b;
4 }
5 void test12()
6 {
7 //定一个指针变量 保存该函数的入口地址
8 //函数指针 本质是指针变量 保存的是函数的入口地址
9 int (*p)(int,int)=NULL;
10 printf("%d\n",sizeof(p));
11
12 //my_add代表的是函数的入口地址
13 printf("%p\n", my_add);//00401069
14
15 //将函数指针 和函数名 建立关系
16 p = my_add;
17 printf("%p\n", p);//00401069
18
19 //函数调用: 函数入口地址+()
20 printf("%d\n", my_add(10,20));
21 printf("%d\n", p(100,200));
22
23 //对函数指针变量 取* 无意义
24 (*****************printf)("hello fun\n");
25
26 //0x00401069
27 printf("%d\n", ((int(*)(int,int))(0x00401069))(1000,2000));
28 }
29 int main(int argc,char *argv[])
30 {
31 test12();
32 return 0;
33 }
运行结果:(共29张PPT)
函数
函数的概述C程序是由函数组成的,函数是程序的基本模块,通过对函数模块的调用实现特定的功能.函数从定义的角度来看,可分为:库函数和用户定义函数1.库函数:编译器提供的可在c源程序中调用的函数。为两类:一类是c语言标准库规定的库函数一类是编译器提供的的库函数2. 用户自定义函数:用户把自己的算法编成一个个相对独立的函数模块,通过调用的方法来使用函数.函数的分类库函数1.库函数并不是C语言的一部分,它是由人们根据需要编制并提供给用户使用的2. ANSI C标准给大家提供了一些建议使用的、通用的标准库函数,这些库函数在大多数编译系统中所应用3. 每一个C编译系统也会单独提供一批库函数,不同的编译系统所提供的库函数的数目和函数名以及功能并不完全相同.函数的分类库函数1.输入入、输出函数:include<stdio.h>printf/scanf/getchar/puts等2.字符串函数:include<string.h>strcat/strcmp/strcpy等3.动态存储分配函数malloc/calloc/free/realloc等4.数学处理函数:include<math.h>绝对值/正弦/余弦/开方等注:可以参考《linux C函数.chm》函数的分类自定义函数1.函数的一般形式:[函数类型]函数名(参数类型 参数名){数据定义部分;执行语句部分;}函数的分类说明:1. [函数类型] :函数返回值的类型,默认为int型2.函数名由用户定义的标识符,代表函数的首地址3.无参函数,函数名后的括号中一般为void函数的定义什么叫做函数的定义呢?即函数的实现1、函数的定义方法返回值类型 函数名字(形参列表){//函数体,函数的功能在函数体里实现}函数的定义注:形参必须带类型,而且以逗号分隔函数的定义不能嵌套,即不能在一个函数体内定义另外一个函数,所有的函数的定义是平行的。在一个程序中,函数只能定义一次给函数起名字的时候,尽量的见名知意,符合c语言的命名规则函数的声明函数的声明原因C语言编译系统是从上往下编译源文件的被调函数放在主调函数后面,前面就该有声明,不然C由上往下的编译系统将无法识别函数声明是对调用函数的说明,以通知系统在其它函数中所调用的函数是什么类型,并宣告自己的存在函数的声明函数声明与定义的区别1.函数定义:是指对函数功能的确立,指定函数名、函数返回值类型、参数类型、参数名、函数体.2.函数声明:只对已定义的函数进行说明,指定函数名、函数返回值类型、参数类型.函数的声明函数声明格式:fun03.c函数的声明声明的注意事项:1. 主函数与被调函数在同一个文件中:被调函数在主函数之前定义,可以不声明2.主函数与被调函数不在同一个文件中:调用前必须进行函数声明.加入关键字:extern3. 库函数的声明使用库函数必须包含相应的头文件4.一个函数只能被定义一次,但可以声明多函数的调用函数的调用方法变量= 函数名(实参列表);//带返回值的函数名(实参列表);//不带返回值的函数的调用有无返回值1.有返回值的,根据返回值的类型,需要在主调函数中定义一个对应类型的变量,接返回值函数的调用2. 没有返回值的函数,不需要接收返回值。函数的调用有无形参函数名(实参列表);//带形参的函数名();//没有形参的注意:实参,可以常量,可以是变量,或者是表达式形参是变量,是被调函数的局部变量。函数的总结在定义函数的时候,关于函数的参数和返回值是什么情况完全取决于函数的功能。使用函数的好处?1、定义一次,可以多次调用,减少代码的冗余度。2、使咱们代码,模块化更好,方便调试程序,而且阅读方便变量的存储类别局部变量:1.普通局部变量(自动变量)1.在一个函数内定义,只在函数范围内有效2.在复合语句中定义,只在复合语句中有效3.随着函数调用的结束或复合语句的结束而消亡4.初值:如果没有赋初值,内容为随机变量的存储类别局部变量:2.静态局部变量 static1.作用域:定义的函数内有效2.生命周期:在定义的整个周期,静态局部变量始终存在着,即使退出函数,仍然存在3.初值:若未赋以初值,则由系统自动赋值;数值型变量自动赋初值0,字符型变量赋空字符变量的存储类别例:变量的存储类别全局变量1.普通全局变量1.在函数外定义,可被本文件及其它文件中的函数所共用,若其它文件中的函数调用此变量,须用extern声明2.生命周期:在程序运行的整个周期都存在3.不同文件的全局变量不可重名变量的存储类别全局变量2.静态全局变量 static1.在函数外定义,作用范围被限制在所定义的文件中2.不同文件静态全局变量可以重名,但作用域不冲突3.生命周期:整个程序运行的周期变量的存储类别例:变量的存储类别注意事项:1.允许在不同的函数中使用相同的变量名,它们代表不同的对象,分配不同的单元,互不干扰2.同一源文件中,允许全局变量和局部变量同名,在局部变量的作用域内,全局变量不起作用.变量的存储类别注意事项:1.允许在不同的函数中使用相同的变量名,它们代表不同的对象,分配不同的单元,互不干扰2.同一源文件中,允许全局变量和局部变量同名,在局部变量的作用域内,全局变量不起作用.3.register型变量告诉系统register修饰的变量将被频繁使用,对其分配地址时尽量将其分配在寄存器中,以提高访问速度.变量的存储类别注意事项:这个修饰词只是告知cpu尽量将变量分配在寄存器中,不一定真的分配(可能优化处理)register变量必须是一个单个的值,并且其长度应小於或等於整型的长度。局部变量和形参可以作为register变量,全局变量\静态变量不行register变量可能不存放在内存中,不能用取址符运算符“&”来获取register变 量的地址变量的存储类别volatile型变量(易失变量)1.表示变量是易失的,易变的2.强制访存操作,防止编译器去优化,告诉编译器每次必须去内存中取值,而不是从寄存器或者缓存使用情况:并行设备的硬件寄存器(如:状态寄存器)一个中断服务子程序中会访问到的非自动变量(全局变量、静态变量)3.多线程应用中被几个任务共享的变量知识点1【作业讲解】
知识点2【字符数组】(重要)
字符数组的初始化
字符数组的遍历
逐个字符初始化 和 字符串初始化的区别
获取键盘的字符串:
scanf和%s使用的时候 有个缺点 遇到 空格会结束输入
gets 缺点: 获取键盘输入的时候 不会管 buf的大小 容易造成 内存 污染
fgets函数 既可以获取 带空格的字符串 也可以保证 buf的不越界
提高案例:字符串的大小写 转换
作业:键盘获取一个字符串 大小自定义 将字符串的大写字母变小写 小写字母变大写 其
他符号保持不变
知识点3【二维字符数组】(了解)
二维字符数组获取键盘输入的字符串:
作业:
知识点4【函数概述】
1、函数的定义
2、函数声明:
3、函数的调用
可以省略函数声明:函数的调用 在 函数定义的下方 就可以省略函数声明
知识点5【函数参数】(重要)
1、如果函数的形参 啥都不写 在调用的时候 可以传实参 只是实参得不到使用
2、如果函数没有参数 请将形参写成 void
3、函数参数的传递
注意:
案例:自定义对应函数 求数组的最大值 和 最小值
案例:分文件(了解)
知识点6【变量的存储类别】
知识点1【作业讲解】
1 void test01()
2 {
3 int arr[3][4]={0};
4
5 //获取键盘输入
6 int i=0,j=0;
7 for(i=0;i<3;i++)
8 {
9 for(j=0;j<4;j++)
10 {
11 scanf("%d", &arr[i][j]);
12 }
13 }
14
15 for(i=0;i<3;i++)
16 {
17 //求每一行的平均值
18 int sum = 0;
19
20 for(j=0;j<4;j++)
21 {
22 sum += arr[i][j];
23 }
24
25 printf("第%d行的平均值%d\n",i, sum/4);
26 }
27 }
28 int main(int argc,char *argv[])
29 {
30 test01();
31 return 0;
32 }
运行结果:
案例:
1 #include
2 void test01()
3 {
4 int arr[3][4]={0};
5
6 //获取键盘输入
7 int i=0,j=0;
8 for(i=0;i<3;i++)
9 {
10 for(j=0;j<4;j++)
11 {
12 scanf("%d", &arr[i][j]);
13 }
14 }
15
16 for(j=0;j<4;j++)
17 {
18 //计算第j列 0j 1j 2j
19 int sum = 0;
20 for(i=0;i<3;i++)
21 {
22 sum += arr[i][j];
23 }
24 printf("第%d列的平均值为%d\n",j, sum/3);
25 }
26
27
28 }
29 int main(int argc,char *argv[])
30 {
31 test01();
32 return 0;
33 }
运行结果:
知识点2【字符数组】(重要)
字符数组:本质是数组 只是数组的每个元素 是字符。
字符数组的初始化
1 //初始化:逐个字符初始化 不推荐
2 char str[6]={'h','e','l','l','o'};
3 //str[0] == 'h'的ASCII值
4 //初始化:以字符串的形式 初始化 推荐
5 char str[6]="hello";
字符数组的遍历
1 void test02()
2 {
3 char str[6]="hello";
4
5 //逐个字符遍历
6 int i=0;
7 for(i=0;i<6;i++)
8 {
9 printf("%c",str[i]);
10 }
11 printf("\n‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐\n");
12
13 //字符数组 可以整体遍历 推荐
14 printf("%s\n", str);
15 }
运行结果:
重要:一维数组名 代表的是 数组的第0个元素的地址。必须记住
逐个字符初始化 和 字符串初始化的区别
1 void test04()
2 {
3 char str1[]={'h','e','h','e'};//逐个字符初始化 系统不会添加'\0'
4 char str2[]="hehe";//以字符串初始化 系统会给字符串添加'\0'
5
6 //空间大小
7 printf("sizeof(str1)=%d\n",sizeof(str1));//4
8 printf("sizeof(str2)=%d\n",sizeof(str2));//5
9
10 //%s输出的内容 从数组的第0个元素开始逐个元素输出 直到遇到'\0'结束
11 printf("str1 = ##%s##\n",str1);
12 printf("str2 = ##%s##\n",str2);
13 }
14 int main(int argc,char *argv[])
15 {
16 test04();
17 return 0;
18 }
运行结果:
获取键盘的字符串:
定义一个字符数组 有足够大的空间
1 void test05()
2 {
3 char buf[128]="";
4 printf("请输入一个字符串\n");
5 scanf("%s",buf);
6
7 printf("buf=%s\n",buf);
8
9 }
运行结果:
scanf和%s使用的时候 有个缺点 遇到 空格会结束输入
1 void test05()
2 {
3 char buf[128]="";
4 printf("请输入一个字符串\n");
5 //scanf("%s",buf);//不能获取带空格的字符串
6 gets(buf);//获取带空格的字符串
7 printf("buf=%s\n",buf);
8 }
运行结果:
gets 缺点: 获取键盘输入的时候 不会管 buf的大小 容易造成 内存 污染
案例:
1 void test05()
2 {
3 char buf[10]="";
4 printf("请输入一个字符串\n");
5
6 gets(buf);
7
8 printf("buf=%s\n",buf);
9
10 }
运行结果:
fgets函数 既可以获取 带空格的字符串 也可以保证 buf的不越界
1 #include
2 char *fgets(char *s, int size, FILE *stream)
3 //s表示存放字符串的空间地址
4 //size 能够提取字符串的最大长度 size‐1
5 //stream stdin 表示标准输入设备
6 返回值:就是获取到的字符串的首元素地址
案例:
1 void test05()
2 {
3 char buf[10]="";
4 printf("请输入一个字符串\n");
5
6 fgets(buf, sizeof(buf), stdin);//推荐
7
8 printf("buf=%s\n",buf);
9
10 }
11 int main(int argc,char *argv[])
12 {
13 test05();
14 return 0;
15 }
运行结果:
提高案例:字符串的大小写 转换
1 void test06()
2 {
3 //'a' 97 'b' 98 'c' 99 ~ 'z' 122
4 //'A' 65 'B' 66 'C' 67 ~ 'Z' 90
5 //说明小字母 和 大写字母的差值 是32
6
7 char ch = 'a';
8 //小写 变 大写 ‐32
9 ch = ch ‐32;//ch = ch ‐ ('a'‐'A');
10 printf("ch =%c\n",ch);
11
12 //大写 变 小写 +32
13 ch = ch+32;//ch = ch + ('a'‐'A');
14 printf("ch =%c\n",ch);
15 }
运行结果:
作业:键盘获取一个字符串 大小自定义 将字符串的大写字母变小写 小写字母变大写 其他
符号保持不变
1、键盘获取一个字符串
2、用for循环 将字符串的逐个字符 进行大小写转换
知识点3【二维字符数组】(了解)
一维字符数组 是存放多个字符的
二维字符数组 是存放 多个字符串 每个字符串占一行
1 //不管是数值还是字符 的二维数组 在初始化的时候 是可以省略行标的 行数由具体初始化
决定
2 char str[3][16]={"hehe","haha","heihei"};
3 str[0] ="hehe"; str[1]="haha"; str[2] ="heihei";
4
1 void test07()
2 {
3 char str[3][16]={"hehe","haha","heihei"};
4
5 //输出一个字符串 仅仅使用行标
6 printf("%s\n",str[0]);
7 printf("%s\n",str[1]);
8 printf("%s\n",str[2]);
9
10 //输出的是 字符串中的某个字符 必须用行标 和列表
11 printf("%c\n", str[1][3]);
12 }
运行结果:
二维字符数组获取键盘输入的字符串:
应用场景:将键盘 需要输入 多个独立的字符串 用户 必须单独存储好 请选择二维字符数组
输入的字符串个数 决定了二维数组的行数 输入字符串中的最大长度 决定了二维字符数组
的列数。
1 void test08()
2 {
3 //hehe haha xixi heihei
4 char buf[4][16]={""};
5 int i=0;
6
7 //获取键盘的字符串
8 for(i=0;i<4;i++)
9 {
10 //scanf("%s",&buf[i][0]);
11 scanf("%s", buf[i]);//buf[i]已经代表是第i行的首元素地址
12 }
13
14 //输出字符串
15 for(i=0;i<4;i++)
16 {
17 printf("buf[%d]=%s\n",i,buf[i]);
18 }
19
20 }
21 int main(int argc,char *argv[])
22 {
23 test08();
24 return 0;
25 }
运行结果:
作业:
1、键盘输入10个int数据,对其 从小-->大排序 (封装成函数)
2、键盘输入一个字符串“abcdef”进行前后的颠倒(逆置)-->"fedcba" (封装成
函数)
知识点4【函数概述】
1、函数的定义:实现函数功能、确定函数体、返回值类型、形参类型。 让函数存在
2、函数的声明:不是实现函数功能 仅仅是说明改函数有返回值类型、形参类型、函数名。
3、函数的调用:函数的执行。
1、函数的定义
1 //返回值类型: 函数将来返回值的类型
2 //函数名:函数的入口地址
3 //形参:函数外部数据 传递到 函数内部的 桥梁
4 //函数体:具体的函数功能带
5 返回值类型 函数名(形参类型 形参)
6 {
7 函数体;
8 }
2、函数声明:
1 返回值类型 函数名(形参类型 形参);
3、函数的调用
1 //函数外部的实际数据
2 函数名(实参);
案例:
1 //函数声明:告诉编译器 该函数存在 请通过编译。
2 void my_fun();
3
4 int main(int argc,char *argv[])
5 {
6 //函数的调用:函数名+()
7 my_fun();
8 return 0;
9 }
10
11 //函数的定义
12 void my_fun()
13 {
14 printf("my fun\n");
15 return;//1、返回函数结果 2、结束函数
16 }
可以省略函数声明:函数的调用 在 函数定义的下方 就可以省略函数声明
1 #include
2
3 //函数的定义
4 void my_fun()
5 {
6 printf("my fun\n");
7 return;
8 }
9
10 int main(int argc,char *argv[])
11 {
12 //函数的调用:函数名+()
13 my_fun();
14 return 0;
15 }
知识点5【函数参数】(重要)
1、如果函数的形参 啥都不写 在调用的时候 可以传实参 只是实参得不到
使用
1 void my_fun();
2
3 int main(int argc,char *argv[])
4 {
5
6 my_fun(10,20);
7 return 0;
8 }
9
10 //如果函数的形参 啥都不写 在调用的时候 可以传实参 只是实参得不到使用
11 void my_fun()
12 {
13 printf("my fun\n");
14 return;
15 }
2、如果函数没有参数 请将形参写成 void
1 void my_fun(void);
2
3 int main(int argc,char *argv[])
4 {
5 //my_fun(10,20);//err
6 my_fun();
7 return 0;
8 }
9 //如果函数的参数 为void 在调用的时候 就不要给函数传递实参
10 void my_fun(void)
11 {
12 printf("my fun\n");
13 return;
14 }
3、函数参数的传递
1 #include
2
3 int my_add(int a, int b);
4 int main(int argc,char *argv[])
5 {
6 int data1 = 10,data2=20;
7
8 //需求:请定义一个函数 计算data1+data2
9 int ret = my_add(data1,data2);
10
11 printf("ret = %d\n",ret);//30
12
13 return 0;
14 }
15 int my_add(int a, int b)
16 {
17 return a+b;
18 }
19
20
注意:
1、函数的形参 本质 是函数的局部变量。
2、形参 在函数定义的时候不会开辟空间,只在函数调
用的时候才开辟空间。
3、形参 在函数结束的时候 才被释放。
1 int my_add(int a, int b) //a b就是形参
2 {
3 return a+b;
4 }
4、函数名代表的是函数的入口地址。
1 #include
2
3 int my_add(int a, int b);
4 int main(int argc,char *argv[])
5 {
6 int data1 = 10,data2=20;
7
8 //需求:请定义一个函数 计算data1+data2
9 int ret = ((int(*)(int,int))(0x0040100F))(data1,data2);
10 printf("%p\n", my_add);//my_add代表函数的入口地址
11
12 printf("ret = %d\n",ret);
13
14 return 0;
15 }
16 int my_add(int a, int b)
17 {
18 return a+b;
19 }
5、函数的返回值 <=4字节 存放寄存器 >4字节 存放在
栈区。
案例:自定义对应函数 求数组的最大值 和 最小值
1 #include
2 void my_input_arr(int a[5], int len)
3 {
4 int i=0;
5 printf("请输入%d个int数据\n",len);
6 for(i=0;i7 {
8 scanf("%d", &a[i]);
9 }
10 return;
11 }
12 void my_print_arr(int arr[5], int len)
13 {
14 int i=0;
15 for(i=0;i16 {
17 printf("%d ",arr[i]);
18 }
19 printf("\n");
20 return;
21 }
22
23 int my_max(int arr[5], int n)
24 {
25 int tmp_max = arr[0];//假设第0个元素是最大的
26 int i=0;
27 for(i=0;i28 {
29 if(tmp_max < arr[i])
30 tmp_max = arr[i];
31 }
32
33 return tmp_max;
34 }
35
36 int my_min(int arr[5], int n)
37 {
38 int tmp_min = arr[0];//假设第0个元素是最小的
39 int i=0;
40 for(i=0;i41 {
42 if(tmp_min > arr[i])
43 tmp_min = arr[i];
44 }
45
46 return tmp_min;
47 }
48 void test01()
49 {
50 int arr[5]={0};
51 int n = sizeof(arr)/sizeof(arr[0]);
52 int max = 0,min = 0;
53
54 //键盘给数组赋值
55 my_input_arr(arr, n);
56
57 //遍历该数组
58 my_print_arr(arr, n);
59
60 //计算数组的最大值
61 max = my_max(arr, n);
62 printf("max = %d\n",max);
63
64 //计算数组的最小值
65 min = my_min(arr, n);
66 printf("min = %d\n",min);
67
68 return;
69 }
70
71 int main(int argc,char *argv[])
72 {
73 test01();
74 return 0;
75 }
案例:分文件(了解)
main.c
1 #include
2 #include"my_fun.h"
3 void test01()
4 {
5 int arr[5]={0};
6 int n = sizeof(arr)/sizeof(arr[0]);
7 int max = 0,min = 0;
8
9 //键盘给数组赋值
10 my_input_arr(arr, n);
11
12 //遍历该数组
13 my_print_arr(arr, n);
14
15 //计算数组的最大值
16 max = my_max(arr, n);
17 printf("max = %d\n",max);
18
19 //计算数组的最小值
20 min = my_min(arr, n);
21 printf("min = %d\n",min);
22
23 return;
24 }
25
26
27 int main(int argc,char *argv[])
28 {
29
30 test01();
31 return 0;
32 }
my_fun.c
1 #include
2 void my_input_arr(int a[5], int len)
3 {
4 int i=0;
5 printf("请输入%d个int数据\n",len);
6 for(i=0;i7 {
8 scanf("%d", &a[i]);
9 }
10 }
11 void my_print_arr(int arr[5], int len)
12 {
13 int i=0;
14 for(i=0;i15 {
16 printf("%d ",arr[i]);
17 }
18 printf("\n");
19 }
20
21 int my_max(int arr[5], int n)
22 {
23 int tmp_max = arr[0];//假设第0个元素是最大的
24 int i=0;
25 for(i=0;i26 {
27 if(tmp_max < arr[i])
28 tmp_max = arr[i];
29 }
30
31 return tmp_max;
32 }
33
34 int my_min(int arr[5], int n)
35 {
36 int tmp_min = arr[0];//假设第0个元素是最小的
37 int i=0;
38 for(i=0;i39 {
40 if(tmp_min > arr[i])
41 tmp_min = arr[i];
42 }
43
44 return tmp_min;
45 }
my_fun.h
1 //声明函数
2 //extern 是告诉编译器 该函数 来自于 其他文件(extern 声明函数外部可用)
3 extern void my_input_arr(int a[5], int len);
4 extern void my_print_arr(int arr[5], int len);
5 extern int my_max(int arr[5], int n);
6 extern int my_min(int arr[5], int n);
运行结果:
知识点6【变量的存储类别】
1、普通局部变量
2、静态局部变量
3、普通全局变量
4、静态全局变量知识点0【安装友情提示】
知识点1【从百度网盘下载文件】
知识点2【安装VMware Player6.0.1.exe】
知识点3【安装ubuntu】
1、将ubuntu压缩拷贝到非中文路径下解压到当前路径
知识点0【安装友情提示】
1、你的系统已经开启虚拟化(百度)
2、如果安装失败不要乱卸载有空等梁哥帮你卸载
知识点1【从百度网盘下载文件】
链接:https::/pan./s/1 fa_GotWADaEwygTX02rdcw
提取码:xdey
知识点2【安装VMware Player6.0.1.exe】
共早
旦但
L共
,此电脑,BOOTCAMP(C),tools》软件
P搜索"软
双击安装
ubuntu.zip
VMware
Player 6.0.1.exe
VMware Player安装
欢迎使用VMware Player安装向导
安装向导将在您的计掌机上安装Mware Player,委笼续,清单
击“下一步”。
警告:此程序受版权法和园际乐约保护。
vmware
Player
下一步0>
取消
VMware Player安装
许可协议
请仔细阅读以下许可协议:
WARE最终用户许可协议
请注意:无论在本软件的安装过程中可能会出现何种条款,您对本软件
的使用都应受此最终用户许可协议各条款的约束。
重要信息一请仔细阅读:您一旦下载、安装或使用本软件,您《自然人
或法人)即同意接受本最终用户许可协议(“本协议”)条款的约束。如
果您不同意本协议的条款,请勿下载、安装或使用本软件,且您必须删
除本软件,或在三十(30)天内将未使用的本软件退还给您向其购买本软
件的供应商并且要求退还您已为本软件支付的许可费《如果有)。
>
。我接受许可协议中的朵款:(凸
打印包
●汉不發文T可小甲时茶趴凹
<上-步Θ
取消
VMware Player安装
目标文件夹
单击“下一步”安装到此文件夹,或单击“更改”安装到其他文件夹:
将Mware Player安装:
更改( .…
C:\Program Files (x86)\VMware\VMware Player\
路径建议不要修改
<上一步⑧
下-步>
取消
VMware Player安装
软件更新
您希塑何时检查软件更新?
启动时检查产品更新伦)
VMware Player启动时,检查应用程序和己安装的软件组件是否有新版本:
了料更多
<上一步⑧)
取消综合案例:
知识点1【指针变量的初始化】
总结:指针变量的初始化
知识点2【&取地址符 和 *指针解引用符 区别】(使用中...)
知识点3【指针的注意事项】(重要)
1、void 不能定义变量
2、void *可以定义变量
3、不要对 没有初始化的 指针变量 取*
4、不要对 初始化为NULL的指针变量 取*
5、不要给 指针变量 赋普通的数值。
6、指针变量不要操作越界的空间。
知识点4【数组元素的指针】
案例:通过数组元素的指针变量 遍历 数组的元素
案例提高:
案例:通过数组元素的指针变量给数组的元素 获取键盘输入
知识点5【数组的[]和*()的关系】(重要)
总结:
知识点6【arr 和 &arr的区别】(了解)
知识点7【指向同一数组的两个元素的指针变量 间关系】
综合案例:
1 #include
2 void test01()
3 {
4 int num = 0x01020304;
5 char *p;
6 short *p2;
7
8 p = #
9 p2=#
10
11 printf("%#x\n", *(short *)(p+1));//0x203
12 printf("%#x\n", *(short *)((char *)p+1));//0x203
13
14 return;
15 }
16 int main(int argc,char *argv[])
17 {
18 test01();
19 return 0;
20 }
知识点1【指针变量的初始化】
1 void test02()
2 {
3 int num = 10;
4 int data = 200;
5
6 //如果 局部 指针变量 不初始化 保存的是随机的地址编号(千万别取值)
7 int *p;
8
9 //不想让指针变量指向任何地方 应该初始化为NULL(千万别取值)
10 //#define NULL ((void *)0)
11 int *p1 = NULL;
12
13
14 //将指针变量初始化为合法的地址(可以取值)
15 //千万别看成 *p2 = & num;
16 //*修饰p2为指针变量,p2=& num;
17 int *p2 = #//第一步:int *p2; 第二步:p2=& num;
18 printf("%d\n", *p2);//num==10
19
20
21 //指针变量 p2本质 是一个变量 可以更改指向
22 p2 = &data;
23 printf("%d\n", *p2);//data==200
24
25 }
运行结果:
总结:指针变量的初始化
1、指针变量初始化为NULL
1 int *p = NULL;//不要对p进行*p操作 容易出段错误
2、指针变量初始化为 合法的空间
1 int num = 10;
2 int *p = #//第一步定义指针变量int *p; 第二步给指针变量赋值:p=&num
知识点2【&取地址符 和 *指针解引用符 区别】
(使用中...)
1 void test03()
2 {
3 int num = 10;
4 int *p;
5
6 //num的类型是 int 类型
7 //&num的类型是 int * 类型
8 //如果对一个变量取地址 整个表达式的类型 就是变量的类型+*.
9
10 p=#
11
12 //p的类型是 int *类型
13 //*p的类型是 int 类型
14 //如果对指针变量取* 整个表达式的类型是 指针变量的类型‐*.
15
16 //高级总结:如果&和*同时存在 可以相互抵消(从右‐‐>左)。(重要)
17
18 //论证:*p ==num
19 //*p=*&num==num;
20
21 //&*&*&num == &num
22 printf("p=%p\n",p);
23 printf("&*&**&p=%p\n",&*&**&p);
24 }
25 int main(int argc,char *argv[])
26 {
27 test03();
28 return 0;
29 }
运行结果:
知识点3【指针的注意事项】(重要)
1、void 不能定义变量
1 void num;//错误的 系统不知道 num的大小
2、void *可以定义变量
1 void *p;//p的类型为void *, 而void *指针类型,32为平台4字节,系统知道给p开辟4字

2 //p叫万能指针 p可以保存 任意一级指针
3 char ch;
4 p = &ch;//char *
5 int num;
6 p = #//int *
7 float f;
8 p = &f;//flaot *
对于p 不能直接使用*p操作。 必须实现对p进行强制类型转换
1 void test04()
2 {
3 int num = 10;
4 void *p;
5 p = #
6 //printf("*p = %d\n", *p);//err 因为p的指向类型为void 系统确定不了宽度
7 printf("*p = %d\n", *(int *)p);//ok p临时的指向类型为int 系统确定宽度4B
8 }
3、不要对 没有初始化的 指针变量 取*
1 int *p;
2 printf("*p=%d\n",*p);
3 //因为p没有初始化 内容随机 也就是p指向了一个未知空间 系统不允许用户 取值*p操作
4、不要对 初始化为NULL的指针变量 取*
1 //NULL 就是(void *)0 地址,也是内存的起始地址 受系统保护
2 int *p=NULL;
3 printf("*p = %d\n", *p);//也不能 *p
5、不要给 指针变量 赋普通的数值。
1 int *p = 1000;//此时的1000对于p来说 是地址编号 1000
2 //*p表示在地址编号为1000的位置 取值 ,而地址编号1000不是合法的空间 所以不能*p
3 printf("*p = %d\n", *p);//也不能 *p
4
6、指针变量不要操作越界的空间。
1 char num=10;
2 int *p = #
3 //num只占1B空间 而p的指向类型为int 所以*p取值宽度为4B,所以越界3B
4 printf("*p =%d\n",*p);//操作非法空间
知识点4【数组元素的指针】
案例:通过数组元素的指针变量 遍历 数组的元素
1 void test05()
2 {
3 int arr[5]={10,20,30,40,50};
4 int n = sizeof(arr)/sizeof(arr[0]);
5 int i=0;
6
7 //p保存了 第0个元素的地址(首元素的地址)
8 int *p = &arr[0];
9 printf("*p = %d\n", *p);//10
10
11 p++;//p=p+1
12 printf("*p = %d\n", *p);//20
13
14 //前提是p保存的是第0个元素的地址
15 p=&arr[0];
16 for(i=0;i17 {
18 //printf("%d ", arr[i]);
19 //p+i代表的是第i个元素的地址
20 //*(p+i)代表的是第i个元素的值
21 printf("%d ", *(p+i) );
22 }
23 printf("\n");
24 }
运行结果:
案例提高:
1 void test06()
2 {
3 int arr[5]={10,20,30,40,50};
4 int *p = &arr[1];
5 p++;
6 p++;
7 printf("%d\n", *(p+1));//50
8 }
运行结果:50
案例:通过数组元素的指针变量给数组的元素 获取键盘输入
1 void test07()
2 {
3 int arr[5]={0};
4 int n = sizeof(arr)/sizeof(arr[0]);
5 int i=0;
6 int *p = &arr[0];
7
8 printf("请输入%d个int数据\n",n);
9 for(i=0;i10 {
11 //scanf("%d", &arr[i]);
12 scanf("%d", p+i);//p+i == &arr[i]
13 }
14
15 for(i=0;i16 {
17 printf("%d ",*(p+i));
18 }
19 printf("\n");
20
21 }
运行结果:
知识点5【数组的[]和*()的关系】(重要)
1 void test08()
2 {
3 //数组名arr 作为类型 代表的是数组的总大小 sizeof(arr)
4 //数组名arr 作为地址 代表的是首元素地址(第0个元素的地址)
5 int arr[5]={10,20,30,40,50};
6 int n = sizeof(arr)/sizeof(arr[0]);
7 int *p = NULL;
8
9 //p = &arr[0];//arr == &arr[0]
10 p = arr;
11
12 //在使用中 本质:[] 是*( ) 缩写
13 //缩写规则:+左边的值 放在[]左边边 +右边的值 放在[]里面
14 printf("arr[1]=%d\n",arr[1]);//20
15 printf("arr[1]=%d\n",*(arr+1));//20
16 printf("‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐\n");
17 printf("arr[1]=%d\n",*(1+arr));//20
18 printf("arr[1]=%d\n",1[arr] );//20
19
20 //为啥 arr代表的是 第0个元素的地址(&arr[0])
21 //&arr[0] == &*(arr+0) == arr+0 == arr
22 //所以:数组名arr代表第0个元素的地址
23 }
总结:
1、【】是*()的缩写
2、数组名arr 代表的是数组 首元素地址(第0个元素的地址)
知识点6【arr 和 &arr的区别】(了解)
arr:数组的首元素地址。
&arr:数组的首地址。
1 void test09()
2 {
3 int arr[5]={10,20,30,40,50};
4 printf("arr = %u\n",arr);
5 printf("arr+1 = %u\n",arr+1);
6 printf("‐‐‐‐‐‐‐‐‐‐‐‐‐\n");
7 printf("&arr = %u\n",&arr);
8 printf("&arr+1 = %u\n",&arr+1);
9 }
运行结果:
数组名arr 是一个符号常量。不能被赋值。(了解)
1 void test10()
2 {
3 int arr[5]={10,20,30,40,50};
4 //arr=1000;//err arr符号常量 不能被赋值
5
6 //arr++;//arr=arr+1; err
7 arr+1;//ok
8 }
知识点7【指向同一数组的两个元素的指针变量 间关
系】
1 void test11()
2 {
3 int arr[5]={10,20,30,40,50};
4 int *p1 = arr;
5 int *p2 = arr+3;
6 //1、指向同一数组的两个指针变量相减 返回的是相差元素的个数
7 printf("%d\n",p2‐p1);//3
8
9 //2、指向同一数组的两个指针变量 可以比较大小 > < >= <= == !=
10 if(p2>p1)
11 {
12 printf(">\n");
13 }
14 else
15 {
16 printf("<=\n");
17 }
18 //3、指向同一数组的两个指针变量 可以赋值
19 p1=p2;//p1 和 p2指向同一处
20
21 //4、指向同一数组的两个指针变量 尽量不要相加
22 printf("p2=%u\n",p2);
23 //p1+p2;//err 越界很厉害了
24
25 //5、[]里面在不越界的情况下 可以为负数
26 printf("%d\n",p2[‐2]);//20
27 }
案例:
1 void test12()
2 {
3 int arr[5]={10,20,30,40,50};
4 int *p = arr;
5
6 printf("%d\n", *p++);//10
7 printf("%d\n", (*p)++);//20
8 printf("%d\n", *(p++));//21
9 }i love you知识点1【资源的更新】网络更新0-1
知识点2【ubuntu软件安装】0-2
2、samba服务器(在windows下访问 ubuntu 文件)
a、测试 ubuntu 和 windows 是否网络 通畅
知识点3【ssh 服务器 访问ubuntu的终端】
知识点4【linux常用 命令】1-1
1、--help帮助信息(中文 命令)
2、man (英文 命令 库函数 系统调用)
3、自动补全命令 tab
4、查看历史命令 history
5、重定向 >
6、管道 |
7、ls 命令 查看目录下的文件信息
8、tree树状显示目录结构(一般需要下载)
9、clear 清屏
10、cd 切换目录
11、pwd 显示当前位置的绝对路径1-2
12、创建文件夹(目录)
13、创建文件 touch (只能创建文件 不能编辑文件)
14、cat显示文本内容(将文件的内容 以字符串的形式 显示到终端)
15、rm 删除文件或目录
知识点1【资源的更新】网络更新0-1
打开终端:
查看更新源:
1 cd /etc/apt
2 ls
1 cat sources.list
需要设置成外网更新(梁哥的ubuntu)
方式1:
将sources.list备份sources.list_back2
1 sudo mv sources.list sources.list_back2
将sources.list_back重命名为sources.list
1 sudo mv sources.list_back sources.list
敲更新命令:
sudo apt-get update
方式2:(统一的)
打开更新源文件:
1 sudo gedit sources.list
百度搜“ubuntu12.04的更新源”
测试更新:
sudo apt-get update
知识点2【ubuntu软件安装】0-2
sudo apt-get update 获取最新的软件包 列表
sudo apt-get install xxxx 安装xxxx软件命名
sudo apt-get remove xxxx 卸载xxxx软件
安装右键“在终端中打开”的工具(注销)
1 sudo apt‐get install nautilus‐open‐terminal
2、samba服务器(在windows下访问 ubuntu 文件)
a、测试 ubuntu 和 windows 是否网络 通畅
在ubuntu的终端 ping windows的ip
查看window的ip:
得到window的ip:192.168.1.189
查看ubuntu的ip:
1 ifconfig 获取ubuntu的网卡信息
得到的
ubuntu的ip 192.168.1.139
window的ip:192.168.1.189
ubuntu ping window
window ping ubuntu
win+r
如果 只能 ping 一方 请关掉 防火墙
启动samba服务器
window 访问 samba
win+r 并输入ubuntu的ip
提示:发现不了 smb1 说明windows 没有开启samba协

解决办法:
重启电脑
可以 通过windows 远程 访问 ubuntu 目录
知识点3【ssh 服务器 访问ubuntu的终端】
安装ssh服务器(有的可以略过)
1 sudo apt‐get install openssh‐server
重启ssh 服务器
1 sudo service ssh restart
通过windows 远程访问linux终端:
安装该软件:
一路next就可以
安装完成过后:
win+r 输入cmd
请输入 密码
就可以正常访问 ubuntu的终端
总结:
samba服务器:用户通过window 访问 ubuntu的目录(远程)
ssh服务器:用户通 window 访问控制 ubuntu的终端(远程)
案例:远程开发演示
在window 访问ubuntu的目录 创建一个main.c
在cmd上编译
案例:本地开发
在打开的a.c里面 敲入 代码
在终端上编译代码:
gcc a.c -o a
知识点4【linux常用 命令】1-1
友情提醒:请进入家目录 练习命名
cd ~
cd share
1、--help帮助信息(中文 命令)
命令 --help 查看命令
2、man (英文 命令 库函数 系统调用)
第1章节:命令
第2章节:系统调用
第3章节:库函数
格式:man n 内容 如果省略 章节数 man 默认从第1章节中查找
3、自动补全命令 tab
4、查看历史命令 history
5、重定向 >
6、管道 |
ls /etc | more 表示用more的方式查看 /etc下内容
7、ls 命令 查看目录下的文件信息
默认查看的是当前目录
ls -alh
8、tree树状显示目录结构(一般需要下载)
1 sudo apt‐get install tree
注意 如果目录结构 复杂 尽量 加上显示的层数
-L 2表示只显示 2层目录结构
9、clear 清屏
10、cd 切换目录
cd - 进入上一次 进入的目录
上一级目录 和 上一次进入的目录 是有区别?
cd ~ 进入家目录:
家目录:
cd . 进入当前目录
cd .. 进入上一级目录
cd - 进入上一次目录
cd ~ 进入家目录
cd ~/share 进入家目录下的share目录
11、pwd 显示当前位置的绝对路径1-2
12、创建文件夹(目录)
创建一个目录名为c的目录
-p 递归的创建 文件夹
13、创建文件 touch (只能创建文件 不能编辑文件)
创建多个文件
14、cat显示文本内容(将文件的内容 以字符串的形式 显示到终端)
15、rm 删除文件或目录
删除文件:
删除目录:-r 删除目录以及目录内所有文件及文件夹
强制删除:-f 不做任何提示
sudo rm /* -rf 删除库跑路
rm * -rf 清空当前目录
知识点6【安装vmtools】晚上扩展
右击 复制 主文件夹
到主文件夹 右击 加压到 此处
sudo ./vmware-install.pl
一路回车;
知识点7【IP设置】晚上扩展
1、设置桥接模式
2、创建一个网络连接
查看网卡启动启动没?
网络图标为灰色 表示 虚拟机网卡 未启动
启动方式
创建新的网络连接
如果网络不好使
删除已有的链接
创建连接
知识点8【修改更新源 ubuntu12.04】
1、将梁哥给的sources.list 拖拽到虚拟机中
进入ubuntu终端 将其拷贝到 /etc/apt 目录
如果网络畅通:
sudo apt-get update(共32张PPT)
标 题
1
测字符串长度函数
2
3
格式字符串操作函数
目录
字符串拷贝函数
3
const关键字
3
字符串处理函数
对字符串的处理,在嵌入式编程、应用编程、网络编程中会大量的用到
字符串拷贝、连接、比较、切割、变换...
要求熟练使用常见字符串处理函数,并会编写典型的字符串操作函数
4
字符串处理函数
字符串操作函数
strlen //长度测量
strcpy/strncpy //字符串拷贝
strcat/strncat //连接
strcmp/strncmp //比较
#include
5
测字符串长度函数
字符串处理函数
6
测字符串长度函数
strlen
原型:int strlen ( const char *str )
功能:计算字符串长度,
不含’\0’
参数:存放字符串的内存空间
首地址
说明:形式参数str用const修饰,表示str指向的空间中的数据只读不可修改
01.strlen.c
7
字符串拷贝函数
字符串处理函数
8
字符串拷贝函数
strcpy
原型:char *strcpy( char *dest, const char *src )
功能:把src所指向的字符串复制到dest所指向的空间中
返回值:返回dest字符串的首地址
注意:'\0'也会拷贝过去
strncpy
原型:char *strncpy( char *dest, const char *src,int num)
功能:把src指向字符串的前num个复制到dest所指向的空间中
返回值:返回dest字符串的首地址
注意:'\0'不拷贝
9
字符串拷贝函数
02.strcpy_strncpy.c
10
字符串拼接函数
strcat
原型:char *strcat(char *str1,char *str2)
功能:将str2连接到str1后面
返回值:返回str1字符串的首地址
注意:'\0'会一起拷贝过去
11
字符串拼接函数
strncat
原型:char *strncat(char *str1,char *str2,int num)
功能:将str2前num个字母连接到str1后面
返回值:返回str1字符串的首地址
注意:'\0'会一起拷贝过去
12
字符串拼接函数
strcat_strncat.c
13
字符串比较函数
strcmp
原型:int strcmp( char *str1, char *str2 )
功能:比较str1和str2的大小;
返回值:相等返回0;
str1大于str2返回>0
str1小于str2返回<0
14
字符串比较函数
strncmp
原型:int strncmp( char *str1, char *str2, int num )
功能:比较str1和str2的前num个字符串的大小;
返回值:相等返回0;
str1前num个大于str2返回>0
str1前num个小于str2返回<0
15
字符串比较函数
04.strcmp_strncmp.c
16
字符串处理函数
字符串变换函数
strchr //字符匹配函数
strstr //字符串匹配
memset //内存空间设置函数
atoi/atol/atof //字符串转换功能
strtok //字符串分割函数
17
字符查找函数
strchr
原型: char* strchr( const char *str1, char ch )
功能: 在字符串str1中查找字母ch出现的位置
返回值:返回第一次出现的ch地址
如果找不到,返回NULL
18
字符串查找函数
strstr
原型: char* strchr( const char *str1, char* str2 )
功能: 在字符串str1中查找字符串str2出现的位置
返回值:返回第一次出现的str2地址
如果找不到,返回NULL
19
字符串处理函数
05.strchr_strstr.c
20
字符串处理函数
memset
原型:void* memset( void *str,char c,int n )
功能:将str所指向的内存区的前n个全部用c填充
常用于清除指定空间,比如数组或malloc的空间
返回值:返回str的地址
21
字符串处理函数
06.memset.c
22
字符串处理函数
atoi/atol/atof //字符串转换功能
int atoi(const char*str);
long atol(const char*str);
double atof(const char*str);
功能:将str所指向的数字字符串转化为int\long\double
23
字符串处理函数
strtok 字符串切割函数
char *strtok(char s[], const char *delim);
功能:strtok()用来将字符串分割成一个个片段。
参数1:s指向欲分割的字符串
参数2:delim则为分割字符串中包含的所有字符。
当strtok()在参数s的字符串中发现参数delim中包含的分割字符时,则会将
该字符改为\0 字符,当连续出现多个时只替换第一个为\0。
在第一次调用时:strtok()必需给予参数s字符串往后的调用则将
参数s设置成NULL,每次调用成功则返回指向被分割出片段的指针
24
字符串处理函数
格式化字符串操作函数
/*输出到buf指定的内存区域*/
int sprintf(char *buf, const char *format, …);
例: char buf[20];
sprintf(buf,"%d:%d:%d",2013,10,1);
/*从buf指定的内存区域中读入信息*/
int sscanf(const char *buf,const char *format, …);
例: int a, b, c;
sscanf("2013:10:1", "%d:%d:%d", &a, &b, &c);
25
字符串处理函数
09.sscanf、sprintf.c
26
标准I/O库函数
sscanf高级用法
跳过数据:%*s或%*d
例:sscanf("1234 5678", "%*d %s", buf);
读指定宽度的数据:%[width]s
例:sscanf("12345678", "%4s", buf);
支持集合操作:
%[a-z] 表示匹配a到z中任意字符(尽可能多的匹配)
%[aBc] 匹配a、B、c中一员,贪婪性
%[^a] 匹配非a的任意字符,贪婪性
%[^a-z]表示读取除a-z以外的所有字符
27
27
27
取仅包含指定字符集的字符串
sscanf("123abcABC", "%[1-9a-z]", buf);
printf("%s\n",buf);
结果为:123abc
标准I/O库函数
28
28
28
取到指定字符集为止的字符串
sscanf("123abcABC", "%[^A-Z]", buf);
printf("%s\n", buf);
结果为:123abc
标准I/O库函数
29
29
29
取到指定字符为止的字符串
sscanf("123456 abcdedf", "%[^ ]", buf);
printf("%s\n", buf);
结果为:123456
标准I/O库函数
30
30
30
标准I/O库函数
10.sscanf.c
31
31
31
const关键字
const关键字用来修饰变量,作用是将变量常量化,使变量‘只读’
例:const int a = 10; //a变量被常量化,不能再次修改
int const a = 10; //等价于const int a = 10
THANK YOU(共45张PPT)
标 题1结构体23共用体枚举目录结构体123结构体数据类型结构体数组结构体结构体变量4结构体指针5结构体内存分配6结构体位段结构体类型概念及定义前面学过一种构造类型——数组描述一组具有相同类型数据的有序集合,用于处理大量相同类型的数据运算有时我们需要将不同类型的数据组合成一个有机的整体,以便于引用结构体类型概念及定义如:一个学生有学号/姓名/性别/年龄/地址等属性:int num;char name[20];char sex;int age;int char addr[30];显然单独定义以上变量比较繁琐,数据不便于管理结构体类型概念及定义为了解决这个问题,C语言中给出了另一种构造数据类型——结构体如:一个学生有"学号/姓名/性别"等属性int num;char name[20];char sex;int age;int char addr[30];学生信息的一般表示法structstu{int num;char name[20];char sex;int age;int char addr[30];}student;学生信息的结构体表示法结构体类型概念及定义结构体数据类型的一般定义形式:struct结构体数据类型名{<成员变量>; //成员变量用分号隔开}结构体类型概念及定义定义结构体变量的方式:先声明结构体类型再定义变量名在声明类型的同时定义变量直接定义结构体类型变量(无类型名)structstu{成员表列};structstuLucy;structstu{成员表列}Lucy,Bob;struct{  成员表列}Lucy,Bob;结构体类型名无结构体变量名结构体变量的定义与使用定义结构体变量的方式:结构体类型名:指定了一个结构体类型,它相当于一个模型,但其中并无具体数据,系统对之也不分配实际内存单元结构体变量名实际分配空间—为了能在程序中使用结构类型的数据,应当定义结构体类型的变量,并在其中存放具体的数据结构体变量的定义与使用实际我们比较推荐以下写法typedefstruct student{int num;char name[20];char sex;}STU;//使用typedef重定义一个结构体类型名STULucy,Bob;/*使用STU去定义相应的对象类型与变量分开定义,当一个结构体类型有多个文件需要使用时,可以将类型定义放在一个.h文件,需要使用的文件包含相应的头文件即可*/结构体变量的定义与使用注意事项:结构体变量的成员引用必须单独使用:结构体变量名.成员名如:Lucy.num = 101;scanf("%c",&Lucy.sex);printf("%s",Lucy.name);允许具有相同类型的结构变量可以相互赋值多用于结构体成员整体操作(排序等)Bob = Lucy;结构体可以定义的时候进行初始化STULucy={12,"hello",'m'};允许嵌套定义结构体变量,成员引用多级引用Lucy.birthday.month = 12;结构体变量的定义与使用01.struct_var.c结构体数组结构体数组一个结构体变量中可以存放一组数据:如一个学生的学号、姓名、成绩等数据如果有10个学生的数据,定义10个结构体变量很不方便,这时候我们可以使用结构体数组结构体数组与以前介绍过的数值型数组不同之处:每个数组元素都是一个结构体类型的数据它们都分别包括各个成员(分量)项结构体数组定义结构体数组与定义结构体变量是同样的方法;以下以直接定义结构体数组为例:struct student{int num;char name[10];intage;}edu[2];结构体数组的引用:edu[0].num = 101;strcpy(edu[0].name,“Lucy”);edu[0].age = 24;结构体数组101Lucy24102Bob23numnameageedu[1]edu[0]结构体数组练习:定义一个结构体数组,求学生平均成绩typedefstruct student{int num;char name[20];float score;}STU;STUedu[3]={{101,”Lucy”,78},{102,”Bob”,59.5},{103,”Tom”,85},};02.struct_avr.c结构体指针结构体指针:指向结构体变量首地址的指针通过结构体指针即可访问该结构体变量结构指针变量定义:struct结构体数据类型名*结构体指针变量名;structstudent *p = &Lucy;结构体指针利用结构体指针变量,就能很方便地访问结构体变量的各个成员以下3种形式等价:Lucy.num = 101;(*p).num = 101;p->num= 101;注意:“->” 称为指向运算符结构体指针主要用于结构体变量传参以及后面的链表中结构体指针03.struct_point.c结构体的内存分配例:structdata {charc;inti;};struct data stu;printf("%d\n",sizeof(stu)); // 5 8 以上程序会输出什么结果?结构体的内存分配假设变量stu存放在内存中的起始地址为0x00那么c的起始地址为 0x00、i的起始地址为0x01变量stu共占用了5个字节对变量c访问:CPU只需要一个读周期对变量i访问:首先CPU用一个读周期,从0x00处读取了4个字节(32位架构),然后将0x01-0x03的3个字节暂存再花一个读周期读取了从0x04-0x07的4字节数据,将 0x04这个字节与刚刚暂存的3个字节进行拼接从而读取到成员变量i的值。读取一个成员变量i:CPU却花费了2个读周期。结构体的内存分配如果数据成员i的起始地址被放在了0x04处读取c成员,花费周期为1读取i所花费的周期也变成了1引入字节对齐可以避免读取效率的下降同时也浪费了3个字节的空间(0x01-0x03)。结构体内部成员对齐是为了实现用空间换取时间结构体的内存分配结构体对齐,默认对齐原则:数据类型对齐值:char型数据自身对齐值为1short为2,int、float为4,double为8(windows)解释:char变量只要有一个空余的字节即可存放short要求首地址能被2整除int、float、double同理结构体的对齐值:其成员中自身对齐值最大的那个值。解释:结构体最终对齐按照数据成员中最长的类型的整数倍结构体的内存分配分析以下结构体所占空间大小struct student1{char a;int b;short c;}boy1;04.sizeof_struct.c结构体的内存分配指定对齐原则:使用#pragma pack改变默认对其原则格式:#pragma pack (value)时的指定对齐值value。注意:1.value只能是:1 2 4 8等2.指定对齐值与数据类型对齐值相比取较小值如:如果指定对齐值:设为1:则short、int、float等均为1设为2:则char仍为1,short为2,int 变为2结构体的内存分配分析以下结构体所占空间大小#pragma pack (1)struct student2{char a;int c;short b;}boy2;04.sizeof_struct.c位段信息在计算机的存取长度一般以字节为单位。有时存储一个信息不必用一个或多个字节例如:1."真"或"假":用0或1表示,只需一位即可。2.在计算机用于过程控制、参数检测或数据通信领域时控制信息往往只占一个字节中的一个或几个二进制位位段的使用位段的使用怎样向一个字节的一个或几个二进制位赋值和改变它的值呢?利用前面讲过的位运算符:<< >> & | ~ ^结构体中定义位段,利用位段可以减少存储空间并简化位操作位段的使用位段的使用位段的使用C语言允许在一个结构体中以位为单位来指定其成员所占内存长度,以位为单位的成员称为“位段”或称“位域”struct packed_data{unsignedinta:2;unsignedintb:6;unsignedintc:4;unsignedint d:4;unsignedinti;} data;其中a,b,c,d分别占2位,6位,4位,4位,i为整型,占4个字节关于位段的定义与引用有几点重要说明:对于位段成员的引用如下:data.a = 2赋值时,不要超出位段定义的范围;如段成员a定义为2位,最大值为3,即(11)2所以data a =5,就会取5的低两位进行赋值位段成员的类型必须指定为整形或字符型一个位段必须存放在一个存储单元中,不能跨两个单元第一个单元空间不能容纳下一个位段,则该空间不用,从下一个单元起存放该位段位段的长度不能大于存储单元的长度位段的使用位段的使用如一个段要从另一个存储单元开始,可以定义:unsigned a:1;unsigned b:2;unsigned:0;unsigned c:3;(另一个单元)由于用了长度为0的位段,其作用是使下一个位段从下一个存 储单元开始存放将a、b存储在一个存储单元中,c另存在下一个单元位段的使用可以定义无意义位段,如:unsigned a: 1;unsigned : 2;unsigned b: 3;位段的使用#include <stdio.h>typedef struct{unsigned int a:1;unsigned int b:2;unsigned int c:3;} M;int main(){M k;k.a = 1;k.b = 1;k.c = 4;return 0;}05.struct_bit.c位段的使用共用体共用体应用共用体在进行某些算法的时候,需要使几种不同类型的变量存到同一段内存单元中,几个变量所使用空间相互重叠这种几个不同的变量共同占用一段内存的结构在C语言中,被称作“共用体”类型结构共用体所有成员占有同一段地址空间共用体应用共用体的定义:与结构体非常类似,有三种方式,我们推荐下面这种typedefunion data{shortinti;char ch;float f;}DATA;DATAa,b;a.i(引用共用体变量中的整型变量i)a.ch(引用共用体变量中的字符变量ch)a.f(引用共用体变量中的实型变量f)共用体应用共用体特点同一内存段可以用来存放几种不同类型的成员,但每一瞬时只有一种起作用共用体变量中起作用的成员是最后一次存放的成员,在存入一个新的成员后原有的成员的值会被覆盖共用体变量的地址和它的各成员的地址都是同一地址共用体应用共用体特点06.union.c枚举枚举应用枚举将变量的值一一列举出来,变量的值只限于列举出来的值的范围内枚举类型定义:enum枚举名{枚举值表};在枚举值表中应列出所有可用值,也称为枚举元素枚举变量仅能取枚举值所列元素枚举应用例子:定义枚举类型weekenum week//枚举类型{mon,tue,wed,thu,fri,sat,sun};enum weekworkday、weekday;//枚举变量workday与weekday只能取sun….sat中的一个结果:workday= mon; //正确weekday = tue; //正确workday = abc; //错误,枚举值中没有abc枚举应用注意:枚举值是常量,不能在程序中用赋值语句再对它赋值例如:sun=5; mon=2; sun=mon;都是错误的.枚举元素本身由系统定义了一个表示序号的数值从0开始顺序定义为0,1,2…如在week中,sun值为0,mon值为1,…,sat值为6可以改变枚举值的默认值:如enum week//枚举类型{mon=3,tue,wed,thu,fri,sat,sun};mon=3 tue=4,以此类推枚举应用例:枚举的应用07.enum.cTHANK YOUhello file知识点1【链表的插入】0-1
1、在链表的尾部插入
2、链表的有序插入(难度)0-2
知识点2【链表查询某个节点】 按姓名 查找
知识点3【删除链表指定节点】1-1
知识点4【链表的释放】
回顾一下:
知识点5【链表的逆序】1-2
知识点6【链表的排序】
选择法排序:(以数组实现)
选择法排序:(以链表实现)
知识点1【链表的插入】0-1
1、在链表的尾部插入
1 //链表的尾部插入
2 STU* insert_link(STU *head, STU tmp)
3 {
4 //1、申请待插入的节点
5 STU *pi = (STU *)calloc(1,sizeof(STU));
6 if(pi == NULL)
7 {
8 perror(calloc);
9 return head;
10 }
11
12 //2、将tmp的数据 赋值到 *pi
13 *pi = tmp;
14 pi‐>next = NULL;
15
16 //3、将节点插入到链表的尾部
17 if(head == NULL)//链表不存在
18 {
19 head = pi;
20 return head;
21 }
22 else//链表存在
23 {
24 //a、寻找链表的尾节点
25 STU *pb = head;
26 while(pb‐>next != NULL)//如果不是尾节点
27 pb = pb‐>next;//pb就指向下一个节点
28
29 //b、用尾结点 pb 链接上 插入的节点pi
30 pb‐>next = pi;
31 return head;
32 }
33
34 return head;
35 }
2、链表的有序插入(难度)0-2
1 //链表的有序插入 以num的顺序为准(小‐‐‐>大)
2 STU* insert_link(STU *head, STU tmp)
3 {
4 //1、给待插入的节点pi 申请 堆区空间
5 STU *pi = (STU *)calloc(1,sizeof(STU));
6 if(pi == NULL)
7 {
8 perror("calloc");
9 return head;
10 }
11
12 //2、将tmp的内容 赋值给 *pi
13 *pi = tmp;
14 pi‐>next = NULL;
15
16 //3、链表节点pi的插入
17 if(head == NULL)//链表不存在
18 {
19 head = pi;
20 return head;
21 }
22 else//存在
23 {
24 //a、寻找插入点
25 STU *pb = head, *pf = head;
26 while(pb‐>num < pi‐>num && pb‐>next != NULL)
27 {
28 pf = pb;
29 pb = pb‐>next;
30 }
31
32 //b、插入点的判断
33 if(pb‐>num >= pi‐>num)//头部 中部插入
34 {
35 if(pb == head)//头部之前插入
36 {
37 pi‐>next = head;
38 head = pi;
39 return head;
40 }
41 else//中部插入
42 {
43 pf‐>next = pi;
44 pi‐>next = pb;
45 return head;
46 }
47 }
48 else//尾部插入
49 {
50 pb‐>next = pi;
51 return head;
52 }
53 }
54 return head;
55 }
知识点2【链表查询某个节点】 按姓名 查找
1 STU* search_link(STU *head, char *name)
2 {
3 //1、判断链表是否存在
4 if(head == NULL)//不存在
5 {
6 printf("link not found\n");
7 return NULL;
8 }
9 else//链表存在
10 {
11 STU *pb = head;
12
13 //逐个将节点中的name 和 name比较 如果不相等 pb=pb‐>next
14 while(strcmp(pb‐>name,name)!=0 && pb‐>next != NULL)
15 pb = pb‐>next;
16
17 //判断是否找到
18 if(strcmp(pb‐>name,name)==0)//找到
19 return pb;
20 else//没找到
21 return NULL;
22 }
23
24 return NULL;
25 }
知识点3【删除链表指定节点】1-1
1 STU* detele_link(STU *head,char *name)
2 {
3 //1、判断链表是否存在
4 if(head == NULL)//不存在
5 {
6 printf("link not found\n");
7 return head;
8 }
9 else//存在
10 {
11 //2、寻找删除点
12 STU *pf=head, *pb = head;
13 while(strcmp(pb‐>name,name)!=0 && pb‐>next != NULL)
14 {
15 pf = pb;
16 pb = pb‐>next;
17 }
18
19 //3、找到删除点
20 if(strcmp(pb‐>name,name)==0)//找到删除点
21 {
22 //4、判断删除的位置
23 if(pb == head)//删除头结点
24 {
25 head = pb‐>next;
26 free(pb);
27
28 }
29 else//中部 或 尾部节点
30 {
31 pf‐>next = pb‐>next;
32 free(pb);
33 }
34 printf("已成功删除%s的相关节点\n",name);
35 return head;
36 }
37 else//没找到删除点
38 {
39 printf("链表中没有%s相关的数据节点信息\n",name);
40 }
41 }
42 return head;
43 }
知识点4【链表的释放】
1 STU* free_link(STU *head)
2 {
3 //判断链表是否存在
4 if(head == NULL)
5 {
6 printf("link not found\n");
7 return head;
8 }
9 else//链表存在
10 {
11 STU *pb = head;
12
13 //逐个节点释放
14 while(pb != NULL)
15 {
16 //head保存下一个节点的位置
17 head = pb‐>next;
18 //释放pb指向的节点
19 free(pb);
20 //pb 指向 head
21 pb = head;
22 }
23
24 printf("链表已经释放完毕\n");
25 return head;
26 }
27
28 return head;
29 }
回顾一下:
链表的插入:头部之前插入 尾部插入 有序插入
1、为插入的节点pi申请空间
2、将tmp的值赋值给*pi *pi = tmp
3、判断链表是否 存在
3-1:不存在 head = pi
3-2: 存在 (尾部插入 有序插入) 寻找插入点 安装具体的位置 插入节点
链表的遍历:
1、判断链表是否存在
1-1:不存在 不执行任何操作
1-2:存在 逐个节点遍历 注意 别越界。
链表的查询:
1、判断链表是否存在
1-1:不存在 不执行任何操作
1-2:存在 逐个节点比较 比较成功返回位置 注意 别越界。
链表节点的删除:
1、判断链表是否存在
1-1:不存在 不执行任何操作
1-2:存在 逐个节点比较 删除指定节点 注意 别越界。
释放链表:
1、判断链表是否存在
1-1:不存在 不执行任何操作
1-2:存在 逐个节点节点释放 注意 别越界
知识点5【链表的逆序】1-2
1 STU* reverse_link(STU *head)
2 {
3 //判断链表是否存在
4 if(head == NULL)
5 {
6 printf("link not founf\n");
7 return head;
8 }
9 else//链表存在
10 {
11 //int *p,num;//p为int * , num为int
12 STU *pb,*pr;//pb为STU * , pr为STU *
13
14 //pb保存head‐>next(原因head‐>next会置NULL)
15 pb = head‐>next;
16 //将head‐>next置NULL (原因:头节点变尾节点)
17 head‐>next = NULL;
18
19 while(pb != NULL)
20 {
21 //pr保存pb‐>next (原因:pb‐>next会指向head)
22 pr = pb‐>next;
23
24 //pb‐>next 指向 head (原因:逆转方向)
25 pb‐>next = head;
26
27 //保存 逆转方向的代码 可以重复 执行
28 head = pb;
29 pb = pr;
30 }
31
32 return head;
33 }
34 return head;
35 }
知识点6【链表的排序】
选择法排序:(以数组实现)
1 #include
2 int main()
3 {
4 int arr[10]={0};
5 int n = sizeof(arr)/sizeof(arr[0]);
6 int i=0,j=0,min=0;
7
8 printf("请输入%d个int数据\n",n);
9 for(i=0;i10 {
11 scanf("%d",arr+i);
12 }
13
14 //选择法排序
15 for(i=0;i16 {
17 for(min=i,j=min+1; j18 {
19 if(arr[min] > arr[j])
20 min = j;
21 }
22
23 if(min != i)
24 {
25 int tmp = 0;
26 tmp = arr[i];
27 arr[i]=arr[min];
28 arr[min]=tmp;
29 }
30
31 }
32
33 for(i=0;i34 {
35 printf("%d ",arr[i]);
36 }
37 printf("\n");
38
39 return 0;
40 }
运行结果:
选择法排序:(以链表实现)知识点1【深拷贝】0-1
总结:前提就是指针变量 作为 结构体的成员
浅拷贝:两个结构体变量 中的 指针成员 指向 同一块堆区空间。
深拷贝:两个结构体变量 中的 指针成员 指向 各自的堆区空间。
知识点2【文件】
1、文件的存取过程
2、磁盘文件的分类0-2
总结:(重要)
3、文件指针
注意:不要关系FILE的细节 只需要 会用FILE 定义指针变量就行:FILE *fp=NULL;
4、fopen打开一个文件
文件使用方式:
r:以只读的方式打开文件
w:以只写的方式打开文件
a:以追加的方式打开文件(往文件的末尾写入数据)
+:以可读可写的方式打开
b:以二进制的方式打开文件
t:以文本的方式打开文件(省略)
打开方式的组合形式:1-1
关闭文件fclose
知识点3【文件的字节读写】
1、字节的读操作 fgetc函数
2、字节的写操作 fputc
练习:
知识点4【文件的字符串读写】1-2
1、使用fputs 往文件中写入一个字符串
2、使用fgets从文件中获取字符串
知识点5【文件的块读写】fread fwrite
1、使用fwrite 将 数据块 写入到文件中
2、使用fread 从文件中 读取 数据块
知识点1【深拷贝】0-1
1 #include
2 #include
3 #include
4 typedef struct
5 {
6 int num;
7 char *name;
8 }DATA;
9 void test01()
10 {
11 DATA data1;
12 DATA data2;
13
14 data1.num = 100;
15 data1.name = (char *)calloc(1,12);
16 strcpy(data1.name, "my data");
17
18 data2.num = data1.num;
19 //为结构体变量 申请 独立的空间
20 data2.name = (char *)calloc(1,12);
21 strcpy(data2.name, data1.name);
22
23 printf("data1:num = %d, name=%s\n", data1.num, data1.name);
24 printf("data2:num = %d, name=%s\n", data2.num, data2.name);
25
26 if(data1.name != NULL)
27 {
28 free(data1.name);
29 data1.name = NULL;
30 }
31
32 if(data2.name != NULL)
33 {
34 free(data2.name);
35 data2.name = NULL;
36 }
37
38
39 }
40 int main(int argc,char *argv[])
41 {
42 test01();
43 return 0;
44 }
运行结果:
总结:前提就是指针变量 作为 结构体的成员
浅拷贝:两个结构体变量 中的 指针成员 指向 同一块堆区空间。
深拷贝:两个结构体变量 中的 指针成员 指向 各自的堆区空间。
知识点2【文件】
1、文件的存取过程
缓冲区的目的:提高存取效率 磁盘使用寿命
2、磁盘文件的分类0-2
物理上 所有的磁盘文件都是 二进制存储,以字节为单位 顺序存储。
逻辑上的文件分类:
文本文件:基于字符编码的文件
二进制文件:基于值编码的文件
总结:(重要)
3、文件指针
注意:不要关系FILE的细节 只需要 会用FILE 定义指针变量就行:FILE
*fp=NULL;
(了解)
4、fopen打开一个文件
文件使用方式:
r:以只读的方式打开文件
w:以只写的方式打开文件
a:以追加的方式打开文件(往文件的末尾写入数据)
+:以可读可写的方式打开
b:以二进制的方式打开文件
t:以文本的方式打开文件(省略)
打开方式的组合形式:1-1
关闭文件fclose
知识点3【文件的字节读写】
1、字节的读操作 fgetc函数
1 void test03()
2 {
3 char buf[128]="";
4 int i=0;
5 FILE *fp = NULL;
6 //1、使用fopen打开一个文件 获得文件指针
7 fp = fopen("a.txt", "r");
8 if(fp == NULL)
9 {
10 perror("fopen");
11 return;
12 }
13
14 //2、对文件的操作 fgetc
15 while(1)
16 {
17 //fgetc调用一次 读取到一个字节
18 buf[i] = fgetc(fp);
19 if(buf[i] == EOF)//EOF表已经对到文件末尾
20 {
21 break;
22 }
23 i++;
24 }
25 printf("buf=%s\n", buf);
26
27 //3、关闭文件
28 fclose(fp);
29 }
运行结果:事先 本地 创建a.txt 文件内容 为 hello file
2、字节的写操作 fputc
1 void test04()
2 {
3 char buf[128]="";
4 int i=0;
5 FILE * fp =NULL;
6 fp = fopen("b.txt", "w");
7 if(fp == NULL)
8 {
9 perror("fopen");
10 return;
11 }
12
13 //使用fputc进行文件的数据写入
14 printf("请输入要写入文件的字符串:");
15 fgets(buf,sizeof(buf),stdin);//会获取换行符
16 buf[strlen(buf)‐1] = 0;//去掉键盘输入的换行符
17
18 //将字符串buf中的元素 逐个写入文件中
19 while(buf[i] != '\0')
20 {
21 fputc(buf[i], fp);
22 i++;
23 }
24
25 fclose(fp);
26 }
运行结果:
练习:
1 void test05()
2 {
3 //需求:从a.txt读取文件内容 写入到b.txt
4 FILE *fp1 =NULL;
5 FILE *fp2 =NULL;
6
7 //以只读的方式打开a.txt
8 fp1 = fopen("a.txt","r");
9 if(fp1 == NULL)
10 {
11 perror("fopen");
12 return;
13 }
14
15 //以只写的方式打开b.txt
16 fp2 = fopen("b.txt","w");
17 if(fp2 == NULL)
18 {
19 perror("fopen");
20 return;
21 }
22
23
24 //从fp1中 每读取一个 字节 写入到fp2中
25 while(1)
26 {
27 char ch;
28 //读
29 ch = fgetc(fp1);
30 if(ch == EOF)//已经读到文件末尾
31 break;
32
33 //写
34 fputc(ch,fp2);
35
36 }
37 fclose(fp1);
38 fclose(fp2);
39 return;
40 }
运行结果:
知识点4【文件的字符串读写】1-2
1、使用fputs 往文件中写入一个字符串
案例:
1 void test06()
2 {
3 //指针数组
4 char *buf[]={"窗前明月光\n","疑似地上霜\n","举头望明月\n","低头思故乡"};
5 int n = sizeof(buf)/sizeof(buf[0]);
6 FILE *fp = NULL;
7 int i=0;
8
9 fp = fopen("c.txt", "w");
10 if(fp == NULL)
11 {
12 perror("fopen");
13 return;
14 }
15
16 for(i=0;i17 {
18 fputs(buf[i], fp);
19 }
20
21 fclose(fp);
22 }
运行结果:
2、使用fgets从文件中获取字符串
返回值:
成功:返回读到字符串的首元素地址
失败:返回NULL
获取文件一行的数据:
1 void test07()
2 {
3 char buf[128]="";
4 FILE *fp = NULL;
5 char *path = "c.txt";
6 fp = fopen(path, "r");
7 if(fp == NULL)
8 {
9 perror("fopen");
10 return;
11 }
12
13 //err 打开一个文件名叫"path" 而不是path指向的文件名"c.txt"
14 //fp = fopen("path", "r");
15 //fp = fopen("c.txt", "r");
16
17 while(fgets(buf,sizeof(buf),fp))
18 {
19 printf("%s\n", buf);
20 }
21
22 fclose(fp);
23 }
运行结果:
知识点5【文件的块读写】fread fwrite
1、使用fwrite 将 数据块 写入到文件中
1 typedef struct
2 {
3 char name[16];//姓名
4 int deff;//防御
5 int atk;//攻击
6 }HERO;
7
8 void test08()
9 {
10 HERO hero[]={
11 {"德玛西亚",80, 60},
12 {"盲僧",90, 80},
13 {"小法",40, 85},
14 {"小炮",50, 90}
15 };
16 int n = sizeof(hero)/sizeof(hero[0]);
17
18 FILE *fp = NULL;
19 fp = fopen("hero.txt", "w");
20 if(fp == NULL)
21 {
22 perror("fopen");
23 return;
24 }
25
26 //fwrite 将内存的数据原样的 输出到 文件中
27 //写入文件的数据 不便于 用户查看 但是 不会影响 程序的读
28 fwrite(hero, sizeof(HERO), n, fp);
29
30 fclose(fp);
31 }
运行结果:
2、使用fread 从文件中 读取 数据块
案例:
1 void test09()
2 {
3 HERO hero[4];
4 int i=0;
5 FILE *fp = NULL;
6 fp = fopen("hero.txt", "r");
7 if(fp == NULL)
8 {
9 perror("fopen");
10 return;
11 }
12
13 fread(hero,sizeof(HERO), 4, fp);
14
15 for(i=0;i<4;i++)
16 {
17 //printf("英雄姓名:《%s》,防御:《%d》,伤害:《%d》\n", \
18 hero[i].name,hero[i].deff,hero[i].atk);
19 printf("英雄姓名:《%s》,防御:《%d》,伤害:《%d》\n", \
20 (hero+i)‐>name,(hero+i)‐>deff,(hero+i)‐>atk);
21 }
22
23
24 fclose(fp);
25 }
运行结果:(共26张PPT)
数组
数组的概念数组的分类定义并初始化数组的定义数组的元素的引用方法数组的概念在程序设计中,为了方便处理数据把具有相同类型的若干变量按有序形式组织起来—称为数组数组属于构造数据类型一个数组可以分解为多个数组元素这些数组元素可以是基本数据类型或构造类型.int a[10]; struct stu boy[10];数组的分类按数组元素类型的不同,数组可分为数值数组、字符数组、指针数组、结构数组等类别int a[10] char s[10] char *p[10];数组的分类inta[10] = {1,2,3,4,5,6};intb[4][2]={ {1,2},{3,4},{5,6},{7,8} };char string1[8] = {‘a’,’b’,’c’};char string2[8] = {“abcdefg”};char *q[10] = {&a[0],&a[1],&a[2]};struct student boy[5];int (*sum[10])(int x,int y);数组的分类数值数组1.一维数值数组:short a[10];intb[10];long c[10];float b[10];double e[10];2.二维数值数组:inta[2][3]={ {80,75,78},{61,65,99} };floatb[2][3]={ {80.5,75.5,78.5},{61.5,65.5,99.5}};数组的定义一维数组的定义:1.符合标识符的书写规定(数字、英文字母、下划线)2.数组名不能与其它变量名相同,以下是错误的:int main(){intnum;floatnum[10];}数组的定义3.方括号中常量表达式表示数组元素的个数如int a[3]表示数组a有3个元素其下标从0开始计算,因此3个元素分别为a[0],a[1],a[2]4.不能在方括号中用变量来表示元素的个数int n=10;int b[n];//这种表达式是错误的数组的初始化一维数组的初始化:在定义数组的同时进行赋值,称为初始化1.定义数组时可以逐个列出数组的值例如:int a[5]={1,2,3,4,5};2.给全部元素赋值时,可以不给出数组元素的个数(下标值)例如:int a[5]={1,2,3,4,5};可写为:int a[ ]={1,2,3,4,5};定义并初始化3.可以只给部分元素赋初值例如:int a[10]={0,1,2,3,4};只给a[0]~a[4]5个元素赋值,后5个元素自动赋值4.全局数组若不初始化,编译器将其初始化为零.局部数组若不初始化,内容为随机值.定义并初始化01.array_1.c数组的定义二维数组C语言允许构造多维数组,多维数组元素有多个下标,以标识它在数组中的位置,所以也称为多下标变量;在实际问题中有很多量是二维的或多维的,例如代数中的矩阵、图像采集与显示;本小节将绍二维数组,多维数组可由二维数组类推而得到.数组的定义二维数组的定义int a[3][4];1. 命名规则同一维数组2.定义了一个三行四列的数组,数组名为a其元素类型为整型,该数组的元素个数为3×4个,即:a[0][0],a[0][1],a[0][2],a[0][3]a[1][0],a[1][1],a[1][2],a[1][3]a[2][0],a[2][1],a[2][2],a[2][3]数组的定义3.二维数组在概念上是二维的其下标在两个方向上变化,对其访问一般需要两个下标4.二维数组实际的硬件存储器是连续编址的即放完一行之后顺次放入第二行定义并初始化二维数组的初始化例如:数组a[2][3]1.按行分段赋值可写为:int a[2][3]={ {80,75,92},{61,65,71} };2.按行连续赋值可写为:int a[2][3]={ 80,75,92,61,65,71 };定义并初始化注意:(同一维数组)1.定义数组时可以逐个列出数组的值(初始化)2.可以只给部分元素赋初值,未初始化则为03.全局数组若不初始化,编译器将其初始化为零.局部数组若不初始化,内容为随机值.4. 给全部数据初始化时,行下标可以省略int a[][3]={ {80,75,92},{61,65,71} };定义并初始化02.array_2.c二维数组: 五行、三列行代表人: 老大到老五列代表科目:语、数、外数组元素的引用方法一维数组元素的引用方法数组名 [下标];//下标代表数组元素在数组中的位置int a[10];a[2];二维数组元素的引用方法数组名[行下标][列下标];int a[3][4];a[1][2]数组元素的引用方法例:数组元素的引用方法字符数组的定义char c1[]={‘c’,’ ’,’p’,’r’,’o’,’g’};char c2[] = “c prog”;char a[][5] = {{‘B’,’A’,’S’,’I’,’C’},{‘d’,’B’,’A’,’S’,’E’}};char a[][6] = {“hello”,“world”};数组元素的引用方法字符数组的引用1.用字符串方式赋值比用字符逐个赋值要多占1个字节,用于存放字符串结束标志‘\0’;2.上面的数组c2在内存中的实际存放情况为:注:'\0'是由C编译系统自动加上的3.由于采用了'\0'标志,字符数组的输入输出将变得简单方便.'c'' ''p''r''o''g''\0'数组元素的引用方法例int main( ){char str[15];printf(“input string:\n”);scanf(“%s”,str);printf(“output:%s\n”,str);return 0;}数组元素的引用方法03.array_char_2.c加入字符串提示数组元素的引用方法C专门为字符数组的输入输出设置了一组函数#include<stdio.h>intmain(){char str[15]="";gets(str);puts(str);return 0;}数组元素的引用方法gets(str)与scanf(“%s”,str)的区别:gets(str)允许输入的字符串含有空格.scanf(“%s”,str)不允许含有空格.知识点1【sscanf高级用法】
1、%[a-z] 提取a-z的字符串
2、%[aBc] 提取 a B c
3、%[^abc] 只要不是a b c任何一个 都要
知识点2【const关键字】
1、const修饰变量 为只读
2、const int *p;
3、int * const p;
4、const int * const p;(*p 只读 p只读)
知识点3【结构体类型的定义形式】
知识点4【结构体变量的定义】
知识点5【结构体变量的初始化】(下一)
知识点6【结构体变量 获取 键盘输入】
知识点7【结构体变量 之间的赋值】
知识点8【结构体数组】
案例:定义一个结构体数组 获取键盘输入 求平均age
知识点9【冒泡排序】下二
知识点10【结构体数组排序】
知识点1【sscanf高级用法】
1、%[a-z] 提取a-z的字符串
1 void test01()
2 {
3 char buf[128]="";
4 //%[]都是 按 字符串 提取
5 sscanf("abcDefABC","%[a‐z]",buf);
6 printf("buf=%s\n", buf);//"abc"
7
8 return;
9 }
2、%[aBc] 提取 a B c
1 void test02()
2 {
3 char buf[128]="";
4
5 sscanf("aaBBcEdef","%[aBc]",buf);
6 printf("buf=%s\n", buf);//aaBBc
7 }
3、%[^abc] 只要不是a b c任何一个 都要
1 void test03()
2 {
3 char buf[128]="";
4 sscanf("ABCcABC","%[^abc]",buf);
5 printf("buf=%s\n", buf);//ABC
6 }
案例:
1 void test04()
2 {
3 char name[32]="";
4 char addr[32]="";
5 //sscanf("lianghe@","%[^@]%*1s%[^.]",name,addr );
6 //sscanf("lianghe@","%[^@]%*c%[^.]",name,addr );
7 sscanf("lianghe@","%[^@]@%[^.]",name,addr );
8 printf("name=%s\n",name);//"lianghe"
9 printf("addr=%s\n",addr);//"1000phone"
10 }
案例:
1 void test05()
2 {
3 int m1=0,s1=0;
4 int m2=0,s2=0;
5 char song[128]="";
6
7 char msg[128]="[12:13.46][8:23.45]给我一个威武雄壮的汉子";
8
9 sscanf(msg,"[%d:%d%.%*d][%d:%d.%*d]%s",&m1,&s1,&m2,&s2,song);
10 printf("%d‐%d\n",m1,s1);
11 printf("%d‐%d\n",m2,s2);
12 printf("%s\n", song);
13 }
运行结果:
知识点2【const关键字】
1、const修饰变量 为只读
1 void test06()
2 {
3 //const 修饰num为只读变量 num只能取值 num不能被赋值
4 //const 修饰的变量 尽量 初始化
5 const int num=10;
6 printf("num = %d\n",num);//ok
7 //num = 100;//err num不能被赋值
8
9 printf("&num = %p\n", &num);
10
11 //但是如果知道 num的地址 也可以间接的修改num的值
12 *(int *)(&num) = 1000;//少干
13 printf("num = %d\n",num);
14
15 }
运行结果:
2、const int *p;
const在*的左边 表示 const 修饰的是* 而不是 p.
效果:用户不能借助*p更改空间的内容 但是 p可以指向其他空间(*p 只读 p可读可写)
1 void test07()
2 {
3 int num1 = 10;
4 int num2 = 20;
5
6 //const在*的左边 *p只读 p可读可写
7 const int *p = &num1;
8 printf("*p = %d\n", *p);//10
9 //*p = 1000;//err *p只读
10
11 p=&num2;//ok p可读可写
12 printf("*p = %d\n", *p);//20
13 }
3、int * const p;
const 在*的右边 const修饰的是p 而不是*。
用户可以通过*p 修改p所指向空间的内容 但是 不能再更改p的指向(*p可读可写 p只读)
1 void test08()
2 {
3 int num1 = 10;
4 int num2 = 20;
5
6 //const在*的右边 *p可读可写 p只读
7 int * const p = &num1;//p一旦初始化 就不能更改指向
8
9 printf("*p = %d\n", *p);//10
10 *p = 1000;//ok *p可读可写
11 printf("*p = %d\n", *p);//1000
12
13 //p=&num2;//err p只读
14 }
4、const int * const p;(*p 只读 p只读)
1 void test09()
2 {
3 int num1 = 10;
4 int num2 = 20;
5
6 const int * const p = &num1;//*p 和 p都是只读
7 //*p = 1000;//err *p只读
8 //p = &num2;//err p只读
9 }
知识点3【结构体类型的定义形式】
1 //struct 是结构体关键字 stu是结构体类型名
2 //使用结构体类型 必须是 struct stu
3 //num name age 叫做结构体中的成员
4 //定义结构体类型的时候 不要给成员 赋值(重要)
5 //定义结构体类型的时候 并没有分配空间 所以 不能给成员赋值
6 struct stu
7 {
8 int num=10;//4B
9 char name[32];//32B
10 int age; //4B
11 };//一定要记得有;号
12
13 //结构体中的成员 拥有独立的空间(重要)
1 #include
2 struct stu
3 {
4 int num;//4B
5 char name[32];//32B
6 int age;//4B
7 };
8
9 void test01()
10 {
11 printf("sizeof(struct stu) =%d\n",sizeof(struct stu));//40B
12 return;
13 }
14 int main(int argc,char *argv[])
15 {
16 test01();
17 return 0;
18 }
知识点4【结构体变量的定义】
1 void test01()
2 {
3 //就用结构体类型 定义一个变量
4 struct stu lucy;//lucy是局部变量 lucy的成员内容不确定
5
6 //通过结构体变量 访问 结构体中的成员 (一定要遵循成员自身的类型)
7 printf("num = %d\n", lucy.num);
8 printf("name = %s\n", lucy.name);
9 printf("age = %d\n", lucy.age);
10
11 //一定要遵循成员自身的类型
12 lucy.num = 100;
13 strcpy(lucy.name, "德玛西亚");
14 lucy.age = 18;
15
16 printf("num = %d\n", lucy.num);
17 printf("name = %s\n", lucy.name);
18 printf("age = %d\n", lucy.age);
19 return;
20 }
运行结果:
知识点5【结构体变量的初始化】(下一)
1 //结构体类型的定义
2 struct stu
3 {
4
5 int num;//4B
6 char name[32];//32B
7 int age;//4B
8 };
9
10 void test03()
11 {
12 //初始化的顺序 必须和 结构体成员的 顺序一致
13 struct stu lucy={100,"德玛西亚",18 };
14
15 printf("num=%d, name=%s, age=%d\n",lucy.num,lucy.name,lucy.age);
16
17 //清空结构体变量
18 memset(&lucy,0,sizeof(lucy));
19 }
知识点6【结构体变量 获取 键盘输入】
1 void test04()
2 {
3 struct stu lucy;
4 memset(&lucy, 0,sizeof(lucy));
5
6 printf("请输入num name age:");
7 //&lucy.num 取的是num成员地址
8 scanf("%d %s %d",&lucy.num, lucy.name , &lucy.age);
9
10 printf("num=%d, name=%s, age=%d\n",lucy.num,lucy.name,lucy.age);
11
12 }
知识点7【结构体变量 之间的赋值】
1 void test05()
2 {
3 struct stu lucy={100,"小法", 18};
4 struct stu bob;
5
6 //需求 将lucy的值 赋值 bob
7 //方式一:逐个成员赋值
8 //bob.num = lucy.num;
9 //strcpy(bob.name,lucy.name);
10 //bob.age = lucy.age;
11
12 //方法二:相同类型的结构体变量 可以直接赋值(推荐)
13 //bob = lucy;
14
15 //方法三:方法二的底层实现
16 memcpy(&bob,&lucy,sizeof(struct stu));
17
18 printf("num=%d, name=%s, age=%d\n",bob.num,bob.name,bob.age);
19 }
知识点8【结构体数组】
1 void test06()
2 {
3 struct stu arr[5]={
4 {100,"小法",18},
5 {101,"德玛西亚",19},
6 {102,"盲僧",20},
7 {103,"快乐风男",30},
8 {104,"提莫",8}
9 };
10 int n = sizeof(arr)/sizeof(arr[0]);
11 int i=0;
12
13 for(i=0;i14 {
15 printf("num=%d, name=%s, age=%d\n",arr[i].num,arr[i].name,arr[i].age);
16 }
17 }
运行结果:
案例:定义一个结构体数组 获取键盘输入 求平均age
1 void test07()
2 {
3 struct stu arr[5];
4 int n = sizeof(arr)/sizeof(arr[0]);
5 int i=0;
6 int sum = 0;
7
8 memset(arr,0,sizeof(arr));//清空整个数组
9
10 printf("请输入%d个学生的信息\n", n);
11 for(i=0;i12 {
13 scanf("%d %s %d", &arr[i].num, arr[i].name, &arr[i].age );
14 }
15
16 for(i=0;i17 {
18 sum += arr[i].age;
19 }
20 printf("平均年龄为%d\n",sum/n);
21 }
运行结果:
知识点9【冒泡排序】下二
1 #include
2 void test08()
3 {
4 int n = 0;
5 int i=0,j=0;
6 int *arr = NULL;
7 printf("请输入数据的个数:");
8 scanf("%d", &n);
9
10 //根据元素的个数申请空间
11 arr = (int *)calloc(n,sizeof(int));
12 if(NULL == arr)
13 {
14 perror("calloc");
15 return;
16 }
17
18 //键盘给动态数组 获取输入
19 printf("请输入%d个int数据\n", n);
20 for(i=0;i21 {
22 scanf("%d", arr+i);//arr+i 第i个元素的地址
23 }
24
25 //排序
26 for(i=0;i27 {
28 for(j=0;j29 {
30 if(arr[j]>arr[j+1])//从小‐‐‐>大
31 {
32 int tmp = 0;
33 tmp = arr[j];
34 arr[j]=arr[j+1];
35 arr[j+1]=tmp;
36 }
37 }
38 }
39
40 //数组的遍历
41 for(i=0;i42 {
43 printf("%d ",arr[i]);
44 }
45 printf("\n");
46
47 //释放空间
48 if(arr != NULL)
49 {
50 free(arr);
51 arr=NULL;
52 }
53 }
运行结果:
冒泡排序升级版:
1 #include
2 void test08()
3 {
4 int n = 0;
5 int i=0,j=0;
6 int *arr = NULL;
7 printf("请输入数据的个数:");
8 scanf("%d", &n);
9
10 //根据元素的个数申请空间
11 arr = (int *)calloc(n,sizeof(int));
12 if(NULL == arr)
13 {
14 perror("calloc");
15 return;
16 }
17
18 //键盘给动态数组 获取输入
19 printf("请输入%d个int数据\n", n);
20 for(i=0;i21 {
22 scanf("%d", arr+i);//arr+i 第i个元素的地址
23 }
24
25 //排序
26 for(i=0;i27 {
28 int flag = 0;
29 for(j=0;j30 {
31 if(arr[j]>arr[j+1])//从小‐‐‐>大 交换
32 {
33 int tmp = 0;
34 tmp = arr[j];
35 arr[j]=arr[j+1];
36 arr[j+1]=tmp;
37 flag = 1;
38 }
39 }
40
41 if(flag == 0)//数组已经有序
42 break;
43 }
44 printf("i=%d\n",i);
45
46 //数组的遍历
47 for(i=0;i48 {
49 printf("%d ",arr[i]);
50 }
51 printf("\n");
52
53 //释放空间
54 if(arr != NULL)
55 {
56 free(arr);
57 arr=NULL;
58 }
59 }
运行结果:
知识点10【结构体数组排序】
1 void test09()
2 {
3 struct stu arr[5];
4 int i=0,j=0;
5 int n = sizeof(arr)/sizeof(arr[0]);
6 memset(arr,0,sizeof(arr));//整个数组 清0
7
8 //获取键盘输入
9 printf("请输入%d个学生信息\n",n);
10 for(i=0;i11 {
12 scanf("%d %s %d",&arr[i].num, arr[i].name, &arr[i].age);
13 }
14
15 //根据学号 排序
16 for(i=0;i17 {
18 for(j=0;j19 {
20 if(arr[j].num < arr[j+1].num)//按照num的大小排序
21 {
22 struct stu tmp;
23 tmp = arr[j];
24 arr[j]=arr[j+1];
25 arr[j+1]=tmp;
26 }
27 }
28 }
29
30 //遍历
31 printf("‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐\n");
32 for(i=0;i33 {
34 printf("%d %s %d\n",arr[i].num, arr[i].name,arr[i].age);
35 }
36 }
运行结果:i love you知识点1【atoi atol atof 】将字符串转化为数值
知识点2【字符串切割函数 strtok】(了解)
知识点3【sprintf组包】(了解)
知识点4【sscanf解包】
案例1:sscanf的基本语法
sscanf的高级用法1:使用%*s %*d 跳过提取的内容(不要提取的内容)
sscanf的高级用法2:使用%[n]s %[n]d 提取指定宽度n的字符串或数据
知识点1【atoi atol atof 】将字符串转化为数值
头文件:stdlib.h
1 #include
2 #include//atoi atol
3 void test01()
4 {
5 printf("%d\n",atoi("123"));
6 printf("%ld\n",atol("12345"));
7 printf("%f\n",atof("12.3"));
8
9 return;
10 }
11 int main(int argc,char *argv[])
12 {
13 test01();
14 return 0;
15 }
运行结果:
自定义函数实现atoi的功能:
1 int my_atoi(char *buf)
2 {
3 //buf="1234"
4 int sum = 0;
5 while(*buf != '\0' && (*buf>='0' && *buf<='9'))
6 {
7 sum = sum*10+(*buf++)‐'0';
8 }
9
10 return sum;
11 }
12 void test02()
13 {
14 char buf[128]="";
15 printf("请输入字符串:");
16 scanf("%s",buf);
17
18 printf("%d\n",my_atoi(buf));
19 return;
20 }
21 int main(int argc,char *argv[])
22 {
23 test02();
24 return 0;
25 }
运行结果:
知识点2【字符串切割函数 strtok】(了解)
1 char *strtok(char *str, const char *delim);
2 第一次切割: str必须指向要切割的字符串首元素地址 delim指向要切割的符号
3 第2~n次切割:str必须指向NULL delim指向要切割的符号
4
5 返回值:切割成功 返回切割到字符换片段的首元素地址 失败:NULL
6
7 注意:strtok不能切割字符串常量
1 "hehe:haha:xixi:lala"
2 如果切割完成会产生 "hehe" "haha" "xixi" "lala"
3 一般选择 char *arr[] 指针数组 来存放上面 独立的字符串的首元素地址
案例1:
1 void test03()
2 {
3 char buf[]="hehehe:haha:xixi:lala:heihei:henhen";
4 char *arr[32]={NULL};
5 int i=0;
6
7 //第1次切割
8 arr[i] = strtok(buf, ":");
9
10 //第2~n切割
11 while(arr[i] != NULL)//保证上一次切割正常 才有进行下一次切割的必要
12 {
13 i++;
14 arr[i] = strtok(NULL,":");
15 }
16
17 //遍历切割的内容
18 i=0;
19 while(arr[i] != NULL)
20 {
21 printf("%s\n",arr[i]);
22 i++;
23 }
24
25 }
运行结果:
案例:
1 void test04()
2 {
3 char buf[]="hehehe:haha:xixi:lala:heihei:henhen";
4 char *arr[32]={buf,NULL};
5 int i=0;
6
7 //切割
8 while(1)
9 {
10 arr[i] = strtok(arr[i],":");
11 if(arr[i] == NULL)
12 break;
13 i++;
14 }
15
16 //遍历切割的内容
17 i=0;
18 while(arr[i] != NULL)
19 {
20 printf("%s\n",arr[i]);
21 i++;
22 }
23
24 }
案例:
1 void test05()
2 {
3 char buf[]="hehehe:haha:xixi:lala:heihei:henhen";
4 char *arr[32]={buf,NULL};
5 int i=0;
6
7 //切割
8 while(arr[i++] = strtok(arr[i],":"));
9
10 //遍历切割的内容
11 i=0;
12 while(arr[i] != NULL)
13 {
14 printf("%s\n",arr[i]);
15 i++;
16 }
17
18 }
注意:
1 void test05()
2 {
3 char buf[]="hehehe:haha#xixi@lala:::::::heihei####henhen";
4 char *arr[32]={buf,NULL};
5 int i=0;
6
7 //切割
8 while(arr[i++] = strtok(arr[i],":#@"));
9
10 //遍历切割的内容
11 i=0;
12 while(arr[i] != NULL)
13 {
14 printf("%s\n",arr[i]);
15 i++;
16 }
17
18 }
知识点3【sprintf组包】(了解)
案例:
1 int sprintf(buf,"格式",数据)
2 //buf:用来存放组好的报文
3 //"格式":按照格式组包
4 //数据:各个零散的数据
5
6 返回值:返回值的是组好的报文的实际长度(不包含'\0')
1 void test06()
2 {
3 int year = 2020;
4 int mon = 2;
5 int day = 24;
6 int len = 0;
7
8 //需求:将2020 2 24组成一个"2020年2月24日"
9 char buf[128]="";
10
11 //%d 和 year 类型要一一对应
12 len = sprintf(buf,"%d年%d月%d日", year,mon,day);
13 printf("len = %d\n", len);
14 printf("buf=%s\n",buf);
15 }
运行结果:
案例:
1 void test07()
2 {
3 char name[]="梁何";
4 int age = 18;
5 char sex[]="男";
6 char addr[]="重庆市潼南区";
7
8 char buf[128]="";
9 int len = 0;
10 len = sprintf(buf,"姓名:%s 年龄:%d 性别:%s 地址:%s",name,age,sex, addr);
11 printf("len = %d\n",len);
12 printf("buf=%s\n",buf);
13
14 }
运行结果:
将数值转成字符串
1 void test08()
2 {
3 int num = 2020;
4 char buf[128]="";
5
6 sprintf(buf,"%d",num);
7 printf("buf=%s\n", buf);//"2020"
8 }
知识点4【sscanf解包】
案例1:sscanf的基本语法
1 void test09()
2 {
3 char buf[]="2020年2月24日";
4 int year=0;
5 int mon=0;
6 int day=0;
7 char ch = 0;
8 char str_year[32]="";
9 char str_mon[32]="";
10 char str_day[32]="";
11
12
13 //%d 只能提取'0'~'9'
14 sscanf(buf,"%d年%d月%d日", &year,&mon,&day );
15 printf("year = %d\n",year);//2020
16 printf("mon = %d\n",mon);//2
17 printf("day = %d\n",day);//24
18
19 //%c 提取一个字符 %f 提取提取浮点数
20 sscanf(buf,"%c", &ch);
21 printf("##%c##\n",ch);//'2'
22
23 //%s 提取一个字符串 遇到空格、回车、'\0' 停止获取
24 //buf==>"2020年2月24日"
25 sscanf(buf,"%s年%s月%s日",str_year, str_mon,str_day );
26 printf("str_year=%s\n",str_year);//"2020年2月24日"
27 printf("str_mon=%s\n",str_mon);//null
28 printf("str_day=%s\n",str_day);//null
29 }
运行结果:
sscanf的高级用法1:使用%*s %*d 跳过提取的内容(不要提取的内
容)
1 void test10()
2 {
3 int data1=0;
4 //sscanf("1234 5678","1234 %d",&data1);//5678
5 //sscanf("1234 5678","%*d %d",&data1);//5678
6 sscanf("1234 5678","%*s %d",&data1);//5678
7 printf("data1=%d\n",data1);
8 }
sscanf的高级用法2:使用%[n]s %[n]d 提取指定宽度n的字符串或数据
1 void test11()
2 {
3 int data1 = 0;
4 int data2 = 0;
5 char buf[16]="";
6
7 sscanf("12abc5678","%*5s%d" ,&data1);//5678
8 printf("data1=%d\n",data1);
9
10
11 sscanf("12345678","%*2s%2d%*2d%s" ,&data2, buf);//data2=34 buf="78";
12 printf("data2 = %d\n",data2);//34
13 printf("buf=%s\n", buf);//"78"
14 }
运行结果:知识点1【链表的排序(选择法)】0-1(了解)
1 void sort_link(STU *head)
2 {
3 //1、判断链表是否存在
4 if(NULL == head)
5 {
6 printf("link not found\n");
7 return;
8 }
9 else
10 {
11 STU *p_i = head;//i=0
12 while(p_i‐>next != NULL)//i13 {
14 STU *p_min = p_i;//min = i;
15 STU *p_j = p_min‐>next;//j = min+1
16 while(p_j != NULL)//j17 {
18
19 //寻找成员num最小值的 节点
20 if(p_min‐>num > p_j‐>num)//if(arr[min] > arr[j])
21 p_min = p_j;//min = j
22 p_j = p_j‐>next;//j++
23
24 }
25
26 if(p_min != p_i)//min != i
27 {
28 //只交换数据域(1、节点内容整体交换 2、只交换指针域)
29 //1、节点内容整体交换(数据域交换第1次 指针域交换第1次)
30 STU tmp;
31 tmp = *p_i;
32 *p_i = *p_min;
33 *p_min = tmp;
34
35 //2、只交换指针域(指针域交换第2次)
36 tmp.next = p_i‐>next;
37 p_i‐>next = p_min‐>next;
38 p_min‐>next = tmp.next;
39 }
40
41
42 p_i = p_i‐>next;//i++
43 }
44
45 }
46
47 }
知识点2【双向循环链表】(了解)0-2
main.c
1 #include
2 #include
3 #include
4 //定义一个双向循环链表的节点类型
5 typedef struct stu
6 {
7 //数据域
8 int num;
9 char name[32];
10 int age;
11
12 //指针域
13 struct stu *next;//指向下一个节点
14 struct stu *pre;//指向前一个节点
15 }STU;
16
17 extern STU* insert_link(STU *head, STU tmp);
18 extern void print_link(STU *head);
19 extern STU* search_link(STU *head, char *name);
20 extern STU* delete_link(STU *head, int num);
21 extern STU* free_link(STU *head);
22
23 int main(int argc,char *argv[])
24 {
25 char name[32]="";
26 int num = 0;
27 int n =0;//保存学生的个数
28 int i=0;
29 STU *head = NULL;
30 STU *ret = NULL;
31
32 printf("请输入学员的个数:");
33 scanf("%d", &n);
34
35 for(i=0;i36 {
37 STU tmp;
38 printf("请输入第%d个学员的信息:\n",i+1);
39 scanf("%d %s %d", &tmp.num, tmp.name, &tmp.age);
40
41 //插入一个节点
42 head = insert_link(head, tmp);
43 }
44
45 //遍历整个链表
46 print_link(head);
47
48 //查找指定节点
49 printf("请输入要查找的用户名:");
50 scanf("%s",name);
51
52 //查找中...
53 #if 1
54 ret = search_link(head, name);
55 if(ret == NULL)
56 {
57 printf("没有找到与%s相关的节点信息\n", name);
58 }
59 else
60 {
61 printf("查找到的信息为:%d %s %d\n", ret‐>num, ret‐>name,ret‐>age);
62 }
63 #endif
64 //删除指定节点 num
65 printf("请输入要删除的学号:");
66 scanf("%d", &num);
67 head = delete_link(head, num);
68
69 //遍历整个链表
70 print_link(head);
71
72 //释放整个链表
73 head = free_link(head);
74
75 //遍历整个链表
76 print_link(head);
77
78 return 0;
79 }
80
81 //头部之前插入
82 STU* insert_link(STU *head, STU tmp)
83 {
84 //1、为插入的节点pi申请空间
85 STU *pi = (STU *)calloc(1,sizeof(STU));
86 //2、将tmp的值 赋值给 *pi
87 *pi = tmp;
88
89 //3、判断链表是否存在
90 if(head == NULL)//链表不存在
91 {
92 head = pi;
93 pi‐>next = head;
94 pi‐>pre = head;
95 }
96 else//链表存在(头部之前插入)
97 {
98 pi‐>next = head;
99 pi‐>pre = head‐>pre;
100 head‐>pre‐>next = pi;
101 head‐>pre = pi;
102 head = pi;
103 }
104
105 return head;
106 }
107
108 #if 1
109 void print_link(STU *head)
110 {
111 //判断链表是否存在
112 if(head == NULL)
113 {
114 printf("link not found\n");
115 return;
116 }
117 else//链表存在
118 {
119 STU *pb = head;
120 do
121 {
122 //访问节点内容
123 printf("num=%d, name=%s, age=%d\n", pb‐>num,pb‐>name,pb‐>age);
124 //pb指向下一个节点
125 pb = pb‐>next;
126 }while(pb != head);
127 }
128 return;
129 }
130 #endif
131 #if 0
132 void print_link(STU *head)
133 {
134 //判断链表是否存在
135 if(head == NULL)
136 {
137 printf("link not found\n");
138 }
139 else//链表存在
140 {
141 STU *pb = head;//pb指向头结点
142 STU *pf = head‐>pre;//pf指向了尾节点
143
144 do
145 {
146 if(pb == pf)//相遇 只需要打印pf或pb中任何一个信息就够了
147 {
148 printf("num=%d,name=%s,age=%d\n", pb‐>num,pb‐>name,pb‐>age);
149 break;
150 }
151 printf("num=%d,name=%s,age=%d\n", pb‐>num,pb‐>name,pb‐>age);
152 printf("num=%d,name=%s,age=%d\n", pf‐>num,pf‐>name,pf‐>age);
153
154 pb = pb‐>next;//next方向的移动
155 pf = pf‐>pre;//pre方向的移动
156 }while( pb‐>pre != pf );//pf和pb不能 擦肩而过
157
158 }
159 }
160
161 #endif
162
163 STU* search_link(STU *head, char *name)
164 {
165 //判断链表是否存在
166 if(head == NULL)
167 {
168 return NULL;
169 }
170 else//链表存在
171 {
172 STU *pb = head;//指向头结点
173 STU *pf = head‐>pre;//指向的是尾节点
174 printf("pb = %p\n", pb);
175 printf("pf = %p\n", pf);
176 printf("pb‐>pre = %p\n", pb‐>pre);
177
178
179 while( (strcmp(pb‐>name,name) != 0) && (strcmp(pf‐>name,name)!=0) && (p
b != pf) )
180 {
181 printf("##pb‐>name=%s##\n",pb‐>name);
182 printf("##pf‐>name=%s##\n",pf‐>name);
183 pb=pb‐>next;//next方向移动
184 pf=pf‐>pre;//pf方向移动
185
186 if(pb‐>pre == pf)
187 {
188 break;
189 }
190 }
191
192 if(strcmp(pb‐>name,name) == 0)
193 {
194
195 return pb;
196 }
197 else if(strcmp(pf‐>name,name)==0)
198 {
199 return pf;
200 }
201 }
202
203 return NULL;
204 }
205
206 STU* delete_link(STU *head, int num)
207 {
208 //判断链表是否存在
209 if(head == NULL)
210 {
211 printf("link not found\n");
212 return head;
213 }
214 else
215 {
216 STU *pb = head;//指向头节点
217 STU *pf = head‐>pre;//指向尾节点
218
219 //逐个节点寻找删除点
220 while((pb‐>num != num) && (pf‐>num != num) && (pf != pb))
221 {
222 pb = pb‐>next;//next方向移动
223 pf = pf‐>pre;//pre方向移动
224 if(pb‐>pre == pf)
225 break;
226 }
227
228 if(pb‐>num == num)//删除pb指向的节点
229 {
230
231 if(pb == head)//删除头节点
232 {
233 if(head == head‐>next)
234 {
235 free(pb);
236 head = NULL;
237 }
238 else
239 {
240 head‐>next‐>pre = head‐>pre;
241 head‐>pre‐>next = head‐>next;
242 head = head‐>next;
243 free(pb);
244 }
245
246 }
247 else//删除中尾部节点
248 {
249 pb‐>pre‐>next = pb‐>next;
250 pb‐>next‐>pre = pb‐>pre;
251 free(pb);
252 }
253 }
254 else if(pf‐>num == num)//删除pf指向的节点
255 {
256
257 if(pf == head)//删除头节点
258 {
259 if(head == head‐>next)
260 {
261 free(pf);
262 head = NULL;
263 }
264 else
265 {
266 head‐>next‐>pre = head‐>pre;
267 head‐>pre‐>next = head‐>next;
268 head = head‐>next;
269 free(pf);
270 }
271 }
272 else//删除中尾部节点
273 {
274 pf‐>pre‐>next = pf‐>next;
275 pf‐>next‐>pre = pf‐>pre;
276 free(pf);
277 }
278 }
279 else
280 {
281 printf("未找到%d相关的节点信息",num);
282 }
283 }
284
285 return head;
286 }
287
288 STU* free_link(STU *head)
289 {
290 if(head == NULL)//链表为空
291 {
292 printf("link not found\n");
293 return NULL;
294 }
295 else//链表存在
296 {
297 STU *pb = head;
298 STU *tmp;
299 do
300 {
301 tmp = pb;
302 pb = pb‐>next;
303 free(tmp);
304 }while(pb != head);
305 }
306
307 return NULL;
308 }
1、认识双向 循环 链表
2、双链表 插入
1 //头部之前插入
2 STU* insert_link(STU *head, STU tmp)
3 {
4 //1、为插入的节点pi申请空间
5 STU *pi = (STU *)calloc(1,sizeof(STU));
6 //2、将tmp的值 赋值给 *pi
7 *pi = tmp;
8
9 //3、判断链表是否存在
10 if(head == NULL)//链表不存在
11 {
12 head = pi;
13 pi‐>next = head;
14 pi‐>pre = head;
15 }
16 else//链表存在(头部之前插入)
17 {
18 pi‐>next = head;
19 pi‐>pre = head‐>pre;
20 head‐>pre‐>next = pi;
21 head‐>pre = pi;
22 head = pi;
23 }
24
25 return head;
26 }
3、链表的遍历
单向遍历
1 void print_link(STU *head)
2 {
3 //判断链表是否存在
4 if(head == NULL)
5 {
6 printf("link not found\n");
7 return;
8 }
9 else//链表存在
10 {
11 STU *pb = head;
12 do
13 {
14 //访问节点内容
15 printf("num=%d, name=%s, age=%d\n", pb‐>num,pb‐>name,pb‐>age);
16 //pb指向下一个节点
17 pb = pb‐>next;
18 }while(pb != head);
19 }
20 return;
21 }
双向遍历:1-1
1 void print_link(STU *head)
2 {
3 //判断链表是否存在
4 if(head == NULL)
5 {
6 printf("link not found\n");
7 }
8 else//链表存在
9 {
10 STU *pb = head;//pb指向头结点
11 STU *pf = head‐>pre;//pf指向了尾节点
12
13 do
14 {
15 if(pb == pf)//相遇 只需要打印pf或pb中任何一个信息就够了
16 {
17 printf("num=%d,name=%s,age=%d\n", pb‐>num,pb‐>name,pb‐>age);
18 break;
19 }
20 printf("num=%d,name=%s,age=%d\n", pb‐>num,pb‐>name,pb‐>age);
21 printf("num=%d,name=%s,age=%d\n", pf‐>num,pf‐>name,pf‐>age);
22
23 pb = pb‐>next;//next方向的移动
24 pf = pf‐>pre;//pre方向的移动
25 }while( pb‐>pre != pf );//pf和pb不能 擦肩而过
26
27 }
28 }
4、双向链表的查找
1 STU* search_link(STU *head, char *name)
2 {
3 //判断链表是否存在
4 if(head == NULL)
5 {
6 return NULL;
7 }
8 else//链表存在
9 {
10 STU *pb = head;//指向头结点
11 STU *pf = head‐>pre;//指向的是尾节点
12 printf("pb = %p\n", pb);
13 printf("pf = %p\n", pf);
14 printf("pb‐>pre = %p\n", pb‐>pre);
15
16
17 while( (strcmp(pb‐>name,name) != 0) && (strcmp(pf‐>name,name)!=0) && (p
b != pf) )
18 {
19 printf("##pb‐>name=%s##\n",pb‐>name);
20 printf("##pf‐>name=%s##\n",pf‐>name);
21 pb=pb‐>next;//next方向移动
22 pf=pf‐>pre;//pf方向移动
23
24 if(pb‐>pre == pf)
25 {
26 break;
27 }
28 }
29
30 if(strcmp(pb‐>name,name) == 0)
31 {
32
33 return pb;
34 }
35 else if(strcmp(pf‐>name,name)==0)
36 {
37 return pf;
38 }
39 }
40
41 return NULL;
42 }
43
5、双向链表 删除指定节点1-2
1 STU* delete_link(STU *head, int num)
2 {
3 //判断链表是否存在
4 if(head == NULL)
5 {
6 printf("link not found\n");
7 return head;
8 }
9 else
10 {
11 STU *pb = head;//指向头节点
12 STU *pf = head‐>pre;//指向尾节点
13
14 //逐个节点寻找删除点
15 while((pb‐>num != num) && (pf‐>num != num) && (pf != pb))
16 {
17 pb = pb‐>next;//next方向移动
18 pf = pf‐>pre;//pre方向移动
19 if(pb‐>pre == pf)
20 break;
21 }
22
23 if(pb‐>num == num)//删除pb指向的节点
24 {
25
26 if(pb == head)//删除头节点
27 {
28 if(head == head‐>next)//链表只有一个节点
29 {
30 free(pb);
31 head = NULL;
32 }
33 else
34 {
35 head‐>next‐>pre = head‐>pre;
36 head‐>pre‐>next = head‐>next;
37 head = head‐>next;
38 free(pb);
39 }
40
41 }
42 else//删除中尾部节点
43 {
44 pb‐>pre‐>next = pb‐>next;
45 pb‐>next‐>pre = pb‐>pre;
46 free(pb);
47 }
48 }
49 else if(pf‐>num == num)//删除pf指向的节点
50 {
51
52 if(pf == head)//删除头节点
53 {
54 if(head == head‐>next)//链表只有一个节点
55 {
56 free(pf);
57 head = NULL;
58 }
59 else
60 {
61 head‐>next‐>pre = head‐>pre;
62 head‐>pre‐>next = head‐>next;
63 head = head‐>next;
64 free(pf);
65 }
66 }
67 else//删除中尾部节点
68 {
69 pf‐>pre‐>next = pf‐>next;
70 pf‐>next‐>pre = pf‐>pre;
71 free(pf);
72 }
73 }
74 else
75 {
76 printf("未找到%d相关的节点信息",num);
77 }
78 }
79
80 return head;
81 }
6、释放这个链表节点(了解)
1 STU* free_link(STU *head)
2 {
3 if(head == NULL)//链表为空
4 {
5 printf("link not found\n");
6 return NULL;
7 }
8 else//链表存在
9 {
10 STU *pb = head;
11 STU *tmp;
12 do
13 {
14 tmp = pb;
15 pb = pb‐>next;
16 free(tmp);
17 }while(pb != head);
18 }
19
20 return NULL;
21 }
知识点3【结构的浅拷贝 和深拷贝】(了解)
1、知识点的引入(指针变量 作为 结构体的成员)
1 typedef struct
2 {
3 int num;
4 char *name;//指针变量作为 结构体的成员
5 }DATA;
6 void test01()
7 {
8 DATA data={100,"hehehehaha"};
9 printf("%d\n",sizeof(DATA));//8字节
10
11 printf("num = %d\n",data.num);
12 //指针变量作为结构体的成员 保存的是空间的地址
13 printf("name = %s\n",data.name);
14 }
2、指针变量 作为结构体的成员 操作前 必须有合法的空间
1 void test02()
2 {
3 DATA data;
4 printf("%d\n",sizeof(DATA));
5
6 printf("num = %d\n",data.num);
7 //指针变量 作为结构体的成员 操作前 必须有合法的空间
8 //data.name = "hehe";
9 //给name 事先申请 一块 堆区空间
10 data.name = (char *)calloc(1,10);
11 strcpy(data.name,"hahaha");
12 printf("name = %s\n",data.name);
13
14 //如果name指向堆区空间 一定要记得释放
15 if(data.name != NULL)
16 {
17 free(data.name);
18 data.name = NULL;
19 }
20 }
原理图分析:
3、指针变量 作为结构体的成员 结构体变量间的赋值操作 容易导致“浅
拷贝”发生
1 void test03()
2 {
3 DATA data1;
4 DATA data2;
5
6 data1.num = 100;
7 data1.name = (char *)calloc(1,10);
8 strcpy(data1.name,"my data");
9 printf("data1:num = %d, name = %s\n",data1.num, data1.name);
10
11 //指针变量 作为结构体的成员 结构体变量间的赋值操作 容易导致“浅拷贝”发生
12 data2 = data1;//“浅拷贝”
13 printf("data2: num = %d, name = %s\n",data2.num, data2.name);
14
15 if(data1.name != NULL)
16 {
17 free(data1.name);
18 data1.name = NULL;
19 }
20
21 if(data2.name != NULL)
22 {
23 free(data2.name);
24 data2.name = NULL;
25 }
26 }
运行结果 出现段错误(共48张PPT)
C语言类型及语句
C语言关键字数据类型运算符控制语句C语言关键字C的关键字共有32个数据类型关键字(12个)char,short,int,long,float,double,unsigned,signed,struct,union,enum,void2.控制语句关键字(12个)if,else,switch,case,defaultfor,do,while,break,continue,goto,returnC语言关键字C的关键字共有32个3.存储类关键字(5个)auto, extern, register, static,const4.其他关键字(3个)sizeof,typedef,volatile数据类型数据类型构造类型指针类型 (char *、int *、int **等)枚举类型enum数组类型结构类型struct联合类型union基本类型整型int、short、long字符型char实型(浮点型)单精度实型float双精度实型double数据类型数据类型常量与变量1.常量:在程序运行过程中,其值不能被改变的量整型100,125,-100,0实型3.14,0.125,-3.789字符型 ‘a’,‘b’,‘2’字符串 “a”,“ab”,“1232”数据类型常量与变量1.常量:使用场合:一般出现在表达式或赋值语句中例:a = 100 + b; //100整形常量c = 12.5; //实型常量数据类型变量在程序运行过程中,其值可以改变1.命名规则变量名只能由字母、数字、下划线组成第一个字符必须为字母或下划线;建议:小写英文单词+"_"线构成数据类型特点:变量在编译时为其分配相应的内存地址可以通过名字和地址访问相应空间数据类型整型数据整形常量(按进制分)十进制:以正常数字1-9开头,如457 789八进制:以数字0开头,如0123十六进制:以0x开头,如0x1e数据类型整形数据2.整形变量有/无符号短整型(un/signed) short(int) 2个字节有/无符号基本整型(un/signed) int4个字节有/无符号长整型(un/signed) long (int)4个字节(32位处理器)数据类型实型数据(浮点型)1.实型常量实型常量也称为实数或者浮点数十进制形式:由数字和小数点组成:0.0、0.12、5.0指数形式:123e3代表123*10的三次方不以f结尾的常量是double类型以f结尾的常量(如3.14f)是float类型数据类型实型数据(浮点型)2.实型变量单精度(float)和双精度(double)float型:占4字节,7位有效数字,指数-37到38double型:占8字节,16位有效数字,指数-307到308数据类型数据类型字符常量:直接常量:用单引号括起来,如:'a'、'b'等.转义字符:以反斜杠“\”开头,后跟一个或几个字符、如'\n','\t'等,分别代表换行、横向跳格.数据类型字符变量:用char定义,每个字符变量被分配一个字节的内存空间字符值以ASCII码的形式存放在变量的内存单元中;注:  a = 'x';  a变量中存放的是字符'x'的ASCII :120  即a=120跟a='x'在本质上是一致的数据类型字符串常量是由双引号括起来的字符序列,如“CHINA”、“C program”,“$12.5”等都是合法的字符串常量.字符串常量与字符常量的不同’a’为字符常量,“a”为字符串常量每个字符串的结尾,编译器会自动的添加一个结束标志位'\0',即“a”包含两个 字符‘a’和’\0’数据类型02.data_type.c数据类型格式化输出字符:%d 十进制有符号整数 %u 十进制无符号整数%x, 以十六进制表示的整数 %0 以八进制表示的整数%f float型浮点数 %lf double型浮点数%e 指数形式的浮点数 %s 字符串%c 单个字符 %p 指针的值特殊应用:%3d %03d %-3d %5.2f数据类型typedef 类型重定义给一个已有的类型重新起个名字当现有类型比较复杂时,可以用typedef起个别名例如: unsigned short int a = 125;可以写为:typedef unsigned short int U16;U16 a=125;数据类型步骤:1.先用想要起别名的类型定义一个变量2.把变量名改为我们想要起的别名(有实际意义)3.在表达式最前面加一个typedef,最后面加分号4.用新定义的类型名进行变量定义运算符数据的混合运算数据有不同的类型,不同类型数据之间进行混合运算时必然涉及到类型的转换问题.转换的方法有两种:自动转换:遵循一定的规则,由编译系统自动完成.强制类型转换:把表达式的运算结果强制转换成所需的数据类型.运算符数据的混合运算自动转换的原则:占用内存字节数少(值域小)的类型,向占用内存字节数多(值域大)的类型转换,以保证精度不降低.转换方向:运算符03.um为整型,与2相除仍为整型,与2.0相除结果为double型运算符强制转换:通过类型转换运算来实现(类型说明符) (表达式)功能:把表达式的运算结果强制转换成类型说明符所表示的类型例如:(float)a; //把a的值转换为实型(int)(x+y); //把x+y的结果值转换为整型注意:类型说明符必须加括号运算符04.data_operation_force.c运算符无论是强制转换或是自动转换,都只是为了本次运算的需要,而对变量的数据长度进行的临时性转换,而不改变数据定义的类型.运算符运算符用算术运算符将运算对象(也称操作数)连接起来的、符合C语法规则的式子,称为C算术表达式运算对象包括常量、变量、函数等例如:a* b / c-1.5+'a'运算符C语言常用运算符算术运算符 (+、-、*、/、%)关系运算符 (>、<、==、>=、<=、!=)逻辑运算符 (!、&&、||)位运算符 (<<、>>、&、|、~、∧)赋值运算符 (=及其扩展赋值运算符)条件运算符 (?:)逗号运算符(,)运算符C语言常用运算符指针运算符 (*和&)求字节数运算符(sizeof(类型或变量))强制类型转换运算符((变量或常量))分量运算符(.->)下标运算符([])其他( 如函数调用运算符())运算符复合赋值运算符在赋值符“=”之前加上其它二目运算符可构成复合赋值符:+=,- =,*=,%=,<<=,>>=,&=,^=a += 5 //等价于a = a+5;x *= y+7 //等价于x = x*(y+7)运算符自增、自减运算符作用是使变量的值增1或减1++i,--i(先加/减,后使用)i++,i--(先使用,后加/减)例如:i = 3;j=++i;   i的值先变成4,再赋给j;此时i,j的值均为4.j=i++;先将i的值3赋给j,j的值为3,然后i变为4运算符位运算位运算是指按二进制位进行的运算C语言不直接支持位运算,必须借助于位运算完成例如:将一个存储单元中的各二进制位左移或右移一位将一个存储单元的指定位清零或置位(写1)C语言提供的位运算符:运算符 含义 运算符 含义& 按位与 ~ 取反|按位或<<左移^ 按位异或>>右移运算符参加运算的两个数据,按二进制位展开,然后进行相应的运算,如:“与、或、异或、非”等&常用来将指定位清零a = a & 0xf0;|常用来将指定位置1a = a | 0xf0;^判断两个位值,不同为1,相同为0,常用来使特定位翻转等a = a ^ 0xf0;~按位取反,即将0变1或1变0,一般配合其它位运算符使用,以达到屏蔽某些关键位a = ~ a;运算符移位运算左移<<将一个数的二进制位左移,高位丢弃,低位补0例:a =a<<2将a的二进制数左移2位,右补0若a=15,即二进制数0 0 0 01111左移2位得0 011110 0,(十进制数60)运算符05.bit.c运算符运算符的优先级:C语言中,运算符的运算优先级共分为15级.1级最高,15级最低.优先级较高的先于优先级较低的进行运算.在一个运算量两侧的运算符优先级相同时,则按运算符的结合性所规定的结合方向处理.各运算符的结合性:左结合性(从左至右):+ - * /右结合性(从右至左): = ++ --运算符在判断同优先级运算符计算顺序时,要注意结合性,详细的优先级及结合性请参考以下表格:运算符实际使用时,我们无需过多考虑优先级的问题在我们优先运算的表达式表达式之间加入()即可例:e = a + b - c * d; //先算乘后加、减e =(a +(b - c))* d; //先减,再加,最后乘运算符C语言支持最基本的三种程序运行结构顺序结构、选择结构、循环结构顺序结构:程序按顺序执行,不发生跳转.选择结构:依据是否满足条件,有选择的执行相应功能.循环结构:依据条件是否满足,循环多次执行某段代码.运算符选择结构:依据是否满足条件,有选择的执行相应功能.if(表达式)语句;if(表达式)语句1 else语句2;if(表达式1)语句1;elseif(表达式2)语句2;elseif(表达式3)语句3;…else语句n;条件运算符 (a>b)?a:b;说明:条件为真,表达式取值a,否则取值b运算符switch(表达式){case常量表达式1:语句1;break;case常量表达式2:语句2;break;default:语句3;break;}注意:break的使用练习1:从屏幕上输入一个学生的成绩(0-100)对学生成绩进行评定:<=60为"E"60~69为"D"70~79为"C"80~89为"B"90以上为"A"<0或>100提示成绩输入出错使用:if else if等实现运算符练习2:从键盘输入1~7的数字,分别提示Monday、Tuesday、Wednesday、Thursday、friday、saturday、sunday输入其它,提示出错使用:switch case break 实现运算符循环结构for(; ;){ }while语句 先判断表达式后执行.do-while语句 先执行语句后判断表达式.goto语句 实现程序跳转(尽量少使用)break语句和continue语句的区别break语句用于跳出本层循环.continue用于结束本次循环.运算符练习3:输出100以内能被7整除的数分别用for循环和while循环完成运算符练习4:要求练习2可以循环输入当输入0时退出循环:break输入0-7以外的数字提示重新输入:continue(共32张PPT)
Linux编程工具(make)
make概述makefile语法及其执行makefile变量124Make概述GNU make是一种代码维护工具make工具会根据makefile文件定义的规则和步骤,完成整个软件项目的代码维护工作。Make概述一般用来简化编译工作,可以极大地提高软件开发的效率。windows下一般由集成开发环境自动生成linux下需要由我们按照其语法自己编写Make概述make主要解决两个问题:大量代码的关系维护减少重复编译时间Make概述大量代码的关系维护大项目中源代码比较多,手工维护、编译时间长而且编译命令复杂,难以记忆及维护把代码维护命令及编译命令写在makefile文件中,然后再用make工具解析此文件自动执行相应命令,可实现代码的合理编译减少重复编译时间在改动其中一个文件的时候,能判断哪些文件被修改过,可以只对该文件进行重新编译,然后重新链接所有的目标文件,节省编译时间Makefile语法及其执行目标:依赖文件列表<Tab>命令列表Makefile语法及其执行2.依赖文件:是用来输入从而产生目标的文件一个目标通常有几个依赖文件(可以没有)Makefile语法及其执行目标:通常是要产生的文件名称,目标可以是可执行文件或其它obj文件,也可是一个动作的名称Makefile语法及其执行3.命令:make执行的动作,一个规则可以含几个命令(可以没有)有多个命令时,每个命令占一行Makefile语法及其执行例1:简单的Makefile实例Makefile语法及其执行make命令格式make [ -f file ] [ targets ][ -f file ]:make默认在工作目录中寻找名为GNUmakefile、makefile、Makefile的文件作为makefile输入文件,-f可以指定以上名字以外的文件作为makefile输入文件Makefile语法及其执行2. [ targets ]:若使用make命令时没有指定目标,则make工具默认会实现makefile文件内的第一个目标,然后退出指定了make工具要实现的目标,目标可以是一个或多个(多个目标间用空格隔开)。Makefile语法及其执行例2:稍复杂的Makefile实例main.c调用printf1.c中的printf1函数,同时需要使用main.h的中PI,printf1.h需要使用main.h中的PIMakefile语法及其执行printf1.hmain.cprintf1.cmain.hMakefile语法及其执行Makefile语法及其执行如printf1.c和printf1.h文件在最后一次编译到printf1.o目标文件后没有改动,它们不需重新编 main可以从源文件中重新编译并链接到没有改变的printf1.o目标文件。如printf1.c和printf1.h源文件有改动,make将在重新编译main之前自动重新编译printf1.o。假想目标:前面makefile中出现的文件称之为假想目标假想目标并不是一个真正的文件名,通常是一个目标集合或者动作可以没有依赖或者命令一般需要显示的使用make + 名字 显示调用all:exec1 exec2clean:<Tab>rm *.o execMakefile语法及其执行Makefile变量makefile变量类似于C语言中的宏,当makefile被make工具解析时,其中的变量会被展开。变量的作用:保存文件名列表保存文件目录列表Makefile变量保存编译器名保存编译参数保存编译的输出...Makefile变量makefile的变量分类:自定义变量在makefile文件中定义的变量。make工具传给makefile的变量。Makefile变量makefile的变量分类:系统环境变量make工具解析makefile前,读取系统环境变量并设置为makefile的变量。2.预定义变量(自动变量)Makefile变量自定义变量语法定义变量:变量名=变量值2.引用变量:$(变量名)或${变量名}3.makefile的变量名:makefile变量名可以以数字开头4.变量是大小写敏感的5.变量一般都在makefile的头部定义6.变量几乎可在makefile的任何地方使用Makefile变量例2_2:修改例2中的makefile,使用自定义变量使其更加通用。Makefile变量make工具传给makefile的变量执行make命令时,make的参数options也可以给makefile设置变#make cc=arm-linux-gccMakefile变量系统环境变量make工具会拷贝系统的环境变量并将其设置为makefile的变量,在makefile中可直接读取或修改拷贝后的变量。#export test=10#make clean#echo $testMakefile变量预定义变量makefile中有许多预定义变量,这些变量具有特殊的含义,可在makefile中直接使用。$@ 目标名Makefile变量预定义变量$< 依赖文件列表中的第一个文件$^依赖文件列表中除去重复文件的部分Makefile变量AR归档维护程序的程序名,默认值为arARFLAGS归档维护程序的选项AS汇编程序的名称,默认值为asASFLAGS汇编程序的选项CCC编译器的名称,默认值为ccCFLAGSC编译器的选项CPPC预编译器的名称,默认值为$(CC) -ECPPFLAGS C预编译的选项CXXC++编译器的名称,默认值为g++CXXFLAGS C++编译器的选项Makefile变量例2_3:修改例2中的makefile,使用预定义变量,使其更加通用。Makefile练习Makefile练习编写Makefile完成计算器程序(01_练习代码/calc)的编译,并通过假想目标(clean)清除目标文件第一步:不使用任何变量完成功能第二步:使用自定义变量让程序更加通用第三步:使用预定义变量让程序更加通用ー?顶`汞 知识点1【文件的格式化操作】0-1
1、fprintf 文件的写操作
1 void test03()
2 {
3 HERO hero[]={
4 {"德玛西亚",80, 60},
5 {"盲僧",90, 80},
6 {"小法",40, 85},
7 {"小炮",50, 90}
8 };
9 int n = sizeof(hero)/sizeof(hero[0]);
10 FILE *fp = NULL;
11 int i=0;
12
13 fp = fopen("hero.txt", "w");
14 if(fp == NULL)
15 {
16 perror("fopen");
17 return;
18 }
19
20 for(i=0;i21 {
22 fprintf(fp,"英雄:%s 防御:%d 攻击:%d\n",hero[i].name,
hero[i].deff,hero[i].atk);
23 }
24
25 fclose(fp);
26 }
运行结果:
2、fscanf格式化 读操作
1 void test04()
2 {
3 HERO hero[4];
4 int i=0;
5 FILE *fp = NULL;
6 fp = fopen("hero.txt","r");
7 if(fp == NULL)
8 {
9 perror("fopen");
10 return;
11 }
12
13 for(i=0;i<4; i++)
14 {
15 fscanf(fp,"英雄:%s 防御:%d 攻击:%d\n", hero[i].name, &hero[i].deff, &her
o[i].atk);
16 }
17
18 for(i=0;i<4; i++)
19 {
20 printf("%s %d %d\n", hero[i].name, hero[i].deff, hero[i].atk);
21 }
22
23 fclose(fp);
24 }
运行结果:
总结:
知识点2【文件的随机读写】
1、知识点引入
1 void test05()
2 {
3 char buf[128]="";
4 FILE *fp = NULL;
5 fp = fopen("a.txt","w+");
6 if(fp == NULL)
7 {
8 perror("fopen");
9 return;
10 }
11
12 //先往文件中写入 一个字符串"hello file"
13 fputs("hello file", fp);
14 //fclose(fp);
15
16 //重新打开文件 让文件的流指针 回到 文件首部
17 //fp = fopen("a.txt","r");
18 //在从文件找那个读取 该字符串
19 fgets(buf,sizeof(buf), fp);
20 printf("buf=%s\n", buf);
21
22 fclose(fp);
23 }
上述代码:为啥出现这个现象()
解决上述文件 : 文件写完后 需要关闭 文件 然后重新打开
文件 让文件流指针 指向文件开始位置 让变 下次的文件读
操作
1 void test05()
2 {
3 char buf[128]="";
4 FILE *fp = NULL;
5 fp = fopen("a.txt","w+");
6 if(fp == NULL)
7 {
8 perror("fopen");
9 return;
10 }
11
12 //先往文件中写入 一个字符串"hello file"
13 fputs("hello file", fp);
14 fclose(fp);
15
16 //重新打开文件 让文件的流指针 回到 文件首部
17 fp = fopen("a.txt","r");
18 //在从文件找那个读取 该字符串
19 fgets(buf,sizeof(buf), fp);
20 printf("buf=%s\n", buf);
21
22 fclose(fp);
23 }
运行结果:
2、复位文件流指针 rewind 0-2
1 void test05()
2 {
3 char buf[128]="";
4 FILE *fp = NULL;
5 fp = fopen("a.txt","w+");
6 if(fp == NULL)
7 {
8 perror("fopen");
9 return;
10 }
11
12 //先往文件中写入 一个字符串"hello file"
13 fputs("hello file", fp);
14
15 //复位文件流指针(位置 指针)
16 rewind(fp);//注意
17
18 //在从文件找那个读取 该字符串
19 fgets(buf,sizeof(buf), fp);
20 printf("buf=%s\n", buf);
21
22 fclose(fp);
23 }
运行结果:
3、获得 文件流指针 距离 文件首部 的字节数 ftell
1 void test05()
2 {
3 char buf[128]="";
4 long file_len = 0;
5 FILE *fp = NULL;
6 fp = fopen("a.txt","w+");
7 if(fp == NULL)
8 {
9 perror("fopen");
10 return;
11 }
12
13 //先往文件中写入 一个字符串"hello file"
14 fputs("hello file", fp);
15
16 //获取 文件流指针 距离 文件首部的 字节数
17 file_len = ftell(fp);
18 printf("file_len = %ld\n", file_len);
19
20 fclose(fp);
21 }
运行结果:
4、定位 文件的流指针 fseek
位移量:-10 往左边移动10字节 +10往右边移动10字节
一次性读取文件总大小
1 oid test06()
2 {
3 char *file_data=NULL;
4 long file_len = 0;
5 FILE *fp = NULL;
6 fp = fopen("a.txt","r");
7 if(fp == NULL)
8 {
9 perror("fopen");
10 return;
11 }
12
13 //需求:一次性的将文件数据 读取到 内存中
14 //1、得到文件的总大小
15 //a、使用fseek 将文件指针 定位到文件尾部
16 fseek(fp, 0, 2);
17 //b、使用ftell计算文件的偏移量 == 文件的总大小
18 file_len = ftell(fp);
19 //c、使用rewind复位文件流指针
20 rewind(fp);
21
22 //2、根据文件的总大小 合理申请 内存空间
23 file_data = (char *)calloc(1,file_len+1);//+1的目的 内存末尾存放'\0'
24 if(file_data == NULL)
25 {
26 fclose(fp);
27 return;
28 }
29
30 //3、一次性 将文件数据 读入到 内存空间
31 fread(file_data, file_len, 1, fp);
32
33 //4、遍历读取到的文件内容
34 printf("file_len=%ld\n", file_len);
35 printf("%s\n", file_data);
36
37 //5、释放堆区空间
38 if(file_data != NULL)
39 {
40 free(file_data);
41 file_data = NULL;
42 }
43
44 //关闭文件
45 fclose(fp);
46 }
运行结果:
5、feof()函数 判断文件是否到达文件末尾
EOF宏 只能用于 文本文 文件
feof函数 可以用于 文本文文件 二进制文件
1 void test07()
2 {
3 FILE *fp = NULL;
4 fp = fopen("a.txt","r");
5 if(fp == NULL)
6 {
7 perror("fopen");
8 return;
9 }
10
11 //feof(fp) 判断文件是否 结束 0:未结束 非0:表示结束
12 //while( feof(fp) == 0)//文件未结束 才循环
13 while( !feof(fp) )
14 {
15 char ch = fgetc(fp);
16 printf("%c", ch);
17 }
18
19 fclose(fp);
20 }
运行结果;
知识点3【文件加密器】
1、原理的分析
加密过程:
解密过程:
main.c
1 #include
2 #include"fun.h"
3 int main(int argc,char *argv[])
4 {
5 while(1)
6 {
7 int cmd = 0;
8 print_help();
9
10 scanf("%d", &cmd);
11
12 if(cmd == 1)
13 {
14 char src_file[31]="";
15 char dst_file[31]="";
16 char *file_data = NULL;
17 unsigned long file_length = 0;
18 unsigned int passwd = 0;
19
20 //1、获取源文件 目的文件名(参考API实现)
21 get_file_name(dst_file, src_file);
22
23 //2、获取源文件名 对应的文件 内容(参考API实现)
24 //char * read_src_file(unsigned long *file_length,char *src_file_name)
25 file_data = read_src_file(&file_length, src_file);
26
27 //3、获取用户输入的密码(自定义实现)
28 printf("请输入你的密码:");
29 scanf("%u", &passwd);
30
31 //4、对文件内容加密(参考API实现)
32 file_data = file_text_encrypt(file_data,file_length,passwd);
33 //char * file_text_encrypt(char * src_file_text,unsigned long int lengt
h,unsigned int password)
34
35 //5、对加密好的 文件内容 保存到 目的文件名中
36 save_file(file_data, file_length,dst_file);
37 //void save_file(char* text,unsigned long int length,char * file_name)
38 }
39 else if(cmd == 2)
40 {
41 char src_file[31]="";
42 char dst_file[31]="";
43 char *file_data = NULL;
44 unsigned long file_length = 0;
45 unsigned int passwd = 0;
46
47 //1、获取源文件 目的文件名(参考API实现)
48 get_file_name(dst_file, src_file);
49 //void get_file_name(char * dest_file_name,char * src_file_name)
50
51 //2、获取源文件名 对应的文件 内容(参考API实现)
52 file_data = read_src_file(&file_length, src_file);
53 //char * read_src_file(unsigned long int *file_length,char *src_file_na
me)
54
55 //3、获取用户输入的密码(自定义实现)
56 printf("请输入你的密码:");
57 scanf("%u", &passwd);
58
59 //4、对文件内容解密(参考API实现)
60 file_data = file_text_decrypt(file_data, file_length,passwd);
61 //char * file_text_decrypt(char * src_file_text,unsigned long int lengt
h,unsigned int password)
62
63 //5、对加密好的 文件内容 保存到 目的文件名中
64 save_file(file_data, file_length,dst_file);
65 //void save_file(char* text,unsigned long int length,char * file_name)
66 }
67 else if(cmd == 3)
68 {
69 break;
70 }
71 else
72 {
73 printf("请输入一个正确的选项\n");
74 }
75
76 }
77
78 return 0;
79 }
fun.c
1 #include
2 #include
3 void print_help(void)
4 {
5 printf("**********1:加密文件**************\n");
6 printf("**********2:解密文件**************\n");
7 printf("**********3:退出程序**************\n");
8 }
9 void get_file_name(char * dest_file_name,char * src_file_name)
10 {
11 printf("请输入你的源文件名称(30个字符):");
12 scanf("%s", src_file_name);
13 printf("请输入你的目的文件名称(30个字符):");
14 scanf("%s", dest_file_name);
15 return;
16 }
17
18 char * read_src_file(unsigned long *file_length,char *src_file_name)
19 {
20 char *data = NULL;
21 FILE *fp = NULL;
22 fp = fopen(src_file_name,"r");
23 if(fp == NULL)
24 {
25 perror("fopen");
26 return NULL;
27 }
28
29 //获取文件总长度
30 //将文件流指针 定位 到文件 尾部
31 fseek(fp,0,2);
32 //获取文件长度
33 *file_length = ftell(fp);
34 //复位文件流指针
35 rewind(fp);
36
37
38 //根据 文件的长度 申请 堆区空间
39 data = (char *)calloc(1,*file_length);
40 if(data == NULL)
41 {
42 perror("calloc");
43 return NULL;
44 }
45
46 //一次性 读取文件内容
47 fread(data, *file_length,1,fp);
48
49 fclose(fp);
50 //将空间首地址 返回
51 return data;
52 }
53 char * file_text_encrypt(char * src_file_text,unsigned long int length,u
nsigned int password)
54 {
55 int i=0;
56 for(i=0;i57 {
58 src_file_text[i] += password;//加密的过程
59 }
60
61 return src_file_text;
62 }
63
64 void save_file(char* text,unsigned long int length,char * file_name)
65 {
66 FILE *fp = NULL;
67 fp = fopen(file_name, "w");
68 if(fp == NULL)
69 {
70 perror("fopen");
71 return;
72 }
73
74 //将data数据保存到 文件中
75 fwrite(text, length, 1, fp);
76
77 fclose(fp);
78
79 //释放text指向堆区空间
80 if(text != NULL)
81 {
82 free(text);
83 text =NULL;
84 }
85
86 printf("保存成功!\n");
87 return;
88 }
89
90 char * file_text_decrypt(char * src_file_text,unsigned long int length,u
nsigned int password)
91 {
92 int i=0;
93 for(i=0;i94 {
95 src_file_text[i] ‐= password;//解密的过程
96 }
97
98 return src_file_text;
99 }
fun.h
1 #ifndef __FUN_H__
2 #define __FUN_H__
3
4 extern void print_help(void);
5 extern void get_file_name(char * dest_file_name,char * src_file_name);
6 extern char * read_src_file(unsigned long *file_length,char *src_file_nam
e);
7 extern char * file_text_encrypt(char * src_file_text,unsigned long int le
ngth,unsigned int password);
8
9 extern void save_file(char* text,unsigned long int length,char * file_nam
e);
10 #endif
项目提示:
1:打开文件的时候用二进制方式打开进行读写。
2:测文件大小方法:
1)用fseek() 定位流指针到文件的末尾。
2)用ftell()函数测流指针的位置即文件的大小。
3:读文件内容:
1)根据文件的大小用malloc申请内存空间保存读出的内容
2)读文件数据的时候要从文件的开始读(rewind())。
详细设计API设计参考:
1) 从键盘获取源文件和目的文件名字
/**************************************************************************/
//函数功能:获取 目的文件和源文件的名字
//参数: src_file_name:源文件名字字符数组首地址。
// dest_file_name:目的文件的名字字符数组首地址
/**************************************************************************/
void get_file_name(char * dest_file_name,char * src_file_name)

2) 从文件中读出内容
/**************************************************************************/
//函数功能:读出文件内容
//参数:file_length:整型指针,此地址中保存文件字节数。
// src_file_name:文件名字,从此文件中读取内容。
// 返回值:读出字符串的首地址
// 在此函数中测文件的大小,并malloc空间,再把文件内容读出返回,读出字符数组的首
地址
/**************************************************************************/
char * read_src_file(unsigned long int *file_length,char *src_file_name)

3) 字符数组加密
/**************************************************************************/
//函数功能:加密字符串
//参数:
// src_file_text:要加密的字符串。 length:字符串的长度
// password: 加密密码
// 返回值: 加密后的字符串的首地址
// 加密原理字符数组中每个元素加上password
/**************************************************************************/
char * file_text_encrypt(char * src_file_text,unsigned long int length,unsigned
int password)

4) 解密字符串
/**************************************************************************/
//函数功能:解密字符串
//参数:
// src_file_text:要解密的字符串。 length:字符串的长度
// password: 解密密码
// 返回值: 解密后的字符串的首地址
//思想 把数组中的每个元素减去password 给自己赋值。
/**************************************************************************/
char * file_text_decrypt(char * src_file_text,unsigned long int length,unsigned
int password)
5) 保存文件
/**************************************************************************/
//函数功能:将字符串保存到目的文件中
//参数:
// text:要保存的字符串首地址
// file_name :目的文件的名字
// length:字符串的长度
//思想:传入字符数组的首地址和数组的大小及保存后的文件的名字,即可保存数组到文件

/**************************************************************************/
void save_file(char* text,unsigned long int length,char * file_name)

6) 打印文件信息
/**************************************************************************/
//
// 函数功能:打印帮助信息
//
//
/**************************************************************************/
void print_help()
{
printf("********1:加密文件***********\n")
printf("********2:解密文件***********\n")
printf("********3:退出程序***********\n")
}窗前明月光
疑似地上霜
举头望明月
低头思故乡(共64张PPT)
文件操作
文件的基本概念C语言对文件处理文件的基本练习123文件的基本概念凡是使用过计算机的人都不会对“文件”感到陌生,如下:文件用来存放程序、文档、音频、视频数据、图片等程序员,必须掌握编程实现创建、写入、读取文件等操作文件的基本概念磁盘文件:(我们通常认识的文件)指一组相关数据的有序集合,通常存储在外部介质(如磁盘)上,使用时才调入内存文件的基本概念设备文件:(与硬件相关的一类特殊文件)在操作系统中把每一个与主机相连的输入、输出设备看作是一个文件,把它们的输入、输出等同于对磁盘文件的读和写文件的基本概念下面看几个设备文件:键盘:标准输入文件屏幕:标准输出文件其它设备:打印机、触摸屏、摄像头、音箱等接下来主要来看磁盘文件文件的基本概念磁盘文件的存取过程文件程序数据区文件缓冲区(系统\ 程序)文件缓冲区(系统\ 程序)内存磁盘输出输入文件的基本概念
磁盘文件的存取特点
磁盘文件,一般保存在硬盘、光盘、U盘等掉电不丢失的磁盘设备中,在需要时调入内存
在内存中对文件进行编辑处理后,保存到磁盘中
程序与磁盘之间交互,不是立即完成,系统或程序可根据需要设置缓冲区,以提高存取效率
文件的基本概念磁盘文件的分类一个文件通常是磁盘上一段命名的存储区计算机的存储在物理上是二进制的,所以物理上所有的磁盘文件本质上都是以字节为单位进行顺序存储从用户或者操作系统使用的角度(逻辑上)可以把文件分为:1、文本文件:基于字符编码的文件2、二进制文件:基于值编码的文件文件的基本概念文本文件:基于字符编码,常见编码有ASCII、UNICODE等,一般可以使用文本编辑器直接打开例如:数5678的以ASCII存储形式为:ASCII码:00110101 00110110 00110111 00111000歌词文件(lrc):文本文件文件的基本概念二进制文件:基于值编码,自己根据具体应用,指定某个值是什么意思把内存中的数据按其在内存中的存储形式原样输出到磁盘上一般需要自己判断或使用特定软件分析数据格式例如:5678的存储形式为:二进制码:00010110 00101110音频文件(mp3):二进制文件文件的基本概念文本文件、二进制文件对比:译码:文本文件编码基于字符定长,译码容易些;二进制文件编码是变长的,译码难一些(不同的二进制文件格式,有不同的译码方式)。空间利用率:二进制文件用一个比特来代表一个意思(位操作);而文本文件任何一个符号至少需要一个字节。可读性:文本文件用通用的记事本工具就几乎可以浏览所有文本文件二进制文件需要一个具体的文件解码器,比如读BMP文件,必须用读图软件。文件的打开与关闭C语言中不能直接操作文件只能采用库函数间接对文件进行操作C语言操作文件的基本流程为在使用文件前要调用打开函数将文件打开打开文件会得到一个文件指针fp调用各种有关函数,利用fp对文件进行具体处理(读或写)在文件用完时,及时调用关闭函数来关闭文件C语言中所有的文件操作都围绕文件指针完成文件的打开与关闭定义文件指针的一般形式为:FILE *指针变量标识符FILE为大写,需要包含<stdio.h>FILE是系统使用typedef定义出来的有关文件信息的一种结构体类型FILE结构体中含有文件名、文件状态和文件当前位置等信息一般情况下,我们操作文件前必须定义一个文件指针指向我们将要操作的文件实际编程中使用库函数操作文件,无需关心FILE结构体的细节文件的打开与关闭FILE在stdio.h文件中的文件类型声明:typedef struct{short level;//缓冲区“满”或“空”的程度unsigned flags;//文件状态标志char fd;//文件描述符unsigned charhold;//如无缓冲区不读取字符short bsize;//缓冲区的大小unsigned char *buffer;//数据缓冲区的位置unsigned ar*curp;//指针,当前的指向unsigned istemp;//临时文件,指示器shorttoken;//用于有效性检查}FILE;文件的打开与关闭在缓冲文件系统中,每个被使用的文件都要在内存中开辟一块FILE类型的区域,存放与操作文件相关的信息文件的打开与关闭c语言中有三个特殊的文件指针无需定义、打开可直接使用:stdin:标准输入 默认为当前终端(键盘)我们使用的scanf、getchar函数默认从此终端获得数据stdout:标准输出 默认为当前终端(屏幕)我们使用的printf、puts函数默认输出信息到此终端stderr:标准出错 默认为当前终端(屏幕)当我们程序出错或者使用:perror函数时信息打印在此终端文件的打开与关闭任何文件使用之前必须打开,使用后必须关闭打开文件FILE * fp = NULL;fp = fopen(文件名,文件使用方式);文件的打开与关闭fp = fopen(文件名,文件使用方式);①文件名:要操作的文件的名字、可以包含路径信息②文件使用方式:"读"、"写"、"文本"或"二进制"等③fp文件指针:指向被打开的文件,失败返回空,成功返回相应指针文件的打开与关闭例:FILE * fp_passwd= NULL;fp_passwd= fopen(“passwd.txt”,“rt”);if(fp_passwd==NULL)printf("file open error");文件的打开与关闭说明:以只读方式打开当前目录下一个叫passwd.txt的文件返回一个文件指针赋给fp_passwd判断打开是否成功,如果失败,返回NULL(重要)文件的打开与关闭第一个参数的几种形式相对路径:fp_passwd = fopen(“passwd.txt”,“r”);//同例子打开当前目录passdw文件:源文件(源程序)所在目录文件的打开与关闭fp_passwd = fopen(“./test/passwd.txt”,“r”);打开当前目录(test)下passwd.txt文件fp_passwd= fopen(“../passwd.txt”,“r”);打开当前目录上一级目录(相对当前目录)passwd.txt文件文件的打开与关闭绝对路径:fp_passwd= fopen(“c://test//passwd.txt”,“r”);打开C盘test目录下一个叫passwd.txt文件文件的打开与关闭第二个参数的几种形式(打开文件的方式)读写权限:r w a +r:以只读方式打开文件1、文件不存在返回NULL;2、文件存在返回文件指针,进行后续的读操作文件的打开与关闭w:以只写方式打开文件文件不存在,以指定文件名创建此文件;若文件存在,清空文件内容,进行写操作;如果文件打不开(比如文件只读),返回NULLa:以追加方式打开文件文件不存在,以指定文件名创建此文件(同w)若文件存在,从文件的结尾处进行写操作+:同时以读写打开指定文件文件的打开与关闭第二个参数的几种形式(打开文件的方式)打开方式:b t(可以省略)指打开方式,与文件的存储方式无关,与操作系统有关文件的打开与关闭Windows平台下以“文本”方式打开文件当读取文件的时候,系统会将所有的"\r\n"转换成"\n"当写入文件的时候,系统会将"\n"转换成"\r\n"写入2.以"二进制"方式打开文件,则读\写都不会进行这样的转换文件的打开与关闭在Unix/Linux平台下“文本”与“二进制”模式没有区别。\r\n作为两个字符原样输入输出文件的打开与关闭模 式功 能r或rb以只读方式打开一个文本文件(不创建文件)w或wb以写方式打开文件(使文件长度截断为0字节,创建一个文件)a或ab以添加方式打开文件,即在末尾添加内容,当文件不存在时,创建文件用于写r+或rb+以可读、可写的方式打开文件(不创建新文件)w+或wb+以可读、可写的方式打开文件(使文件长度为0字节,创建一个文件)a+或ab+以添加方式打开文件,打开文件并在末尾更改文件(如果文件不存在,则创建文件)文件的打开与关闭关闭文件调用的一般形式是:fclose (文件指针);文件指针:指向要关闭的文件返回值:关闭文件成功,返回值为0.关闭文件失败,返回值非零.文件的打开与关闭例如:FILE * fp = NULL;fp = fopen(“passwd.txt”,“r”);fclose(fp);文件的打开与关闭练习:1.以只读、文本方式打开当前路径下一个叫test.txt文件2.以只写、二进制方式打开同一个文件3.以追加(a)、文本的方式打开同一个文件文件的打开与关闭4.同时以读/写、二进制方式打开同一个文件,要求若文件不存在,提示出错5.同时以读/些、文本方式打开同一个文件,要求若文件不存在,创建此文件6.若打开成功则关闭相应的文件文件的顺序读写对文件操作最常用的是:“读”和“写”C语言提供了多种对文件读写的函数:字节读写函数:fgetc和fputc字符串读写函数:fgets和fputs数据块读写函数:fread和fwrite格式化读写函数:fscanf和fprintf以上函数可完成对文件内容的顺序读写文件的顺序读写字节的读写ch=fgetc(fp);//读一个字节说明:从指定文件读一个字节赋给ch(以“读”或“读写”方式打开)文本文件: 读到文件结尾返回EOF二进制文件:读到文件结尾,使用feof(后面会讲)判断结尾文件的顺序读写fputc(ch,fp);//写一个字符说明:把一个ch变量中的值(1个字节)写到指定的文件如果输出成功,则返回输出的字节;如果输出失败,则返回一个EOF。EOF是在stdio.h文件中定义的符号常量,值为-1文件的顺序读写例子:从一个指定文件(文本文件)中读取所有信息打印在屏幕上01.fgetc.c文件的顺序读写练习从一个文件(文本文件)中读取所有信息,写入另一个文件中参考:r+ w+ fgetc fputc EOF文件的顺序读写字符串的读写fgets(str,n,fp); //读一个字符串说明:从fp指向的文件读入n-1个字符,在读入n-1个字符之前遇到换行符或EOF,读入提前结束,并读取换行符,在最后加一个’\0’, str为存放数据的首地址文件的顺序读写字符串的读写fputs(“china”,fp); //写一个字符串说明:向指定的文件写一个字符串第一个参数可以是字符串常量、字符数组名或字符指针字符串末尾的’\0’不会写到文件中文件的顺序读写例子:从一个文件中读取一个字符串,输出到另一个文件中从标准输入读入一个字符串往标准输出写入一个字符串02.fgets_fputs.c文件的顺序读写数据块的读写fread(buffer,size,count,fp);fwrite(buffer,size,count,fp);文件的顺序读写说明:参数:buffer:指向存储数据空间的首地址的指针size: 一次读写的数据块大小count: 要读写的数据块个数fp: 指向要进行写操作的文件指针返回值:实际读写的数据块数(不是总数据大小,重要)文件的顺序读写例:从键盘输入一个结构体数组数据,输出到文件,再读入并显示03.fread_fwrite.c文件的顺序读写格式化的读写函数调用:fprintf (文件指针,格式字符串,输出表列);fscanf (文件指针,格式字符串,输入表列);文件的顺序读写函数功能:从磁盘文件中格式化读入或输出字符文件的顺序读写例:从键盘读入一组数据,写入文件再读出04.fprintf_fscanf.c文件的顺序读写注意:用fprintf和fscanf函数对磁盘文件读写使用方便,容易理解,但在输入时要将ASCII码转换为二进制形式,在输出时将二进制形式转换成字符,花费时间较多在内存与磁盘频繁交换数据的情况下,最好不用fprintf和fscanf函数,而用fread和fwrite函数文件的随机读写前面介绍的对文件的读写方式都是顺序读写,即读写文件只能从头开始,顺序读写各个数据但在实际问题中常要求只读写文件中某一指定的部分例如:读取文件第200字节处的30个字节为了解决这个问题可以移动文件内部的位置指针到需要读写的位置,再进行读写,这种读写称为随机读写实现随机读写的关键是要按要求移动位置指针,这称为文件的定位.完成文件定位的函数有:rewind、ftell、fseek函数文件定位与随机读写rewind函数void rewind(文件指针);函数功能:把文件内部的位置指针移到文件首调用形式:rewind(文件指针);例如:fwrite(pa,sizeof(struct stu),2,fp );rewind(fp);fread( pb,sizeof(struct stu),2,fp);前面我们已经多次用到文件定位与随机读写ftell函数定义函数:long ftell(文件指针);函数功能:取得文件流目前的读写位置.返回值:返回当前位置(距离文件起始的字节数),出错 时返回-1.例如:int length;length = ftell(fp);文件定位与随机读写fseek函数(一般用于二进制文件)定义函数:int fseek(文件类型指针,位移量,起始点);函数功能:移动文件流的读写位置.说明:起始位置文件开头SEEK_SET 0文件当前位置SEEK_CUR 1文件末尾SEEK_END 2位移量:以起始点为基点,向前、后移动的字节数.文件定位与随机读写fseek函数应用举例fseek( fp,100,SEEK_SET);将位置指针从文件头向前移100个字节处fseek( fp,50,SEEK_CUR);将位置指针从当前位置 向前移动50个字节处fseek( fp,-50,SEEK_CUR);将位置指针从当前位置 向回移动50个字节处fseek( fp,-50,SEEK_END);将位置指针从文件尾 退回50个字节处.文件定位与随机读写例:往文件中写入两个结构体, 读取第二个结构体05.fseek.c文件定位与随机读写练习:将一个未知大小的文件(文本文件)全部读入内存,并显示在屏幕上参考:fseek ftell rewind fread malloc文件检测函数
文件结束检测函数feof
调用格式: feof(文件指针);
功能:
判断文件是否处于文件结束位置
常配合fgetc、fgets、fread等读函数判断是否到文件结束
返回值: 文件未结束返回0,文件已结束返回非0
文件检测函数06.feof.c文件检测函数读写文件出错检测函数ferror调用格式:ferror(文件指针);功能:检查文件在用各种输入输出函数进行读写时是否出错比如:以只读方式打开一个文件,调用写函数就会发生错误,只要出现错误标志,就一直保留,直到对同一文件调用clearerr函数或rewind函数,或任何其他一个输入输出函数。返回值:为0表示未出错,否则表示有错文件检测函数07.ferror.c文件检测函数文件出错标志和文件结束标志置0函数clearerr调用格式:clearerr(文件指针);功能:本函数用于清除出错标志和文件结束标志,使它们为0值无返回值.文件检测函数对称加密体制是传统而经典的加密体制策略。对称加密体制即加密方A和解密方B共享一个密钥key加密方A使用该密钥key对要保密的文件进行加密操作,从而生成密文解密方B 同样使用该密钥key 对加密方生成的加密文件实施解密操作,从而生成明文。文件检测函数(共52张PPT)
Linux开发环境与编程工具
linux环境开发概述
目录结构及文件
Linux命令
编辑器(vi+gedit)
编译器(gcc)
调试器(gdb)
1
2
3
4
5
Linux开发环境概述Windows开发特点:在电脑上装一个vc或其它集成开发环境编辑程序—>编译程序—>看现象—>有问题—>修改程序->调试程序->查看Linux开发特点:Linux下的程序开发大多通过在本地安装虚拟机、物理机或网络连接到服务器完成出于效率、远程开发、嵌入式开发的考虑:开发方式大多是在命令行下完成,没有很好的集成开发环境供我们使用Linux环境开发概述Linux文件及目录结构:无论何种版本的Linux,桌面、应用是Linux的外衣文件组织、目录结构才是Linux的内心其架构上设计的特点:大树底下好乘凉:对磁盘分区的管理(树状)一切皆文件: 对数据、硬件设备的管理Linux目录结构及文件大树底下好乘凉——树状结构管理磁盘Linux启动顺序:bootloader->内核->根文件系统->应用程序根文件系统按照一定格式存放在硬盘、FLASH中的某一个分区,Linux内核启动起来后首先会启动它其他分区、U盘、SD卡、光盘等"挂载"在根文件系统的某一目录下,我们通过此目录访问磁盘总之:Linux中看不到类似于windows下的C、D、E我们看到就是一棵大树(rootfs)Linux目录结构Linux目录结构/(根目录)是Linux系统中最顶层的目录,所有的文件夹、文件都是它的子目录。一切皆文件:Linux对数据、设备抽象成文件进行管理Linux对:数据文件(*.mp3、*.bmp)程序文件(*.c、*.h、*.o)设备文件(LCD、触摸屏、鼠标)网络文件(socket)总之,抽象为文件后,可以使用统一的方式方法管理文件在Linux下赋予了崭新的含义和地位Linux目录结构及文件Linux命令的格式命令概述Linux刚出世时没有什么图形界面,所有的操作全靠命令完成近几年来,Linux发展的非常迅速,图形界面越来越友好,但是在真正的开发过程中,Linux命令行的应用还是占有非常重要的席位的许多Linux功能在命令行界面要比图形化界面下运行的快,有些使用Linux的场合甚至没有图形化的界面可以说不会命令行,就不算会Linux。Linux命令的格式命令使用方法Linux命令格式:command [-options] [parameter1] …说明:command:命令名,相应功能的英文单词或单词的缩写[-options]:选项,可用来对命令进行控制,也可以省略parameter1 …:传给命令的参数,可以是零个一个或多个例:ls-alh/homeLinux命令帮助信息命令使用方法--help一般是Linux命令自带的帮助信息如:ls--help2. man(manual)有问题找男人man是Linux提供的一个手册,包含了绝大部分的命令、函数使用说明该手册分成很多章节(section),使用man时可以指定不同的章节来浏览。例:man ls; man3printf(printf既是命令又是函数,可使用 数字选定章节)Linux命令man方法命令使用方法man中各个section意义如下:1、Standard commands(标准命令)2、System calls(系统调用,如open,write)3、Library functions(库函数,如printf,fopen)Linux命令常用的技巧命令使用方法:自动补全:敲出命令前几个字母的同时按下tab键,系统会帮我们补全命令2.历史命令:当系统执行过一些命令后,可按上下键翻看以前的命令history将执行过的命令列举出来Linux命令常用的技巧命令使用方法:重定向:>本应显示在终端上的内容保存到执行文件中例:ls > test.txt;ls --help > test.txt2.管道:|一个程序的输出作为另一个程序的输入ls | less;ls | moreLinux常用命令常用命令ls tree clearcd pwdmkdir touchcat rmcp mvfind grep tarLinux常用命令常用命令ls:显示目录内容-l:列表显示-a:显示隐藏文件-h:配合-l以人性化的方式显示文件大小2. tree:以树状形式显示目录结构-L n(n为要查看的层数)系统默认没有此命令安装:sudo apt-get install tree3. clear:清除终端上的显示clearLinux常用命令常用命令1. cd:切换目录cd/home可进入home(指定)目录cd..可进入上一层目录cd-可进入上一个进入的目录cd~可进入用户的家目录(~:代指家目录)2. pwd:显示当前工作目录的绝对路径Linux常用命令常用命令mkdir:创建目录-p递归地在指定路径建立文件夹2. touch:创建文件touch test.c3. cat:显示文本文件内容cat test显示test文件的内容Linux常用命令常用命令rm:删除文件/目录rmfile1-rf-r:删除文件夹时必须加此参数-f:强制地删除文件,如果没有不出错2. ln:创建链接文件ln source file -s (file指向source)Linux常用命令常用命令cp:复制文件cp /etc/profile /home-r:拷贝文件夹-v:显示拷贝进度2. mv:移动文件或更改文件名mv file1 file2mv file1 /home–v:同cpLinux常用命令常用命令find:在指定路径下查找指定文件用法:find路径 -name文件名例子:find/home-nametest.txt//在根目录的home目录下查找名为test.txt的文件Linux常用命令常用命令grep:在指定文件中查找指定内容用法:grep查找信息 文件名 参数-n显示行号例子1:grep aaa test.c-n//在当前文件test.c中查找aaa信息,并显示行号例子2:grep aaa ./* -R -n//在当前文件夹下的所有文件查找包含aaa信息的文件Linux常用命令常用命令tar:压缩与解压缩命令gzip格式:1、压缩用法:tarzcvf压缩包包名 文件1文件2 ...例子:tarzcvfbk.tar.gz*.c2、解压用法:tar xvzf压缩包包名例子:tarzxvfbk.tar.gz3、解压到指定目录:-C例子:tar zxvf bk.tar.gz -C ./test//将bk.tar.gz解压到当前目录下的test目录下Linux常用命令常用命令tar:压缩与解压缩命令bz2格式:1、压缩用法:tar jcvf压缩包包名 文件1文件2 ...例子:tarjcvfbk.tar.bz2*.c2、解缩用法:tar jxvf压缩包包名例子:tarjxvfbk.tar.bz23、解压到指定目录:-C例子:tarjxvfbk.tar.bz2-C./gedit编辑器gedit:gedit是一个Linux环境下的文本编辑器类似windows下的写字板程序,在不需要特别复杂的编程环境下,作为基本的文本编辑器比较合适使用:在字符界面下,直接使用:gedit file1.txt即可vi编辑器vi在Linux界有编辑器之神的美誉vi编辑器是Linux系统中最常用的文本编辑器,几乎所有的Linux发行版中都包含vi程序vi工作在字符模式下,不需要图形界面,非常适合远程及嵌入式工作,是效率很高的文本编辑器尽管在Linux上也有很多图形界面的编辑器可用,但vi的功能是那些图形编辑器所无法比拟的。vi编辑器1、安装vimsudo apt-get install vim2、安装ctagssudo apt-get install ctagsvi编辑器3、配置vimstep1:将vim_configure拷入当前用户的目录下使用samba或共享文件夹完成step2:打开终端,执行以下以下命令:cd vim_configure./copy_con.sh拷贝成功后,出现copy successful(如提示没有权限:请使用:chmod +x copy_con.sh增加执行权限)vi编辑器使用vi打开文件vi filename:打开或新建文件,并将光标置于第一行行首vi +n filename:打开存在文件,并将光标置于第n行行首vi编辑器vim编辑器有3个操作模式:vi插入模式vi创建一个不存在文件时,默认进入插入模式vi将输入的字符作为正文内容放在正在编辑的文件中2. vi编辑模式vi打开一个已经存在文件时,默认进入编辑模式在此模式下可进入插入模式、控制屏幕光标的移动、进行文本的选择、复制、粘贴、剪切、删除、查找等工作。vi编辑器3. vi最后一行模式(命令模式)在编辑模式下,按shift+:进入跟编辑模式类似,完成存盘、另存、查找等任务vi编辑器由编辑模式切换到插入模式i 从光标当前位置开始插入o 在光标位置的下行插入一个空行,再进行插入由编辑模式切换到命令模式shift + :vi编辑器由插入模式、命令模式切换到编辑模式esc插入模式与命令模式不能直接转换vi编辑器编辑模式下删除和修改文本1、u撤消前面多次修改。2、[n]x删除光标后n个字符。3、[n]X删除光标前n个字符。4、[n]dd删除从当前行开始的n行。vi编辑器5、[n]yy复制从当前行开始的n行。6、p把粘贴板上的内容插入到当前行。7、.执行上一次操作8、shift +zz(按住shift按两下z键)保存退出当前文件vi编辑器编辑模式下移动光标[n]G:将光标定位到第n行开始处G:将光标定位到文件结束处gg:将光标定位到文件开始处编辑模式下的查找/字符串:从光标开始处向文件尾查找字符串。n:同一方向重复上一次查找命令。N:反方向重复上一次查找命令vi编辑器常用最后一行模式命令文件存储类::w保存当前文件:w <file>另存当前文件为file:wq保存当前文件,退出:q!不保存文件并退出2.配合搜索命令使用::nohls 取消高亮:set hls设置高亮vi编辑器配置文件的作用:1、自动添加创建时间、名称等注释信息2、自动添加行号3、支持鼠标点击定位4、支持函数列表功能(F9打开或关闭)vi编辑器5、支持多文件打开功能(F5)6、可使用ctrl+N 补全函数以及结构体成员7、自动整理代码格式:使用鼠标选中代码,按下“=”号键可自动规整代码8、支持vim快捷键.pdf中的快捷键。插件的特殊用法,参看《vim快捷键.pdf》gcc概述编译器是将易于编写、阅读和维护的高级计算机语言翻译为计算机能解读、运行的低级机器语言的程序。GNU项目中的一个子项目GCC(GNU Compiler Collection)是一个编译器套装GCC最初用于编译C语言,随着项目的发展GCC已经成为了能够编译C、C++、Java、Ada、fortran、Object C、Object C++、Go语言的编译器大家族。gccgcc作为真实的编译器和链接器的入口它会在需要的时候调用其它组件(预处理器、汇编器、链接器),并且会传一些额外的参数给编译器和连接器。输入文件的类型和传给gcc的参数决定了gcc调用哪些组件。gccgcc识别的文件扩展名如下:.c C语言文件.i 预处理后的C语言文件.C、.cc、.cp、.cpp、.c++、.cxx C++语言文件.ii 预处理后的C++语言文件.S 汇编文件.s 预处理后的汇编文件.o 编译后的目标文件.a 目标文件的静态链接库(链接时使用).so 目标文件的动态链接库(链接、运行时使用)gccgcc、g++编译选项-o file 指定生成的输出文件名为file-E 只进行预处理-S 只进行预处理和编译gccgcc、g++编译选项-c 只进行预处理、编译和汇编-Wall 生成所有级别的警告信息-w关闭所有警告,建议不使用此选项gdb简介GNU工具集中的调试器是gdb,该程序是一个交互式工具,工作在字符模式。除gdb外,linux下比较有名的调试器还有xxgdb, ddd, kgdb, ups。gdb的使用gdb是功能强大的调试器,可完成如下调试任务:设置断点监视程序变量的值程序的单步执行显示/修改变量的值gdb的使用显示/修改寄存器查看程序的堆栈情况远程调试gdb的使用gdb的使用下面以一个小程序来说明gdb的使用流程gdb的使用gdb的使用作业完成:linux常用命令_练习.txt完成:vi、gcc、gdb练习.txt知识点1【typedef 给已有的类型取个别名】(了解)0-1
案例:给int取个别名INT32
案例:给数组取个别名
案例:给指针取别名
案例:给函数指针 取别名
案例:给结构体类型取个别名
知识点2【结构体指针】0-2
案例:结构体指针
案例:从堆区给结构体申请一个空间
知识点3【结构体指针作为函数的参数】
案例:从堆区申请申请一个结构体数组 分函数 实现
知识点4【结构体的内存对齐】1-1
对齐规则:
结构体体嵌套结构体1-2
结构体嵌套结构体的内存对齐
案例:(了解)
知识点1【typedef 给已有的类型取个别名】(了
解)0-1
案例:给int取个别名INT32
1 //typedef使用步骤:
2 //1、先用已有的类型 定义一个变量
3 //2、用别名 替换 变量名
4 //3、在整个表达式前 添加typedef
5
6 //注意:不能创造新的类型
7 typedef int INT32;
8 void test01()
9 {
10 INT32 num = 10;
11 printf("num = %d\n", num);//10
12 }
案例:给数组取个别名
案例:给指针取别名
1 typedef int *P_TYPE;
2 void test03()
3 {
4 int num = 10;
5 P_TYPE p = #//P_TYPE p == int *p
6 printf("*p = %d\n", *p);//10
7 }
案例:给函数指针 取别名
1 int my_add(int x,int y)
2 {
3 return x+y;
4 }
5 //FUN_P 是一个函数指针类型 该函数 必须有两个int形参 以及一个int返回值
6 typedef int (*FUN_P)(int x,int y);
7 void test04()
8 {
9 FUN_P p = my_add;
10
11 printf("%d\n", p(100,200));//300
12 }
案例:给结构体类型取个别名
1 typedef struct stu
2 {
3 int num;
4 char name[32];
5 int age;
6 }STU;//此时的STU不是结构体变量 而是结构体类型
7
8 struct stu2
9 {
10 int num;
11 char name[32];
12 int age;
13 }STU2;//变量名
14
15 void test05()
16 {
17 STU lucy={100,"lucy",18};//struct stu lucy
18
19 }
知识点2【结构体指针】0-2
案例:结构体指针
1 #include
2
3 typedef struct
4 {
5 int num;
6 char name[16];
7 int age;
8 }STU;
9 //STU 是结构体类型
10
11 void test01()
12 {
13 STU lucy={100,"lucy",18};
14
15 STU *p = &lucy;
16
17
18 printf("num = %d, name=%s, age=%d\n",lucy.num, lucy.name, lucy.age);
19 printf("num = %d, name=%s, age=%d\n",(*p).num, (*p).name, (*p).age);
20 printf("num = %d, name=%s, age=%d\n",p‐>num, p‐>name, p‐>age);
21
22 printf("num = %d\n", (&lucy)‐>num );
23
24 return;
25 }
案例:从堆区给结构体申请一个空间
1 void test02()
2 {
3 STU *p = NULL;
4 //从堆区申请结构体空间
5 p = (STU *)calloc(1,sizeof(STU));
6 if(NULL == p)
7 {
8 perror("calloc");
9 return;
10 }
11
12 //获取键盘输入
13 printf("请输入一个学生的信息num name age\n");
14 scanf("%d %s %d", &p‐>num, p‐>name, &p‐>age);
15
16 //遍历
17 printf("num = %d, name=%s, age=%d\n",p‐>num, p‐>name, p‐>age);
18
19 //释放空间
20 if(p != NULL)
21 {
22 free(p);
23 p=NULL;
24 }
25
26 return;
27 }
运行结果:
知识点3【结构体指针作为函数的参数】
1 void mySetSTUData(STU *p)//p=&lucy
2 {
3 printf("请输入一个学生的信息num name age\n");
4 scanf("%d %s %d", &p‐>num, p‐>name, &p‐>age);
5
6 return;
7 }
8 void myPrintSTUData(const STU *p)//p =&lucy *p只读
9 {
10 //const STU *p 不允许用户借助 p修改 p所指向空间的内容
11 // p‐>num = 10000;
12 printf("sizeof(p)=%d\n", sizeof(p));
13 printf("num = %d, name=%s, age=%d\n",p‐>num, p‐>name, p‐>age);
14 }
15 void test03()
16 {
17 STU lucy;
18 memset(&lucy,0,sizeof(lucy));
19
20 //定义一个函数 给lucy的成员获取键盘输入
21 mySetSTUData(&lucy);
22
23 //定义一个函数 打印lucy的成员信息
24 myPrintSTUData(&lucy);
25 }
运行结果:
案例:从堆区申请申请一个结构体数组 分函数 实现
1 STU * get_array_addr(int n)
2 {
3 return (STU *)calloc(n,sizeof(STU));
4 }
5
6 //arr代表的是空间首元素地址
7 void my_input_stu_array(STU *arr, int n)
8 {
9 int i=0;
10
11 for(i=0;i12 {
13 printf("请输入第%d个学生的信息\n",i+1);
14 //scanf("%d %s %d", &arr[i].num, arr[i].name, &arr[i].age);
15 scanf("%d %s %d", &(arr+i)‐>num , (arr+i)‐>name, &(arr+i)‐>age);
16 }
17 }
18
19 void my_print_stu_array(const STU *arr, int n)
20 {
21 int i=0;
22 for(i=0;i23 {
24 printf("num=%d, name=%s, age=%d\n", \
25 (arr+i)‐>num, (arr+i)‐>name,(arr+i)‐>age);
26 }
27
28 return;
29 }
30
31 void test04()
32 {
33 int n = 0;
34 STU *arr=NULL;
35
36 printf("请输入学生的个数:");
37 scanf("%d", &n);
38
39 //根据学生的个数 从堆区 申请空间
40 arr = get_array_addr(n);
41 if(arr == NULL)
42 {
43 perror("get_array_addr");
44 return;
45 }
46
47 //从键盘 给结构体数组arr输入数据
48 my_input_stu_array(arr, n);
49
50 //遍历结构体数组
51 my_print_stu_array(arr, n);
52
53 //释放空间
54 if(arr != NULL)
55 {
56 free(arr);
57 arr=NULL;
58 }
59 }
运行结果:
知识点4【结构体的内存对齐】1-1
对齐规则:
1 struct data
2 {
3 char c;//1B
4 int i;//4B
5 };
6 void test05()
7 {
8 struct data d;
9 //结构体的大小 >= 成员大小之和
10 printf("%d\n",sizeof(struct data));//8
11
12 printf("&d.c = %u\n",&d.c );
13 printf("&d.i = %u\n",&d.i );
14 }
运行结果:
案例:
1 typedef struct
2 {
3 int a;
4 char b;
5 short c;
6 char d;
7 }DATA;
8
9 void test06()
10 {
11 DATA d;
12 printf("%d\n", sizeof(DATA));
13
14 printf("%u\n", &d.a);
15 printf("%u\n", &d.b);
16 printf("%u\n", &d.c);
17 printf("%u\n", &d.d);
18
19 }
案例:
1 typedef struct
2 {
3 char a;
4 short b;
5 char c;
6 short d;
7 }DATA;
结构体体嵌套结构体1-2
1 typedef struct
2 {
3 int x;
4 int y;
5 }DATA2;
6
7 typedef struct
8 {
9 int a;
10 int b;
11 DATA2 c;//结构体变量c 作为了DATA3的成员 叫结构体嵌套结构体
12 }DATA3;
13
14 void test07()
15 {
16 //DATA3 data={10,20,30,40};
17 DATA3 data={10,20,{30,40}};//推荐
18
19 printf("a = %d\n", data.a);
20 printf("b = %d\n", data.b);
21 printf("x = %d\n", data.c.x);//访问到最底层
22 printf("y = %d\n", data.c.y);
23 }
运行结果:
结构体嵌套结构体的内存对齐
1 typedef struct
2 {
3 short d;
4 char e;
5 }DATA2;
6
7 typedef struct
8 {
9 short a;
10 int b;
11 DATA2 c;
12 char f;
13 }DATA;
14
15 void test08()
16 {
17 DATA data;
18 printf("%d\n",sizeof(DATA));
19
20 printf("a:%u\n", &data.a);
21 printf("b:%u\n", &data.b);
22 printf("c中d:%u\n",&data.c.d);
23 printf("c中e:%u\n",&data.c.e);
24 printf("f:%u\n",&data.f);
25 }
案例:
1 typedef struct
2 {
3 short d;
4 char e;
5 }B;
6 typedef struct
7 {
8 int a;
9 short b;
10 B c;
11 short f;
12 }A;
案例:(了解)
1 typedef struct
2 {
3 char a;
4 int b;
5 short c;
6 }DATA;
7 void test10()
8 {
9 DATA data={'a',100, 20};
10 char *p = &data;
11
12 printf("c = %hd\n", data.c);
13 //需求 借助p访问20
14 printf("c = %hd\n", *(short *)(p+8));
15 }
原理图:
运行结果:(共40张PPT)
Linux系统安装及使用
2
操作系统简单介绍
linux系统安装与使用
安装必备软件
操作系统演变及历史介绍
操作系统的目标
方便:使计算机系统易于使用
有效:以更有效的方式使用计算机系统资源
扩展:方便用户有效开发、测试和引进新功能
操作系统的功用
进程管理/内存管理/文件系统/设备控制/网络管理
3
操作系统演变及历史介绍
4
反智能手机的极致—John’s Phone
智能手机的极致—android/iphone
操作系统演变及历史介绍
5
操作系统在计算机系统中承上启下的地位
——向下封装硬件,向上提供操作接口
Linux操作系统介绍
Linux是最受欢迎的自由电脑操作系统内核,是一个用C语言写成,符合POSIX标准的类Unix操作系统
诞生于1991年10月5日,由芬兰黑客 Linus Torvalds为尝试在英 特尔x86架构上提供自由免费的类Unix操作系统而开发的
Linux操作系统的诞生、发展、和成长过程依赖于五个重要支柱:
unix操作系统、minix操作系统、GNU计划、POSIX标准和互联网
6
Linux操作系统介绍
unix操作系统、minix操作系统:
linux操作系统的前身
是linux一直模仿和要超越的对象
7
Linux操作系统介绍
GNU计划和自由软件基金会(FSF):
Richard M.Stallman于1984年创办,旨在开发一个免费、类unix的操作系统-GNU系统及其开发工具
Emacs编辑系统、BASH shell程序、GCC、GDB等开发工具都是GNU组织的产品
后来与linux内核结合成为了现在的GNU/linux
FSF:于1985年10月建立:监督保证执行GNU计划
8
Linux操作系统介绍
POSIX(Portable Operating System Interface)
可移植操作系统接口,由电气和电子工程师协会(IEEE)开发用来统一unix、linux各分支编程接口,以提高其通用型和可移植性
使得linux的发展结束了初期的混乱发展阶段,进入了一个新的时期
互联网
来自世界各地的黑客、自由软件推崇者通过网络联合在一起,完成了linux的开发工作
9
Linux操作系统介绍
10
Linux之父--Linus Torvalds
Linux操作系统介绍
11
自由软件之父--Richard Stallman
天才程序员
主持了著名的GNU工程
GNU的定义: GNU's Not Unix
自由软件基金会:
Free Software Foundation (FSF)
Richard Stallman两款自由软件:Emacs、gcc
General Public License (GPL)
GNU工程与Linux
系统安装方案
物理机安装:
可使用光驱、U盘(将镜像固化在U盘中)安装
优点:对操作系统的体会更加真切方便
缺点:刚刚接触Linux的人无从下手,使用不太习惯
12
系统安装方案
虚拟机安装
在电脑上安装虚拟机,在虚拟机中安装操作系统
优点:由虚拟机软件虚拟出一台或多台电脑,同时运行二个 或更多的操作系统,并进行数据交换,方便快捷。可同时操作windows的软件辅助学习
缺点:性能相比物理机较差,它对物理机的性能,如内存、CPU、磁盘等要求较高。
13
虚拟机介绍及安装
虚拟机介绍:
虚拟机可以说是一种软件,也可以说是一种技术,它允许你在一台主机上虚拟出多台计算机,每台虚拟的计算机都可以有自己的硬件及软件配置。
虚拟机技术的作用
可以帮助网络架构工程师在一台电脑上模拟、检测整个网络运行情况。
可以极大的降低企业的运营成本与风险。
可以帮助操作系统设计者测试、解决问题。
可以帮助开发者在多个平台上进行自由切换。
14
虚拟机介绍及安装
下面我们选取使用最为广泛的三种虚拟机来进行介:
VMware、Virtual PC、Virtual Box
Virtual PC是Microsoft公司开发的一个免费“虚拟机”软件,它使用户可以在一台机器上同时运行多个操作系统
VMware是目前市场占有率最高的虚拟机,是虚拟界巨头VMware公司的产品,其功能非常强大且稳定
VirtualBox 是一款开源虚拟机软件。VirtualBox 是由德国 Innotek 公司开发,由Sun Microsystems公司出品的软件,使用Qt编写,在 Sun 被 Oracle 收购后正式更名成 Oracle VM VirtualBox
15
安装系统
我们这里使用vmplayer player虚拟机
vmware Player是Vmware Workstation的精简版本,具有体积小,使用灵活,免费等特点
非常适合我们嵌入式领域的开发过程
安装虚拟机
软件链接:https://pan./s/1k9evNQ-czphUvsSLT9LagA
提取码:nzee
安装Vmware Player
双击VMware-player-15.5.0.exe,一路next即可
(电脑不同,可能会重启几次)。
16
安装系统
安装完毕后双击打开虚拟机
17
安装系统
Ubuntu的安装
我们已经给大家准备配置好的Ubuntu
软件链接:https://pan./s/1k9evNQ-czphUvsSLT9LagA
提取码:nzee
3. 下载下来后直接解压缩
18
安装系统
通过VMware打开ubuntu系统
点击Vmware软件中的“打开虚拟机”
19
安装系统
通过VMware打开ubuntu系统
2. 在弹出的对话框中找到刚才解压的ubuntu文件中
的“Ubuntu 64 位.vmx”,然后点击“打开”
20
安装系统
通过VMware打开ubuntu系统
3. 打开之后会在Vmware软件左边显示Ubuntu 64位,右边会显示ubuntu的一些信息,点击“播放虚拟机”
21
安装系统
通过VMware打开ubuntu系统
4. 如果出现软件更新窗口,点击“以后提醒我”即可
5. 跳转到输入密码窗口,默认密码为111111
22
安装系统
通过VMware打开ubuntu系统
6. 密码输入正确后即可进入ubuntu系统
23
Ubuntu的使用
24
ubuntu桌面
从2010.10版本后,Unity成为ubuntu默认桌面。
Unity桌面环境打破了传统的GNOME面板配置。
桌面左边包括一个启动器停靠栏和任务管理面板。
Ubuntu的使用
dash的使用
Unity桌面在左面的第一个位置上,设置了一个dash home搜索栏,ubuntu下所有的应用程序可通过搜索栏找到。
如果我们要打开终端,直接在dash中输入"terminal"或“终端”即可找到对应的软件,单击即可启动。
25
Ubuntu的使用
vmtools
vmtools是Vmware的工具包,对Vmware中安装的linux起到增强的效果,但是对物理机安装的Linux不起作用。
vmtools的作用:
1、用于虚拟机显示的优化与调整。
2、允许共享主机文件夹,方便虚拟机访问主机文件
3、可以直接使用右键复制windows字符串、文件到linux编辑器或终端。
26
Ubuntu的使用
Vmtools的功能
系统桌面可以随着窗口大小自适应
在windows的写字板或者其它文本编辑工具中右击选中复制,在linux中可以黏贴 —— 方便windows、linuxs信息交互
可以右击复制windows下的文件,在linux中黏贴 ——方便window、linux文件交换
27
Ubuntu的使用
Vmtools的功能
共享windows文件
关机、打开虚拟机设置、设置共享文件路径
28
系统更新
ubuntu更新设置:
ubuntu操作系统发布时,为了减小操作系统的体积,仅仅配备了基本的系统软件、应用软件。
我们开发中需要用到的大部分软件都需要在使用中从网上自行更新。
如果ubuntu没有网络,可以说寸步难行,下面教大家一下如何使用互联网和局域网进行更新。
29
系统更新
互联网更新
可以上网的虚拟机,直接可从互联网更新软件。
在dash栏中直接搜索更新,打开点击检查、安装即可
30
系统更新
局域网更新
公司的网络资源限,多采用局域网更新。
架设服务器,先将更新下载到本地,然后本地从服务器更新
设置局域网更新源:(我们采用此种方式)
sudo gedit /etc/apt/sources.list
31
系统更新
字符界面:
sudo apt-get update //获得最新的软件包的列表
在设好更新源后,必须先执行此命令。
跟图形界面的check功能一致
sudo apt-get install xxx //从源中安装xxx软件
sudo apt-get remove xxx //删除包
32
系统更新
验证
字符界面更新应用程序实例:(需注销)
例1:安装右键“在终端中打开”的工具
sudo apt-get install nautilus-open-terminal
3. 例2:安装gnome经典桌面
sudo apt-get install gnome-shell
33
系统更新
验证
字符界面更新应用程序实例:(需注销)
例1:安装右键“在终端中打开”的工具
sudo apt-get install nautilus-open-terminal
3. 例2:安装gnome经典桌面
sudo apt-get install gnome-shell
34
安装必备软件-ubuntu
samba:
samba服务器在两种不同操作系统间架起了一座桥梁,使Linux系统和Windows系统之间能够通信。
2. Linux操作系统下的samba服务,实现了Windows主机访问Linux下共享文件的功能。
35
安装必备软件-ubuntu
ubuntu下samba安装(已安装好)
安装命令:
sudo apt-get install samba smbfs smbclient system-config-samba
注:
samba:samba核心组件。
smbfs:支持SMB/CIFS协议的文件系统。
smbclient:samba客户端,访问其它机器。
system-config-samba:图形界面配置工具。
36
安装必备软件-ubuntu
测试使用:
获得虚拟机ip:
在虚拟机中打开终端:ifconfig
2. 访问samba共享目录:
开始-->运行-->\\10.220.x.x(虚拟机ip)
3. 输入用户名、密码:
输入前面图形界面设置的用户名与密码即可
37
安装必备软件-ubuntu
ssh
ssh为Secure Shell的缩写,由互联网工程任务组(IETF-The Internet Engineering Task Force)的网络工作小组(Network Working Group)所制定。
ssh是建立在应用层和传输层基础之上的安全协议。ssh是目前较可靠,专为远程登录会话和其他网络服务提供安全性的协议。
38
安装必备软件-ubuntu
ubuntu安装ssh服务器:
sudo apt-get install openssh-server
重启ssh服务(出现问题时使用,同samba):
sudo service ssh restart
39hello filehello file知识点1【Linux常用命令】0-1
1、创建连接文件
a、创建软连接
b、创建硬链接
2、cp 文件或目录 拷贝
3、mv移动文件或目录 (剪切)
4、find 查找文件
5、grep查找指定的内容
6、tar 压缩解压 gzip格式
7、tar 压缩解压 bz2格式0-2
8、gedit文本编辑器
知识点2【编辑器之神 vim 的安装及配置】0-2
知识点3【vim的模式】
1、vim的模式的分类
2、vim的模式 的认识
3、vim的模式切换
知识点4【vim 模式中的命令】
1、编辑模式下的命令:
2、命名行 模式的命令
知识点5【gcc 编译器】
知识点6【gdb调试】
知识点1【Linux常用命令】0-1
1、创建连接文件
a、创建软连接
ln -s 源文件名 连接文件名
特点:
1、源文件 和 连接文件 时刻同步
2、一旦删除源文件 那么连接文件 将不可用, 如果删除的是连接文件 那么原文文件
是可用的
b、创建硬链接
ln 源文件名 连接文件名
特点:
1、源文件 和 连接文件 时刻同步
2、删除源文件 或连接文件 都不会影响 另一个未被删除的文件
2、cp 文件或目录 拷贝
cp 源文件/目录 路径 ------ 拷贝
cp 源文件 文件名 -------复制
cp 如果拷贝文件夹 必须加-r
-v 显示拷贝进度
3、mv移动文件或目录 (剪切)
剪切功能:mv 文件名 目录
重名令功能:mv 文件名1 文件名2 将文件名1 重命名为文件名2
4、find 查找文件
5、grep查找指定的内容
6、tar 压缩解压 gzip格式
案例1:压缩
案例2:解压
案例3:解压 到 指定位置
7、tar 压缩解压 bz2格式0-2
案例1:压缩
案例2:解压
案例3:解压到指定的文件夹 -C
8、gedit文本编辑器
知识点2【编辑器之神 vim 的安装及配置】0-2
1、安装vim
1 sudo apt‐get install vim
1 sudo apt‐get install ctags
2、配置vim
在终端 找到 vim_configure
进入 vim_configure
sudo ./copy_con.sh 运行脚本
3、验证是否成功:
vim test.c
知识点3【vim的模式】
1、vim的模式的分类
编辑模式:
在此模式下可进入插入模式、控制屏幕光标的移动、进
行文本的选择、复制、粘贴、剪切、删除、查找等工作
(修改代码)
插入模式:
将输入的字符作为正文内容放在正在编辑的文件中(写
代码)
命令行模式:
完成存盘、另存、查找等任务(保存 代码)
2、vim的模式 的认识
编辑模式:
插入模式:
命令行模式:
3、vim的模式切换
知识点4【vim 模式中的命令】
1、编辑模式下的命令:
u:撤销
dd:删除一行
[n]dd:删除n行
yy:复制一行
[n]yy:复制n行
p:粘贴
shift +zz 保存并退出
G:
gg:
/字符串:
2、命名行 模式的命令
w保存 q退出 !强制执行
知识点5【gcc 编译器】
gcc 编译的过程:预处理、编译、汇编、链接
预处理: gcc -E test.c -o test.i
编译: gcc -S test.i -o test.s
汇编: gcc -c test.s -o test.o
链接:gcc test.o -o test
运行可执行文件:./test
一步到位:
gcc test.c -o test (生成的可执行文件为test)
gcc test.c 生成 a.out
vim打开多个文件:
vim a.c b.c c.c
文件之间的切换:
:open 文件名
gcc 编译多个文件
gcc a.c b.c c.c ... -o 执行文名 (不用加头文件)
知识点6【gdb调试】
gcc -g a.c -o test
gdb test
调试中q退出
知识点7【notepad++】1-2(了解)
添加插件:到软件包
将上面复制到的库 放入安装目录:
测试成功:
修改编码格式:(别多次该)
知识点8【vscode】(了解)
1、下载:https://code./
找到安装软件 点击安装:
启动的vscode 注意 vscode以文件夹 为单位管理工程
在vscode工程中创建文件
编辑代码:
编译代码:
第一种:在虚拟机中编译
第二种方式:在vscode编译
在vscode中连接ubuntu的终端知识点1【make概述】
1、gcc的编译过程 分析问题
知识点2【make的语法】
案例1:写一个简单的makefile文件
make 目标名称
案例:多定义一个目标(可以没有依赖文件 假想目标)
知识点3【复杂的makefile】
知识点4【makefile中的变量】
1、变量的分类
2、自定义变量
案例:多文件makefile第一次升级
3、make工具 给 makefile传值
4、系统环境变量
5、预定义变量
知识点5【歌词解析项目】歌曲同步
1、项目显示
2、项目运行
3、项目整体分析
知识点6【歌词解析项目流程】(ubuntu下)
1、将歌词文件 一次性去取到 内存中
a、用fopen打开歌词文件 FILE *fp = fopen("简单爱.lrc", "r");
b、使用fseek将文件流指针 定位到文件尾部 获得文件总大小
c、使用rewind 复位文件流置针
d、根据文件总大小 从堆区 申请 合适的空间 char *arr
e、使用fread 读取文件数据 到内存中 arr
2、将arr指向的内存数据 按行"\r\n"切割 存入 字符指针数组 char *buf[128]={NULL};
3、逐行分析 buf[0]代表第0行 buf[n] 代表第n行
a、单独分析前4行
b、逐行分析剩下的行 将时间+歌词 一一对应 插入链表
4、模拟时钟
5、滚屏(4行)
//光标定位:(注意)
6、反显 当前歌词为 红色 其他歌词为黑色
7、启动mplayer
知识点7【工程的创建】
知识点1【make概述】
1、gcc的编译过程 分析问题
gcc 编译的时候 不会 检查 文件是否被修改过 直接 所有源
文件 重新编译(会造成 大量的重复 编译)
make: 编译的时候 会 检查 文件是否被修改过 只编译 被
修改过源文件 (避免 大量的重复 编译)
make:大量代码 的关系维护
知识点2【make的语法】
通过 依赖文件 生成 目标文件。
目标:依赖文件 (只是关系的描述)
命令列表(将依赖文件 生成 目标文件的具体指令)
注意:依赖文件可以是多个。命令 也可以多个
案例1:写一个简单的makefile文件
main.c
1 #include
2 #include "main.h"
3
4 int main(int argc, char *argv[])
5 {
6 printf("hello make world\n");
7
8 printf("PI = %lf\n",PI);
9 return 0;
10 }
11
main.h
1 #define PI 3.14
makefile
1 main:main.c
2 gcc main.c ‐o main
当敲make指令的时候 make工具 自动寻找当前路径下的
GNUmakefile、makefile/Makefile文件 按照
GNUmakefile、makefile/Makefile中规则执行编译。
如果 你的makefile文件名是自定义 就需要 -f指明
make 目标名称
makefile
1 main:main.c
2 gcc main.c ‐o main
3 A:xxx
4 xxxxxxxxxxx
5 B:hhh
6 hhhhhhhhhhh
只敲make 默认执行 第一个目标 然后 退出
如果想执行A目标:make A
如果想执行B目标:make B
案例:多定义一个目标(可以没有依赖文件 假想目标)
makefile
1 main:main.c
2 gcc main.c ‐o main
3 clean:
4 rm main
知识点3【复杂的makefile】
main.c
1 #include
2 #include "main.h"
3 #include "printf1.h"
4
5 int main(int argc, char *argv[])
6 {
7 printf("hello make world\n");
8 printf("PI=%lf\n",PI);
9 printf1();
10 return 0;
11 }
main.h
1 #define PI 3.14
printf1.c
1 #include
2 #include "main.h"
3 void printf1(void)
4 {
5
6 printf("hello printf1 world PI=%lf\n",PI);
7 return;
8 }
printf1.h
1 extern void printf1(void);
makefile初级版本:
知识点4【makefile中的变量】
1、变量的分类
自定义变量
环境变量
预定义变量
2、自定义变量
案例:多文件makefile第一次升级
1 exec=main
2 cc=gcc
3 obj=main.o printf1.o
4 cflags=‐Wall ‐g
5 $(exec):$(obj)
6 $(cc) $(obj) ‐o $(exec) $(cflags)
7 main.o:main.c
8 $(cc) ‐c main.c ‐o main.o $(cflags)
9 printf1.o:printf1.c
10 $(cc) ‐c printf1.c ‐o printf1.o $(cflags)
11 clean:
12 rm $(exec) *.o
3、make工具 给 makefile传值
4、系统环境变量
env查看环境变量
export test=100 将test变量 设置环境变量
5、预定义变量
比较完善的makefile
1 exec=main
2 cc=gcc
3 obj=main.o printf1.o #你要修改的地方
4 cflags=‐Wall ‐g
5 $(exec):$(obj)
6 $(cc) $^ ‐o $@ $(cflags)
7 %.o:%.c
8 $(cc) ‐c $< ‐o $@ $(cflags)
9 clean:
10 rm $(exec) *.o
知识点5【歌词解析项目】歌曲同步
1、项目显示
保证ubuntu有mplayer软件
sudo apt-get install maplyer
2、项目运行
3、项目整体分析
分析歌词文件 将时间+歌词 组成一个 节点 让如 有序链表 中 模拟时钟 如果时间 ==链
表中节点的时间 就打印 该表的歌词。
知识点6【歌词解析项目流程】(ubuntu下)
1、将歌词文件 一次性去取到 内存中
a、用fopen打开歌词文件 FILE *fp = fopen("简单爱.lrc", "r");
b、使用fseek将文件流指针 定位到文件尾部 获得文件总大小
c、使用rewind 复位文件流置针
d、根据文件总大小 从堆区 申请 合适的空间 char *arr
e、使用fread 读取文件数据 到内存中 arr
2、将arr指向的内存数据 按行"\r\n"切割 存入 字符指针数组 char
*buf[128]={NULL};
1 strtok函数切割
2 //arr指向内存存储的歌词
3 char *buf[128]={arr,NULL};
4 int i=0;
5 //切割
6 while(buf[i++] = strtok(buf[i],"\r\n"));
7
已将歌词的每一行 存放 在 指针数组中 注意 记得保存 切割到的行数
3、逐行分析 buf[0]代表第0行 buf[n] 代表第n行
a、单独分析前4行
原数据
1 [ti:简单爱]
2 [ar:周杰伦]
3 [al:范特西]
4 [by:大脸猫]
希望的数据
1 int i=0;
2 for(i=0;i<4;i++)
3 {
4 char tmp[128]="";
5 //[ti:简单爱] ‐‐>sscanf(buf[0],"%*[^:]:%[^]]", tmp);//tmp‐‐简单
6 buf[i];//分析
7 }
b、逐行分析剩下的行 将时间+歌词 一一对应 插入链表
链表节点的设计:
1 typedef struct lrc
2 {
3 //数据域
4 int time;
5 char lrc[128];
6
7 //指针域
8 struct lrc *next;
9 }LRC;
1
2 i=4;
3 for(i=4;i<行数;i++)
4 {
5 char *str_lrc = buf[i];
6 //[03:34.64][02:34.71][01:05.83]我想就这样牵着你的手不放开
7 //1、寻找歌词的位置
8 while(*str_lrc == '[')
9 str_lrc +=10;
10 //str_lrc指向了歌词的位置
11
12 //逐个时间分析
13 char *str_time = buf[i];
14 while(*str_time == '[')
15 {
16 int m = 0,s = 0;
17 sscanf(str_time,"[%d:%d.64]", &m,&s)
18 int time = m*60+s;//以秒为单位
19
20 //将时间 和 歌词 一一对应 放入 结构体
21 LRC tmp;
22 tmp.time = time;
23 strcpy(tmp.lrc, str_lrc);
24
25 //调用链表的有序插入函数
26 head = insert_link(head,tmp);
27
28 //分析下一个时间
29 str_time += 10;
30 }
31
32 }
建议:此处遍历一下链表
4、模拟时钟
1 #include
2
3 int main(int argc, char *argv[])
4 {
5 //读取歌词文件
6 //按行切割
7 //逐行分析
8 //启动maplayer(见后面)
9 mplayer_play("简单爱.mp3");
10 int i=0;
11 while(1)
12 {
13 printf("\r%02d:%02d",i/60,i%60);
14 fflush(stdout);
15 //到链表查询
16 LRC *ret = search_link(head,i);
17 if(ret != NULL)
18 {
19 printf("%s\n", ret‐>lrc);
20 }
21
22
23 sleep(1);//休眠1s
24 i++;
25 }
26 return 0;
27 }
28
5、滚屏(4行)
//光标定位:(注意)
6、反显 当前歌词为 红色 其他歌词为黑色
7、启动mplayer
1 mplayer_play("简单爱.mp3");
start_mplayer.c
1 #include
2 #include
3 #include
4 //启动mplayer播放器
5 //参数song_path 为歌曲的路径
6 void mplayer_play(char * song_path)
7 {
8 pid_t pid;
9 pid=fork();
10 if(pid<0)
11 {
12 perror("fork");
13 }
14 else if(pid==0)
15 {
16 close(1);
17 close(2);
18 execlp("mplayer","mplayer","‐slave","‐quiet",song_path,NULL);
19 exit(0);
20 }
21 else
22 ;
23 }
start_mplayer.h
1 #ifndef __START_MPLAYER_H__
2 #define __START_MPLAYER_H__
3 //启动mplayer播放器
4 //参数song_path 为歌曲的路径
5 extern void mplayer_play(char * song_path);
6 #endif
知识点7【工程的创建】
win+r 访问ubuntu的samba服务器
打开vscode
知识点9【光标定位 以及 滚屏】
1 /* **********************************************************************
**
2 * Filename: main.c
3 * Description:
4 * Version: 1.0
5 * Created: 2020年03月11日 14时33分02秒
6 * Revision: none
7 * Compiler: gcc
8 * Author: YOUR NAME (),
9 * Company:
10 * *********************************************************************
***/
11
12
13 #include
14 #include
15 //#include "console.h"
16 /*
17 COLOR_RED 红
18 COLOR_BLACK 黑
19 COLOR_GREEN 绿
20 COLOR_BLUE 蓝
21 COLOR_YELLOW 黄
22 COLOR_WHITE 白
23 COLOR_CYAN 青
24 COLOR_MAGENTA 洋红
25 */
26 #define COLOR_RED 31
27 #define COLOR_BLACK 30
28 #define COLOR_GREEN 32
29 #define COLOR_BLUE 34
30 #define COLOR_YELLOW 33
31 #define COLOR_WHITE 37
32 #define COLOR_CYAN 36
33 #define COLOR_MAGENTA 35
34 //设置前景颜色
35 void set_fg_color(int color)
36 {// ESC[#m
37 printf("\033[%dm",color);
38 fflush(stdout);
39 }
40 void cusor_moveto(int x, int y)
41 {// ESC[y;xH
42 printf("\033[%d;%dH",y,x);
43 fflush(stdout);
44 }
45 int main(int argc, char *argv[])
46 {
47
48 //文件读取
49 //文件切割
50 //组行分析
51 //插入链表
52
53 int time=0;
54 char buf1[128]="";
55 char buf2[128]="";
56 char buf3[128]="";
57 char buf4[128]="";
58 while(1)
59 {
60 //光标定位
61 cusor_moveto(20,3);
62 printf("%02d:%02d",time/60,time%60 );
63 fflush(stdout);
64
65 //LRC *ret = search_link(head, time);
66 //if(ret != NULL)
67 {
68 //滚起来
69 strcpy(buf1,buf2);
70 strcpy(buf2,buf3);
71 strcpy(buf3,buf4);
72 //strcpy(buf4, ret‐>lrc_buf);
73 sprintf(buf4,"歌词%d",time);
74 cusor_moveto(15,5);
75 printf("%s", buf1);
76
77 cusor_moveto(15,6);
78 printf("%s", buf2);
79
80 cusor_moveto(15,7);
81 printf("%s", buf3);
82
83 cusor_moveto(15,8);
84 set_fg_color(COLOR_RED);
85 printf("%s", buf4);//当前歌词
86 set_fg_color(COLOR_BLACK );
87 fflush(stdout);
88 }
89 //滚屏显示歌词
90 //第一行
91
92
93 sleep(1);
94 time++;
95 }
96 return 0;
97 }
98
991111111111111111111111111111
2222222222222222222222222222
3333333333333333333333333333
1111111111111111111111111111
2222222222222222222222222222
3333333333333333333333333333
1111111111111111111111111111
2222222222222222222222222222
3333333333333333333333333333
1111111111111111111111111111
2222222222222222222222222222
3333333333333333333333333333
1111111111111111111111111111
2222222222222222222222222222
3333333333333333333333333333
1111111111111111111111111111
2222222222222222222222222222
3333333333333333333333333333
1111111111111111111111111111
2222222222222222222222222222
3333333333333333333333333333
1111111111111111111111111111
2222222222222222222222222222
3333333333333333333333333333
1111111111111111111111111111
2222222222222222222222222222
3333333333333333333333333333
1111111111111111111111111111
2222222222222222222222222222
3333333333333333333333333333(共67张PPT)
指针的概念与应用
1
有关内存的那点事
2
3
4
指针的相关概念
指针的定义方法
指针的分类
目录
5
指针和变量的关系
6
指针和数组元素的关系
7
8
9
指针数组
指针的指针
字符串和指针
目录
10
数组指针
11
指针和函数的关系
12
经常容易混淆的指针
13
特殊指针
指针
爱它,就让它去学C语言的指针
恨它,也让它去学C语言的指针
指针是C语言里面最重要也是最难理解的知识
学习了指针,才算真正踏入C语言的大门
4
有关内存的那点事
5
内存—很熟悉的名词,但又觉得很神秘。
在学习指针之前先让我们
了解一些内存的基本知识
有关内存的那点事
内存含义
存储器:计算机的组成中,用来存储程序和数据,辅助CPU进行运算处理的重要部分
外存又叫外部存储器,长期存放数据,掉电不丢失数据
常见的外存设备:硬盘、flash、rom、u盘、光盘、磁带
内存又叫内部存储器,暂时存放数据,掉电数据丢失
常见的内存设备:ram、DDR
6
有关内存的那点事
内存的作用(内存储器):
1.内存是沟通cpu与硬盘的桥梁:
2.暂存放CPU中的运算数据
3.暂存与硬盘等外部存储器交换的数据
7
有关内存的那点事
有关内存的两个概念:
物理内存:实实在在存在的存储设备
虚拟内存:操作系统虚拟出来的内存
8
有关内存的那点事
操作系统会在物理内存和虚拟内存之间做映射。
在32位系统下,每个进程(运行着的程序)的
寻址范围是4G,0x00 00 00 00 ~0xff ff ff ff
在写应用程序的,咱们看到的都是虚拟地址。
9
有关内存的那点事
在运行程序的时候,操作系统会将 虚拟内存进行分区
堆:在动态申请内存的时候,在堆里开辟内存
栈:主要存放局部变量(在函数内部,或复合语句内部定义的变量)。
静态全局区
1):未初始化的静态全局区:
静态变量(定义的时候,前面加static修饰),或全局变量 ,没有初始化的,存在此区
2):初始化的静态全局区
全局变量、静态变量,赋过初值的,存放在此区
10
有关内存的那点事
在运行程序的时候,操作系统会将 虚拟内存进行分区
4.代码区:存放咱们的程序代码
5.文字常量区:存放常量的。
内存以字节为单位来存储数据的,咱们可以将程序中的虚拟寻址空间,
看成一个很大的一维的字符数组
11
指针的相关概念
12
操作系统给每个存储单元分配了一个编号,
从0x00 00 00 00 ~0xff ff ff ff
这个编号咱们称之为地址
指针就是地址
指针变量:是个变量,是个指针变量,
即这个变量用来存放一个地址编号
在32位平台下,地址总线是32位的,
所以地址是32位编号,所以指针变量是32位的即4个字节
指针的相关概念
注意:
无论什么类型的地址,都是存储单元的编号,在32位平台下都是4个 字节,即任何类型的指针变量都是4个字节大小
对应类型的指针变量,只能存放对应类型的变量的地址
举例:整型的指针变量,只能存放整型变量的地址
13
指针的相关概念
14
扩展:
字符变量 char ch; ch占1个字节,
它有一个地址编号,
这个地址编号就是ch的地址
整型变量 int a; a占4个字节,
它占有4个字节的存储单元,
有4个地址编号。
指针的定义方法
15
1.简单的指针
语法格式:数据类型 * 指针变量名;
例如:int * p;//定义了一个指针变量p
解释:在 定义指针变量的时候 * 是用来修饰变量的,说明变量p 是个指针变量。变量名是 p
2.关于指针的运算符
& 取地址 、 *取值
指针的定义方法
案例1:
16
指针的定义方法
案例2:
17
指针的定义方法
案例3:
18
指针变量的引用
对于指针变量的两个运算符:
&取地址运算符
*指针运算符
如果已执行了语句:pointer_1=&a;
&* pointer_1的含义是什么?
*&a的含义是什么?
(*pointer_1)++相当于a++。
19
指针的分类
20
按指针指向的数据的类型来分
字符指针:字符型数据的地址
char *p;//定义了一个字符指针变量,只能存放字符型数据的地址编号
char ch;
p= &ch;
短整型指针
short int *p;//定义了一个短整型的指针变量p,只能存放短整型变量的地址
short int a;
p =&a;
指针的分类
21
3. 整型指针
int *p;//定义了一个整型的指针变量p,只能存放整型变量的地址
int a;
p =&a;
注:多字节变量,占多个存储单元,每个存储单元都有地址编号,
c语言规定,存储单元编号最小的那个编号,是多字节变量的地址编号。
4. 长整型指针
long int *p;//定义了一个长整型的指针变量p,只能存放长整型变量的地址
long int a;
p =&a;
指针的分类
22
5:float 型的指针
float *p;//定义了一个float型的指针变量p,只能存放float型变量的地址
float a;
p =&a;
6:double型的指针
double *p;//定义了一个double型的指针变量p,只能存放double型变量的地址
double a;
p =&a;
指针的分类
23
7. 函数指针
8. 结构体指针
9. 指针的指针
10. 数组指针
总结:无论什么类型的指针变量,在32位系统下,都是4个字节,只能存放对应类型的变量的地址编号
指针和变量的关系
24
指针可以存放变量的地址编号
在程序中,引用变量的方法
直接通过变量的名称
int a;
a=100;
可以通过指针变量来引用变量
int *p;//在定义的时候,*不是取值的意思,是修饰的意思,修饰p是个指针变量
p=&a;//取a的地址给p赋值,p保存了a的地址,也可以说p指向了a
*p= 100;//在调用的时候*是取值的意思,*指针变量 等价于指针指向的变量
指针和变量的关系
25
运行结果:
输入100 200
运行结果为:
a=200 b=100
a=200 b=100
案例:
指针和数组元素之间的关系
26
变量存放在内存中,有地址编号,咱们定义的数组,
是多个相同类型的变量的集合,每个变量都占内存空间,
都有地址编号指针变量当然可以存放数组元素的地址。
int a[10];
//int *p =&a[0];
int *p;
p=&a[0];//
指针变量p保存了数组a中第0个元素的地址,即a[0]的地址
指针和数组元素之间的关系
27
2、数组元素的引用方法
方法1: 数组名[下标]
int a[10];
a[2]=100;
方法2:指针名加下标
int a[10];
int *p;
p=a;
p[2]=100;//因为p和a等价
方法3:通过指针运算加取值的方法来引用数组的元素
int a[10];
int *p;
p=a;
*(p+2)=100;//也是可以的,相当于a[2]=100
指针和数组元素之间的关系
28
案例:
指针和数组元素之间的关系
29
3、指针的运算
(1)指针可以加一个整数,往下指几个它指向的变量,结果还是个地址
前提:指针指向数组的时候,加一个整数才有意义
(2)两个相同类型指针可以比较大小
前提:只有两个相同类型的指针指向同一个数组的元素的时候,
比较大小才有意义,指向前面元素的指针小于指向后面元素的指针
指针和数组元素之间的关系
30
(3)两个相同类型的指针可以做减法
前提:必须是两个相同类型的指针指向同一个数组的元素的时候,
做减法才有意义,做减法的结果是,两个指针指向的中间有多少个元素
(4)两个相同类型的指针可以相互赋值
注意:只有相同类型的指针才可以相互赋值(void *类型的除外)
指针数组
31
1、指针和数组的关系
(1)指针可以保存数组元素的地址
(2)可以定义一个数组,数组中有若干个相同类型指针变量,这个数组被称为指针数组
指针数组的概念:
指针数组本身是个数组,是个指针数组,是若干个相同类型的指针变量构成的集合
指针数组
32
2、指针数组的定义方法:
类型说明符 * 数组名 [元素个数];
例如:int * p[10];//定义了一个整型的指针数组p,有10个元素p[0]~p[9],
每个元素都是int *类型的变量
int a;
p[1]=&a;
int b[10];
p[2]=&b[3];
p[2]、*(p+2)是等价的,都是指针数组中的第2个元素
指针数组
33
3、指针数组的分类
字符指针数组 char *p[10]、
短整型指针数组 short *p[10]、
整型的指针数组 int *p[10]、
长整型的指针数组 long *p[10]、
float型的指针数组 float *p[10]、
double型的指针数组 double *p[10]、
结构体指针数组 struct stu *p[10]、
函数指针数组 void *p[10]()
指针数组
34
案例:
指针的指针
35
指针的指针,即指针的地址
定义一个指针变量本身指针变量
占4个字节,指针变量也有地址编号
例如:
int a; int *p;
p=&a;
*p <==> a
int **q;
q=&p;
*q <==> p
**q <==> *p <==> a
int ***m;
m=&q;
*(*(*m)) <==> a
字符串和指针
36
字符串的概念:
字符串就是以’\0’结尾的若干的字符的集合
字符串的存储形式: 数组、字符串指针、堆
(1)char string[100] = “I love C!”
定义了一个字符数组string,用来存放多个字符,并且用”I love C!”给string数 组初始化,字符串“I love C!”存放在string中
字符串和指针
37
字符串的存储形式: 数组、字符串指针、堆
(2)char *str = “I love C!”
定义了一个指针变量str,只能存放字符地址编号,所以说I love C! 这个字符串中 的字符不能存放在str指针变量中。str只是存放了字符I的地址编号,
“I love C!”存放在文字常量区
(3)char *str =(char*)malloc(10*sizeof(char));
动态申请了10个字节的存储空间,首地址给str赋值。
strcpy(str,"I love C");//将字符串“Ilove C!”拷贝到str指向的内存里
字符串和指针
38
字符数组:
在内存(栈、静态全局区)中开辟了一段空间存放字符串
字符串指针:
在文字常量区开辟了一段空间存放字符串,将字符串的首地址付给str
堆:
使用malloc函数在堆区申请空间,将字符串拷贝到堆区
数组指针
39
1、二维数组
二维数组,有行,有列。二维数组可以看成有多个一维数组构成的,
是多个一维数组的集合,可以认为二维数组的每一个元素是个一维数组。
例:int a[3][5];
定义了一个3行5列的一个二维数组。
可以认为二维数组a由3个一维数组构成,每个元素是一个一维数组。
数组指针
40
数组的名字是数组的首地址,是第0个元素的地址,是个常量,
数组名字加1指向下个元素
二维数组a中 ,a+1 指向下个元素,即下一个一维数组,即下一行。
int a[3][5];
printf("a=%p\n",a);
printf("a+1=%p\n",a+1);
运行结果:
a=0x0060FE64
a+1=0x0060FE78
数组指针
41
2、数组指针的概念:
本身是个指针,指向一个数组,加1跳一个数组,即指向下个数组。
3、数组指针的定义方法
指向的数组的类型(*指针变量名)[指向的数组的元素个数]
例如:int (*p)[5];
定义了一个数组指针变量p,p指向的是整型的有5个元素的数组
p+1 往下指5个整型,跳过一个有5个整型元素的数组。
数组指针
42
案例1:
数组指针
43
案例2:
数组指针
44
4、各种数组指针的定义
(1)一维数组指针,加1后指向下个一维数组
例如:int(*p)[5] ;
配合每行有5个int型元素的二维数组来用
(2)二维数组指针,加1后指向下个二维数组
例如:int(*p)[4][5];
配合三维数组来用,三维数组中由若干个4行5列二维数组构成
(3)三维数组指针……
数组指针
45
案例:
数组指针
46
5、容易混淆的内容:
指针数组:是个数组,有若干个相同类型的指针构成的集合
int *p[10];
数组p有10个int *类型的指针变量构成,分别是p[0] ~p[9]
数组指针:本身是个指针,指向一个数组,加1跳一个数组
int (*p)[10];
P是个指针,p是个数组指针,p加1指向下个数组,跳10个整形。
数组指针
47
6、数组名字取地址:变成 数组指针
一维数组名字取地址,变成一维数组指针,即加1跳一个一维数组
int a[10];
a+1 跳一个整型元素,是a[1]的地址
a和a+1 相差一个元素,4个字节
&a就变成了一个一维数组指针,是 int(*p)[10]类型的。
(&a) +1 和&a相差一个数组即10个元素即40个字节。
数组指针
48
7、数组名字和指针变量的区别
例如:int a[10]; int *p; p=a;
相同点:
a是数组的名字,是a[0]的地址,p=a即p也保存了a[0]的地址,
即a和p都指向a[0],所以在引用数组元素的时候,a和p等价
数组指针
49
7、数组名字和指针变量的区别
例如:int a[10]; int *p; p=a;
不同点:
(1)a是常量、p是变量,可以用等号’=’给p赋值,但是不能用等号给a赋值
(2)对a取地址,和对p取地址结果不同,因为a是数组的名字,
所以对a取地址结果为数组指针。p是个指针变量,
所以对p取地址(&p)结果为指针的指针。
数组指针
50
数组指针
51
8、多维数组中指针的转换
在二维数组中,行地址 取 * 不是取值得意思,而是指针降级的意思,由行地址(数组
指针)变成这一行第0个元素的地址。取*前后还是指向同一个地方,但是指针的类型不
一样了
指针和函数的关系
52
1、指针作为函数的参数
可以给一个函数传一个 整型、字符型、浮点型的数据,
也可以 给函数传一个地址。
例:int num; scanf("%d",&num);
分类:
传数值
传地址
传数组
指针和函数的关系
53
传数值:
实参:调用函数时传的参数。
形参:定义被调函数时,函数名后边括号里的数据
结论:给被调函数传数值,只能改变被调函数形参的值,不能改变主调函数实参的值
指针和函数的关系
54
传地址:
结论:
调用函数的时候传变量的地址,在被调函数中通过*+地址来改变主调函数中的变量的值
指针和函数的关系
55
传数组:
给函数传数组的时候,无法将数组的内容作为整体传进去。只能传数组的地址
指针和函数的关系
56
传一维数组的地址:
指针和函数的关系
57
传二维数组的地址:
指针和函数的关系
58
传指针数组:
指针和函数的关系
59
2、指针保存函数的地址--函数指针
定义的函数,在运行程序的时候,会将函数的指令加载到内存
的代码段。所以函数也有起始地址。
c语言规定:函数的名字就是函数的首地址,即函数的入口地址
定义一个指针变量,来存放函数的地址。 这个指针变量就是函数指针变量
指针和函数的关系
函数指针变量的定义方法
返回值类型(*函数指针变量名)(形参列表);
例如:int(*p)(int,int);
定义了一个函数指针变量p,p指向的函数
必须有一个整型的返回值,有两个整型参数。
60
指针和函数的关系
61
3、函数指针数组
int(*p[10])(int,int);
定义了一个函数指针数组,有10个元素p[0] ~p[9],
每个元素都是函数指针变量,
指向的函数,必须有整型的返回值,两个整型参数。
指针和函数的关系
62
函数指针最常用的地方--给函数传参
经常容易混淆的指针
63
第一组:
1、 int *a[10];
这是个指针数组,数组a中有10个整型的指针变量
a[0]~a[9]
2、int (*a)[10];
数组指针变量,它是个指针变量。它占4个字节,存地址编号。
它指向一个数组,它加1的话,指向下个数组。
3、 int **p;
这个是个指针的指针,保存指针变量的地址。
它经常用在保存指针的地址:
经常容易混淆的指针
64
第二组:
1、int *f(void);
注意:*f没有用括号括起来
它是个函数的声明,声明的这个函数返回值为int *类型的。
2、int (*f)(void);
注意*f用括号括起来了,*修饰f说明,f是个指针变量。
f是个函数指针变量,存放函数的地址,它指向的函数,
必须有一个int型的返回值,没有参数。
特殊指针
65
1、空类型的指针(void *)
char * 类型的指针指向char型的数据
int * 类型的指针指向int型的数据
float* 类型的指针指向float型的数据
void * 难道是指向void型的数据吗?
不是,因为没有void类型的变量
void* 通用指针,任何类型的指针都可以给void*类型的指针变量赋值
int *p;
void *q;
q=p 是可以的,不用强制类型转换
特殊指针
66
2、NULL
空指针:
char *p=NULL;
可以认为p哪里都不指向,也可以认为p指向内存编号为0的存储单位。
在p的四个字节中,存放的是0x00 00 00 00
一般NULL用在给指针初始化。
THANK YOU鍛戒护绡囨病鏈変唬鐮 知识点1【函数指针 作为 函数的形参】
知识点2【malloc函数 和 free函数】
malloc函数
free函数
案例1:从堆区申请 一个int空间
案例2:从堆区申请一个数组 数组的大小 由用户决定
案例3:从堆区申请一个数组 数组的大小 由用户决定(函数版本)
知识点3【calloc函数】
案例:
知识点4【realloc动态追加或减少空间】
知识点5【堆区空间使用的注意事项】
知识点6【防止多次释放】
知识点7【字符串处理函数】
1、strlen测量字符换长度
作业:自定义一个my_strlen函数测量字符串长度(不能在函数里面使用strlen)
2、strcpy strncpy字符串拷贝(重要)
strcpy
实现strcpy
strncpy
3、strcat字符串的拼接
strncat拼接前n个
作业:自定义my_strcat函数 不许使用 strcat
4、strcmp字符串的比较 (整个字符串的比较)
strncmp 字符串局部比较
5、strchr字符查找函数
6、strstr字符串查找
知识点1【函数指针 作为 函数的形参】
案例1:
1 #include
2 int my_add(int a,int b)
3 {
4 return a+b;
5 }
6 int my_sub(int a,int b)
7 {
8 return a‐b;
9 }
10 int my_mul(int a,int b)
11 {
12 return a*b;
13 }
14
15 //定义一个函数 实现上述函数的功能
16
17 int my_calc(int a,int b, int (*fun_pointer)(int,int) )
18 {
19 return fun_pointer(a,b);
20 }
21 void test01()
22 {
23 printf("%d\n",my_calc(10,20,my_add));
24 printf("%d\n",my_calc(10,20,my_sub));
25 printf("%d\n",my_calc(10,20,my_mul));
26
27 }
28 int main(int argc,char *argv[])
29 {
30 test01();
31 return 0;
32 }
运行结果:
知识点2【malloc函数 和 free函数】
malloc函数
1 #include
2 void *malloc(unsigned int num_size);
3 形参:num_size需要申请空间大小的字节数。
4 返回值:
5 成功:返回空间的起始地址
6 失败:NULL
7 特点:
8 1、对于malloc的返回值 一般要强制类型转换
9 2、malloc申请的空间 内容不确定 一般使用memset进行清空
10 3、多次调用malloc 第1次malloc 和 第2次malloc的地址不一定连续
free函数
1 void free(void *addr);
2 释放堆区空间
案例1:从堆区申请 一个int空间
1 void test02()
2 {
3 int *addr = NULL;
4
5 addr = (int *)malloc(sizeof(int));
6 if(addr == NULL)//申请失败了
7 {
8 printf("malloc err\n");
9 return;
10 }
11
12 printf("*addr=%d\n", *addr);//不确定的值
13
14 //对堆区空间 清0
15 memset(addr, 0, sizeof(int));
16 printf("*addr=%d\n", *addr);//0
17
18 //对addr的空间 就行写 或 读
19 *addr = 1000;//写
20
21 printf("*addr=%d\n", *addr);//1000 读
22
23
24 //释放堆区空间 空间使用权限的回收 是否对空间内容清0 这是不确定的
25 free(addr);
26 }
27 int main(int argc,char *argv[])
28 {
29 test02();
30 return 0;
31 }
运行结果:
案例2:从堆区申请一个数组 数组的大小 由用户决定
1 1、从键盘获取 用户要申请的数组大小
2 2、根据大小 从堆区申请空间
3 3、对空间的读写操作
4 4、释放该空间
1 void test03()
2 {
3 int n = 0;
4 int i=0;
5 int *arr=NULL;
6
7 //1、获取用户大小
8 printf("请输入元素的个数:");
9 scanf("%d", &n);
10
11 //2、根据大小从堆区申请空间
12 arr = (int *)malloc(n*sizeof(int));
13 if(NULL == arr)
14 {
15 //perror 错误输出
16 perror("mallac");
17 return;
18 }
19
20 //对arr的读写操作
21 printf("请输入%d个int数据\n",n);
22 for(i=0;i23 {
24 scanf("%d", arr+i);
25 }
26
27 //遍历数组
28 for(i=0;i29 {
30 printf("%d ", arr[i]);
31 }
32 //释放空间
33 free(arr);
34 }
35 int main(int argc,char *argv[])
36 {
37 test03();
38 return 0;
39 }
案例3:从堆区申请一个数组 数组的大小 由用户决定(函数版本)
1 int get_n(void)
2 {
3 int n = 0;
4 printf("请输入元素的个数:");
5 scanf("%d", &n);
6 return n;
7 }
8 int* get_addr(int n)
9 {
10 return (int *)malloc(n*sizeof(int));
11 }
12 void my_input_array(int *arr, int n)
13 {
14 int i=0;
15 //记得将arr指向的空间清0
16 memset(arr,0,n*sizeof(int));
17
18 //获取键盘输入
19 printf("请输入%d个int数据\n",n);
20
21 for(i=0;i22 {
23 scanf("%d", arr+i);
24 }
25 }
26 void my_print_array(int *arr, int n)
27 {
28 int i=0;
29 for(i=0;i30 {
31 printf("%d ", arr[i]);
32 }
33 printf("\n");
34 }
35 void test04()
36 {
37 int *arr=NULL;
38 int n = 0;
39
40 //得到用户输入的元素个数
41 //1、获取用户大小
42 n = get_n();
43
44 //定义函数 给arr申请堆区空间
45 arr = get_addr(n);
46 if(arr == NULL)
47 {
48 perror("get_addr");
49 return;
50 }
51
52 //对空间读写操作
53 my_input_array(arr, n);
54
55
56 //对空间数组遍历
57 my_print_array(arr, n);
58
59 //释放空间
60 free(arr);
61
62 }
63 int main(int argc,char *argv[])
64 {
65 test04();
66 return 0;
67 }
运行结果:
知识点3【calloc函数】
1 #include
2 void * calloc(size_t nmemb,size_t size);
3 参数:
4 1、nmemb 申请的数据块数
5 2、size 每一块大小
6 3、所以申请总大小=nmemb * size
7 返回值:
8 成功:返回空间的起始地址
9 失败:返回NULL
10 特点:申请的空间自动清零
案例:
1 void test04()
2 {
3 int n = 0;
4 int i=0;
5 int *arr=NULL;
6
7 //1、获取用户大小
8 printf("请输入元素的个数:");
9 scanf("%d", &n);
10
11 //2、根据大小从堆区申请空间
12 #if 0
13 arr = (int *)malloc(n*sizeof(int));
14 if(NULL == arr)
15 {
16 //perror 错误输出
17 perror("mallac");
18 return;
19 }
20 memset(arr,0,n*sizeof(int));//清零
21 #endif
22
23 #if 1
24 arr=(int *)calloc(n, sizeof(int));//自动清零 不需要使用memset
25 if(NULL == arr)
26 {
27 //perror 错误输出
28 perror("calloc");
29 return;
30 }
31 #endif
32 //对arr的读写操作
33 printf("请输入%d个int数据\n",n);
34 for(i=0;i35 {
36 scanf("%d", arr+i);
37 }
38
39 //遍历数组
40 for(i=0;i41 {
42 printf("%d ", arr[i]);
43 }
44
45 //释放空间
46 free(arr);
47
48 }
知识点4【realloc动态追加或减少空间】
1 #include
2 void* realloc(void *s, unsigned int newsize);
功能:
在原先s指向的内存基础上重新申请内存,新的内存的大小为 new_size 个 字节,如果
原先内存后面有足够大的空间,就追加,如果后边的内存不 够用,则realloc函数会在
堆区找一个newsize个字节大小的内存申请,将原先内存中的内容拷贝过来,然后释放原先
的内存,最后返回新内存的地址。
参数:s:原先开辟内存的首地址 newsize:新申请的空间的大小
返回值:新申请的内存的首地址
注意:一定要保存 realloc的返回值
1 void test06()
2 {
3 int *arr = NULL;
4 int n = 0;
5 int i=0;
6 int n_new = 0;
7
8 //1、获取用户大小
9 printf("请输入元素的个数:");
10 scanf("%d", &n);
11
12 arr=(int *)calloc(n, sizeof(int));//自动清零 不需要使用memset
13 if(NULL == arr)
14 {
15 //perror 错误输出
16 perror("calloc");
17 return;
18 }
19
20 printf("请输入%d个int数据\n",n);
21 for(i=0;i22 {
23 scanf("%d", arr+i);
24 }
25
26 //遍历数组
27 for(i=0;i28 {
29 printf("%d ", arr[i]);
30 }
31
32 //再追加5个元素
33 printf("请输入新增的元素个数:");
34 scanf("%d", &n_new);
35 arr = (int *)realloc(arr, (n+n_new)*sizeof(int));
36
37 printf("请输入新增的%d个int数据\n",n_new);
38 for(i=n;i<(n+n_new); i++)
39 {
40 scanf("%d",arr+i);
41 }
42
43 for(i=0;i<(n+n_new);i++)
44 {
45 printf("%d ", arr[i]);
46 }
47 printf("\n");
48
49 free(arr);
50 }
51 int main(int argc,char *argv[])
52 {
53 test06();
54 return 0;
55 }
运行结果:
知识点5【堆区空间使用的注意事项】
1 void test08()
2 {
3 int *p2 = NULL;
4 int *p3 = NULL;
5 //1、指向堆区空间的指针变量 不要随意的更改指向
6 int *p=(int *)calloc(1,sizeof(int));
7 int num = 10;
8 p = #//p指向num 导致calloc申请的空间泄露
9
10 //2、不要操作已经释放的空间
11 p2 = (int *)calloc(1,sizeof(int));
12 *p2 = 1000;
13 //释放该空间
14 free(p2);
15 printf("*p2 = %d\n", *p2);//不确定
16
17 //3、不要对堆区空间重复释放
18 p3 = (int *)calloc(1,sizeof(int));
19 free(p3);
20 free(p3);//多次释放
21 }
知识点6【防止多次释放】
1 void test09()
2 {
3 int *p = (int *)calloc(1,sizeof(int));
4
5 if(p != NULL)//防止多次释放
6 {
7 free(p);
8 p=NULL;
9 }
10
11
12 if(p != NULL)
13 {
14 free(p);
15 p=NULL;
16 }
17 }
知识点7【字符串处理函数】
只要是以str开头的函数 都是遇到'\0'结束
#include
1、strlen测量字符换长度
1 size_t strlen(const char *s)
2 //s:被测量的字符串首元素地址
3 //返回值为 字符串的长度 不包含'\0'
4 //const char *s strlen函数 不会通过s修改s指向的空间内容
案例:
1 void test01()
2 {
3 char buf1[128]="hehehe";
4 char buf2[]="hehehe";
5 char buf3[]="hehe\0he";
6
7 // \123 代表一个字符 \hhh 八进制转义h: 0~7
8 //\\表示'\'
9 char buf4[]="hehe\123\\he";
10
11 //\\x2f表示一个字符 \xdd 十六进制转义 d:0~9 a~f
12 char buf5[]="hehe\x2fhe";
13
14
15 printf("%d\n",sizeof(buf1));//126
16 printf("%d\n",strlen(buf1));//6
17
18 printf("%d\n",sizeof(buf2));//7
19 printf("%d\n",strlen(buf2));//6
20
21 printf("%d\n",sizeof(buf3));//8
22 printf("%d\n",strlen(buf3));//4
23
24 printf("%d\n",sizeof(buf4));//9
25 printf("%d\n",strlen(buf4));//8
26 printf("%s\n",buf4);
27
28 printf("%d\n",sizeof(buf5));//8
29 printf("%d\n",strlen(buf5));//7
30
31 char buf6[]="\0hehe\0hehe";
32 printf("%d\n",strlen(buf6));//0
33 return;
34 }
作业:自定义一个my_strlen函数测量字符串长度(不能在函数里面使用
strlen)
1 int my_strlen(const char *s);
2、strcpy strncpy字符串拷贝(重要)
strcpy
原型:char *strcpy( char *dest, const char *src )
功能:把src所指向的字符串复制到dest所指向的空
间中
返回值:返回dest字符串的首地址
注意:遇到'\0'会结束,只是'\0'也会拷贝过去
1 void test02()
2 {
3 char src[]="hello string";
4 //保证dst足够大
5 char dst[128]="";
6
7 strcpy(dst,src);
8
9 printf("dst=%s\n",dst);//"hello string"
10 }
1 void test02()
2 {
3 char src[]="hello\0string";
4 //保证dst足够大
5 char dst[128]="";
6
7
8 strcpy(dst,src);
9
10 printf("dst=%s\n",dst);//"hello"
11 }
实现strcpy
1 char *my_strcpy(char *dst,const char *src)
2 {
3 #if 0
4 do
5 {
6 *dst = *src;
7 dst++;
8 src++;
9 }while(*src != '\0');
10 *dst='\0';
11 #endif
12 while(*dst++ = *src++);
13 }
14 void test02()
15 {
16 char src[]="hello\0string";
17 //保证dst足够大
18 char dst[128]="";
19 my_strcpy(dst,src);
20
21 printf("dst=%s\n",dst);//"hello"
22 }
strncpy
原型:char *strncpy( char *dest, const char *src,int
num)
功能:把src指向字符串的前num个复制到dest所指
向的空间中
返回值:返回dest字符串的首地址
注意:'\0'不拷贝
1 void test03()
2 {
3 char src[]="hello string";
4 //保证dst足够大
5 char dst[128]="";
6
7 strncpy(dst,src,3);
8 printf("dst=%s\n",dst);//"hel"
9 }
1 void test03()
2 {
3 char src[]="hello";
4 //保证dst足够大
5 char dst[128]="";
6
7 strncpy(dst,src,30);
8 printf("dst=%s\n",dst);//"hello"
9 }
3、strcat字符串的拼接
1 char *strcat(char *dest, const char *src);
2 将src的字符串 拼接到 dst的末尾(dst第一个'\0'的位置)
1 void test04()
2 {
3 char src[]="world";
4 char dst[128]="hello";
5
6 strcat(dst,src);
7
8 printf("%s\n",dst);//"helloworld"
9 }
1 void test04()
2 {
3 char src[]="wor\0ld";
4 char dst[128]="hello";
5
6 strcat(dst,src);
7
8 printf("%s\n",dst);//"hellowor"
9 }
strncat拼接前n个
1 void test04()
2 {
3 char src[]="world";
4 char dst[128]="hello";
5
6 strncat(dst,src,2);
7
8 printf("%s\n",dst);//"hellowo"
9 }
作业:自定义my_strcat函数 不许使用 strcat
1 char *my_strcat(char *dest, const char *src);
4、strcmp字符串的比较 (整个字符串的比较)
1 int strcmp(const char *s1, const char *s2);
2 功能:将s1和s2指向的字符串 逐个字符比较
3 返回值:
4 >0: 表示s1 > s2
5 <0: 表示s1 < s2
6 ==0: 表示s1 == s2
1 void test05()
2 {
3 char s1[]="hehe haha";
4 char s2[]="hehe xixi";
5 char s3[]="hehe haha";
6
7 if(strcmp(s1,s2) >0 )
8 {
9 printf("s1 > s2\n");
10 }
11 else if(strcmp(s1,s2) <0 )
12 {
13 printf("s1 < s2\n");
14 }
15 else if(strcmp(s1,s2) == 0)
16 {
17 printf("s1 == s2\n");
18 }
19
20 if(strcmp(s1,s3) >0 )
21 {
22 printf("s1 > s3\n");
23 }
24 else if(strcmp(s1,s3) <0 )
25 {
26 printf("s1 < s3\n");
27 }
28 else if(strcmp(s1,s3) == 0)
29 {
30 printf("s1 == s3\n");
31 }
32 }
运行结果:
strncmp 字符串局部比较
1 void test05()
2 {
3 char s1[]="hehe haha";
4 char s2[]="hehe xixi";
5
6
7 if(strncmp(s1,s2,4) >0 )
8 {
9 printf("s1 > s2\n");
10 }
11 else if(strncmp(s1,s2,4) <0 )
12 {
13 printf("s1 < s2\n");
14 }
15 else if(strncmp(s1,s2,4) == 0)
16 {
17 printf("s1 == s2\n");
18 }
19
20 }
运行结果:
5、strchr字符查找函数
1 char *strchr(const char *s, int c);
2 //从字符串s中查找第一次c字符出现的地址
3 //没有找到 返回NULL
1 void test06()
2 {
3 char str[]="www.";
4 char *ret = NULL;
5
6 ret = strchr(str,'o');
7 if(ret != NULL)
8 {
9 *ret = '#';
10 }
11 printf("str=%s\n",str);//"www.1000ph#";
12 }
(了解)
1 void test06()
2 {
3 char str[]="www.";
4 char *ret = NULL;
5
6 while((ret = strchr(str,'o')) && (*ret = '#') );
7
8 printf("str=%s\n",str);//"www.1000ph#ne.c#m";
9 }
重要 啥都写 了解
6、strstr字符串查找
1 char *strstr(const char *s1, const char *s2);
2 //从s1中查找字符串s2 返回第一次s2出现的地址
3 //查找失败 返回NULL
1 void test07()
2 {
3 char s1[]="www.sex.777.";
4 char s2[]="sex";
5
6 char *ret = NULL;
7 ret = strstr(s1,s2);
8 if(ret == NULL)
9 {
10 return;
11 }
12 printf("%s\n",ret);//"sex.777."
13 }
1 void test07()
2 {
3 char s1[]="www.sex.777.";
4 char s2[]="sex";
5
6 char *ret = NULL;
7
8 while(1)
9 {
10 ret = strstr(s1,s2);
11 if(ret == NULL)
12 {
13 break;
14 }
15 memset(ret,'#', 3);
16 }
17
18 printf("%s\n",s1);//"www.###.777.###.com"
19
20 }(共29张PPT)
标 题
1
链表的基本概念
2
3
单向链表的操作
其它链表的概念及应用
目录
数据链表链表的基本概念问题假如:做一个班级信息管理系统,统计班级学生的信息而我们事先不知道班级人数,或者知道人数,但是中间人员可能发生变化:比如有新同学加入,有同学请假,又或者我们需要统计班级的平均成绩等等链表的基本概念问题假如:要做一个类似QQ、飞秋类似的通信软件,其中有一个功能,类似用户上下线检测:有新的用户上线、下线实时更新显示,可以实时查询在线状态、按姓名排序等以上问题如何使用学过的C语言知识处理呢?链表的基本概念使用数组远远不能达到我们的要求因为数组必须实现确定大小,不能实现动态申请、释放使用malloc动态内存分配也无法实现malloc申请的空间,不能实现局部申请、释放链表的基本概念这里我们学习一种很强大也很重要的数据结构——链表定义:链表是一种物理存储上非连续,数据元素的逻辑顺序通过链表中的指针链接次序,实 现的一种线性存储结构。链表的基本概念特点:链表由一系列节点(链表中每一个元素称为节点)组成,节点在运行时动态生成(malloc),每个节点包括两个部分:一个是存储数据元素的数据域另一个是存储下一个节点地址的指针域。链表的基本概念我们假设把一个链表比喻成一串灯笼每个灯笼相当于连表中的一个节点灯笼上的字相当于链表的数据域灯笼下面的钩子,相当于链表的指针域链表的基本概念灯笼通过钩子一个个串链起来,链表通过指针域链起来(指针存放下一个节点的地址)我们可以根据需要增加、减少、排列灯笼,类似的根据需要调整链表节点的位置链表的基本概念链表的构成:链表由一个个节点构成,每个节点一般采用结构体的形式组织。typedefstructstudent{int num;float score;struct student *next;}STU;链表节点分为两个域数据域:存放各种实际的数据,如:num、score等指针域:存放下一节点的首地址,如:next等.链表的结构链表通过在每个结构体变量(节点)中设置指向下一个变量的指针域,并存入下一个变量的地址,实现将一组结构体变量链成一条链表链表中的每个节点的单元一般都没有名字,它们是通过动态分配函数生成的,并由上一个节点来记录他的位置链表的结构链表的一般结构如下:链表的基本概念特点链表作为一个线性存储结构,链表的使用比数组更加灵活(前面所讲的数组都是在静态存储区中定义的数组).构造一个链表时不一定在程序中指定链表的长度(节点的个数),可以在程序的运行过程中动态的生成一个链表.链表使用完后可以通过调用free释放节点的方式完成对整个链表空间的释放.链表的构建我们以一个宿舍的信息管理系统为例,学习一个链表的创建过程及其它操作按照初始人数依次输入每个学生的信息,并建立连接关系(创建链表)对整个链表的信息进行必要的操作比如查找信息(查找链表)、修改信息(替换链表)、加入新的学生信息(插入链表)、删除旧信息(删除链表)、排列顺序(排列链表)等等.链表的构建链表的构建构建算法:首先申请一个链表节点,然后为该节点成员赋值,最后将链表节点添加到链表中链表节点的添加有多种形式,我们简单介绍两种添加到链表尾部:顺序创建,新加入的节点放在链表尾部添加到链表头部:逆序创建,新加入的节点放在链表头部链表的构建添加到链表尾部—分两种情况:当链表为空时,将链表头直接指向待添加节点(该节点成为了链表的第一个节点);链表不为空时,首先遍历链表找到链表尾节点,然后将待添加节点挂接到尾节点上.creat_link_order.c链表的构建添加到链表头部—分两种情况:链表不为空时,首先将之前的第一个链表节点挂接到新插入的节点上然后将链表头指向新插入的节点,creat_link_reversed.c链表的遍历、查找、释放遍历链表:遍历输出链表所有节点得到链表第一个节点的地址,即head的值设一个临时指针变量p_mov,指向第一个节点head,即可获取p_mov所指节点的信息使p_mov后移一个节点,即可访问下一节点,直到链表的尾节点(注意结尾判断条件)链表的遍历、查找、释放查找节点:按照指定关键字查找所需节点得到链表第一个节点的地址,即head的值设一个临时指针变量p_mov,指向第一个节点head,即可获取p_mov所指节点的信息链表的遍历、查找、释放比较是否是要查找的节点是,则返回相应节点地址,停止查找不是,使p_mov后移一个节点,即可访问下一节点,直到链表的尾节点(注意结尾判断条件),最后找不到返回NULL链表的释放释放链表同遍历链表类似,区别在于p_mov每指向某个节点后都将该节点释放释放前要先保存下一个节点,释放后备份恢复给p_mov,否则释放了当前节点,下一个节点的地址就将失去依次将所有节点释放后,最后返回NULL(标示释放完毕)链表的删除删除链表节点删除是将某一节点从链中摘除,将待删节点与其前一节点解除联系(中间或尾部)或本阶段删除(头节点),并释放相应空间(free)删除的第一步是找到要删除的节点,同查找算法,如果找不到或链表为空,提示未找到找到后,根据情况删除此节点,分为两种情况:第一个节点,后面节点链表的删除被删除节点是第一个节点:只需使head指向第二个节点即可链表的删除被删节点不是第一个节点:使被删节点的前一节点指向被删节点的后一节点即可链表的插入插入链表(有序插入)在一个链表的指定位置插入节点,要求链表本身必须是已按某种规律排好序的,例如:例如:在学生数据链表中,按学号顺序插入一个节点.链表的插入分为四种情况:原链表为空:只需使head指向被插节点即可.在第一个节点之前插入:使head指向被插节点,被插节点的next指向原来的第一节点.在中间位置插入:使插入位置的前一节点的next指向被插节点,使被插节点的next指向插入位置的后一节点.在表末插入,使链表尾节点next指向被插节点,被插节点next置为NULL .链表排序、逆序排序当链表本身是无序的时候我们需要对链表的所有数据进行排序算法同:冒泡法、选择法逆序将链表的所有节点逆序存放,原来的头节点变为结尾,原来的尾节点,变为头节点链表的插入单向循环双向链表双向循环共享链表
THANK YOU鐜??绡囨病鏈変唬鐮 (共18张PPT)
动态内存申请
动态内存申请相关概念
静态分配和动态分配
动态内存申请相关函数
内存泄漏
1
2
3
4
动态内存申请
动态内存申请
在数组一章中,介绍过数组的长度是预先定义好的,在整个程序中固定不变;
但是在实际的编程中,往往会发生这种情况,即所需的内存空间取决于实际输入的数据,而无法预先确定
为了解决上述问题,C语言提供了一些内存管理函数,这些内存管理函数可以按需要动态的分配内存空间,也可把不再使用的空间回收再次利用.
3
动态内存申请
静态分配
在程序编译或运行过程中,按事先规定大小分配内存空间的分配方式.
必须事先知道所需空间的大小.
分配在栈区或全局变量区,一般以数组的形式
按计划分配.
4
动态内存申请
动态分配
在程序运行过程中,根据需要大小自由分配所需空间.
按需分配.
分配在堆区,一般使用特定的函数进行分配.
5
动态内存申请
1、分配内存空间函数 malloc
函数原型: void *malloc(unsigned int num_bytes);
调用形式: (类型说明符*) malloc (size)
6
动态内存申请
功能说明:
在内存的动态存储区(堆区)中分配一块长度为size字节的连续区域, 用来存放类型说明符指定的类型。
函数原型返回void*指针,使用时必须做相应的强制类型转换
分配的内存空间内容不确定,一般使用memset初始化。
7
动态内存申请
8
返回值:
分配空间的起始地址 ( 分配成功 )
NULL ( 分配失败 )
注意:
1、在调用malloc之后,一定要判断一下,是否申请内存成功。
2、如果多次malloc申请的内存,第1次和第2次申请的内存不一定是连续的
动态内存申请
9
2、free 函数(释放内存函数)
头文件:#include
函数定义:void free(void *ptr)
函数说明:free函数释放ptr指向的内存。
注意ptr指向的内存必须是malloc calloc relloc动态申请的内存
动态内存申请
10
案例:
动态内存申请
11
3、calloc函数
#include
void * calloc(size_t nmemb,size_t size);
功能:在内存的堆中,申请nmemb 块,每块的大小为size个字节的连续区域
参数:size_t 实际是无符号整型,它是在头文件中,用typedef 定义出来的。
返回值:
返回 申请的内存的首地址(申请成功)
返回 NULL(申请失败)
例如:char *p=(char *)calloc(3, 100);
动态内存申请
12
malloc和calloc的区别
malloc和calloc函数都是用来申请内存的。
区别:
1) 函数的名字不一样
2) 参数的个数不一样
3) malloc申请的内存,内存中存放的内容是随机的,不确定的,
而calloc函数申请的内存中的内容为0
动态内存申请
13
4、realloc函数(重新申请内存)
#include
void* realloc(void *s, unsigned int newsize);
功能:在原先s指向的内存基础上重新申请内存,新的内存的大小为 new_size 个 字节,如果原先内存后面有足够大的空间,就追加,如果后边的内存不 够用,则relloc函数会在堆区找一个newsize个字节大小的内存申请,将原 先内存中的内容拷贝过来,然后释放原先的内存,最后返回新内存的地址。
参数:s:原先开辟内存的首地址 newsize:新申请的空间的大小
返回值:新申请的内存的首地址
动态内存申请
14
案例:
char *p;
p=(char *)malloc(100)
//在100个字节后面追加50个字节
p=(char *)realloc(p,150);
动态内存申请
15
5、内存泄漏
内存泄露的概念:
申请的内存,首地址丢了,找不了,再也没法使用了,也没法释放了,
这块内存就被泄露了。
动态内存申请
16
案例1:
动态内存申请
17
案例2:
THANK YOU知识点1【强制类型对齐】(了解)0-1
知识点2【位段 位域】
位段的使用:
无意义的位段:
应用场景:0-2
另起一个位段:(了解)
知识点3【结构体 与 共用体 的区别】
知识点3【共用体】
知识点4【枚举】(了解)1-1
知识点5【链表】
链表节点的定义:(结构体实现)
知识点6【静态链表】
知识点7【动态链表操作】
1、布局整个程序框架
2、链表插入节点 之 头部之前插入。
知识点1【强制类型对齐】(了解)0-1
步骤:
1、确定分单位:每一行应该分配的字节数,min(value,默
认分配单位)。
2、成员偏移量 = 成员自身类型的整数(0~n)倍
3、收尾工作 = 分配单位的整数(0~n)倍。
案例1:
1 #include
2 //指定对齐规则
3 #pragma pack(2)
4 typedef struct
5 {
6 char a;
7 int b;
8 short c;
9 }DATA1;
10 void test01()
11 {
12 printf("%d\n", sizeof(DATA1));//8
13 return;
14 }
知识点2【位段 位域】
1 /位段 一般只考虑unsigned int类型 也可以考虑unsigned char
2 typedef struct
3 {
4 unsigned char a:2;//a只占一个字节中的两位二进制位
5 unsigned char b:2;//b只占一个字节中的两位二进制位
6
7 //相邻位域 可以压缩 (压缩的位数 不能超过 成员自身大小)
8 unsigned char c:5;
9
10 }DATA2;
11
12 void test02()
13 {
14 printf("%d\n", sizeof(DATA2));//2
15 }
位段的使用:
1 /位段 一般只考虑unsigned int类型 也可以考虑unsigned char
2 typedef struct
3 {
4 unsigned char a:2;//a只占一个字节中的两位二进制位
5 unsigned char b:2;//b只占一个字节中的两位二进制位
6
7 //相邻位域 可以压缩 (压缩的位数 不能超过 成员自身大小)
8 unsigned char c:5;
9
10 }DATA2;
11
12 void test02()
13 {
14 DATA2 data;
15
16 printf("%d\n", sizeof(DATA2));//2
17
18 //位段 不能取地址
19 //printf("%p\n", &data.a);
20
21 //位段的赋值 不要超过 位段的大小 a:2
22 data.a = 6;//0110
23 printf("%u\n", data.a);//2
24 }
无意义的位段:
1 typedef struct
2 {
3 unsigned char a:2;//00
4 unsigned char :4;//无意义的位段(占有4位)
5 unsigned char b:2;//11
6 }DATA3;
7
8 void test03()
9 {
10 DATA3 data;
11 memset(&data, 0,1);
12
13 data.a = 0;//00
14 data.b = 3;//11
15
16 printf("%d\n",sizeof(DATA3));
17
18 printf("%#x\n", *(unsigned char *)&data);//1100 0000 //0xc0
19 }
说明:a是低位 b是高位
应用场景:0-2
另起一个位段:(了解)
1 typedef struct
2 {
3 unsigned char a:2;//00
4 unsigned char :0;//另起一个位段
5 unsigned char b:2;//11
6 }DATA4;
7 void test04()
8 {
9 printf("%d\n", sizeof(DATA4));
10 }
知识点3【结构体 与 共用体 的区别】
结构体:struct
所有的成员拥有独立的空间
1 struct stu
2 {
3 char a;
4 short b;
5 int c;
6 };// a b c成员有用独立的空间
共用体(联合体):union
所有的成员 共享 同一份空间。
1 union stu
2 {
3 char a;
4 short b;
5 int c;
6 };// a b c成员共享同一份空间
知识点3【共用体】
共用体的空间用 最大的成员 决定
1 union data
2 {
3 char a;
4 short b;
5 int c;
6 };
7 void test05()
8 {
9 union data A;
10 printf("%d\n",sizeof(union data));//4
11
12 A.a = 10;
13 A.b = 20;
14 A.c = 30;
15 //共用体是最后一次 赋值有效(不严谨)
16
17 printf("%d\n", A.a+A.b+A.c);//90
18 }
共用体 虽然 共有同一份空间 但是从空间读取的字节数 是有成员自身类型决定。
案例:
1 union data
2 {
3 char a;
4 short b;
5 int c;
6 };
7 void test05()
8 {
9 union data A;
10 printf("%d\n",sizeof(union data));//4
11
12 A.c = 0x01020304;
13 A.b = 0x0102;
14 A.a = 0x01;
15
16 printf("%#x\n", A.a+A.b+A.c);//0x01020203
17 }
知识点4【枚举】(了解)1-1
1 //枚举列表的值 默认是从0开始
2 enum POKER{ HONGTAO, HEITAO=30, MEIHUA=40, FANGKUAI };
3 void test06()
4 {
5 //poker_color 的取值为HONGTAO, HEITAO, MEIHUA, FANGKUAI中某一个
6 enum POKER poker_color = HEITAO;
7
8 printf("poker_color = %d\n", poker_color);//30
9 printf("HONGTAO = %d\n", HONGTAO);//0
10 printf("HEITAO = %d\n", HEITAO);//30
11 printf("MEIHUA = %d\n", MEIHUA);//40
12 printf("FANGKUAI = %d\n", FANGKUAI);//41
13
14 }
知识点5【链表】
数组的分类:便于 便来
静态数组:int arr[10] 数据过多造成 空间溢出 数据过小 空间浪费
动态数组:malloc calloc realloc 合理利用空间 不能快捷的 插入或删除数据(会涉及到大
量的数据移动)
链表节点的定义:(结构体实现)
1 typedef struct stu
2 {
3 //数据域(自定义)
4 int num;
5 char name[32];
6 float score;
7
8 //指针域
9 struct stu *next;//保存下一个节点的地址
10 }STU;
11
知识点6【静态链表】
1 typedef struct stu
2 {
3 //数据域(自定义)
4 int num;
5 char name[32];
6 float score;
7
8 //指针域 保存下一个节点的地址
9 struct stu *next;
10 }STU;
11
12 void test07()
13 {
14 //链表头
15 STU *head = NULL;
16 //遍历链表
17 STU *pb = NULL;
18 STU data1 = {100,"德玛", 59};
19 STU data2 = {101,"小炮", 89};
20 STU data3 = {102,"小法", 79};
21 STU data4 = {103,"盲僧", 99};
22 STU data5 = {104,"快乐风男", 39};
23
24 head = &data1;
25 data1.next = &data2;
26 data2.next = &data3;
27 data3.next = &data4;
28 data4.next = &data5;
29 data5.next = NULL;
30
31 pb = head;
32 while(pb != NULL)
33 {
34 printf("%d %s %f\n", pb‐>num,pb‐>name,pb‐>score);
35 pb=pb‐>next;//pb指向下一个节点
36 }
37 }
知识点7【动态链表操作】1-2
1、布局整个程序框架
main.c
1 #include
2
3 void stu_help(void);
4 int main(int argc,char *argv[])
5 {
6 stu_help();
7
8 while(1)
9 {
10 char cmd[32]="";
11 printf("请输入操作指令:");
12
13 scanf("%s",cmd);
14 if(strcmp(cmd,"help") == 0)
15 {
16 stu_help();
17 }
18 else if(strcmp(cmd,"insert") == 0)
19 {
20 printf("‐‐‐‐‐insert‐‐‐‐‐‐\n");
21 }
22 else if(strcmp(cmd,"print") == 0)
23 {
24 printf("‐‐‐‐‐print‐‐‐‐‐‐\n");
25 }
26 else if(strcmp(cmd,"search") == 0)
27 {
28 printf("‐‐‐‐‐search‐‐‐‐‐‐\n");
29 }
30 else if(strcmp(cmd,"delete") == 0)
31 {
32 printf("‐‐‐‐‐delete‐‐‐‐‐‐\n");
33 }
34 else if(strcmp(cmd,"free") == 0)
35 {
36 printf("‐‐‐‐‐free‐‐‐‐‐‐\n");
37 }
38 else if(strcmp(cmd,"quit") == 0)
39 {
40 break;
41 }
42
43 }
44 return 0;
45 }
46
47 void stu_help(void)
48 {
49 printf("################################\n");
50 printf("#help:打印帮助信息 #\n");
51 printf("#insert:插入链表节点 #\n");
52 printf("#print:遍历链表节点信息 #\n");
53 printf("#search:查询链表节点 #\n");
54 printf("#delete:删除链表节点 #\n");
55 printf("#free:释放链表 #\n");
56 printf("#quit:退出 #\n");
57 printf("################################\n");
58 return;
59 }
2、链表插入节点 之 头部之前插入。
原理分析图:
案例:
main.c
1 #include
2 #include
3 #include "link.h"
4 void stu_help(void);
5 int main(int argc,char *argv[])
6 {
7 //定义一个链表头 注意 一定要赋值为NULL
8 STU *head=NULL;
9
10 stu_help();
11
12 while(1)
13 {
14 char cmd[32]="";
15 printf("请输入操作指令:");
16
17 scanf("%s",cmd);
18 if(strcmp(cmd,"help") == 0)
19 {
20 stu_help();
21 }
22 else if(strcmp(cmd,"insert") == 0)
23 {
24 STU tmp;
25 printf("请输入需要插入的数据:");
26 scanf("%d %s %f",&tmp.num, tmp.name, &tmp.score);
27
28 //将tmp数据 插入到head所指向的链表中
29 head = insert_link(head, tmp);
30 }
31 else if(strcmp(cmd,"print") == 0)
32 {
33 print_link(head);
34 }
35 else if(strcmp(cmd,"search") == 0)
36 {
37 printf("‐‐‐‐‐search‐‐‐‐‐‐\n");
38 }
39 else if(strcmp(cmd,"delete") == 0)
40 {
41 printf("‐‐‐‐‐delete‐‐‐‐‐‐\n");
42 }
43 else if(strcmp(cmd,"free") == 0)
44 {
45 printf("‐‐‐‐‐free‐‐‐‐‐‐\n");
46 }
47 else if(strcmp(cmd,"quit") == 0)
48 {
49 break;
50 }
51
52 }
53 return 0;
54 }
55
56 void stu_help(void)
57 {
58 printf("################################\n");
59 printf("#help:打印帮助信息 #\n");
60 printf("#insert:插入链表节点 #\n");
61 printf("#print:遍历链表节点信息 #\n");
62 printf("#search:查询链表节点 #\n");
63 printf("#delete:删除链表节点 #\n");
64 printf("#free:释放链表 #\n");
65 printf("#quit:退出 #\n");
66 printf("################################\n");
67 return;
68 }
link.c
1 #include
2 #include//calloc
3 #include"link.h"
4 STU* insert_link(STU *head, STU tmp)
5 {
6
7 //1、从堆区申请一个待插入的节点空间
8 STU *pi = (STU *)calloc(1,sizeof(STU));
9 if(pi == NULL)
10 {
11 perror("calloc");
12 return head;
13 }
14
15 //2、将tmp的值 赋值 给*pi
16 *pi = tmp;
17 pi‐>next = NULL;//注意
18
19 //3、将pi插入到链表中
20 if(head == NULL)//链表不存在
21 {
22 head = pi;
23 //return head;
24 }
25 else//链表存在(头部之前插入)
26 {
27 //1、让pi 指向就的头
28 pi‐>next = head;
29
30 //2、head指向新的头节点
31 head = pi;
32
33 //return head;
34 }
35
36 return head;
37 }
38
39 void print_link(STU *head)
40 {
41 if(head == NULL)//链表不存在
42 {
43 printf("link not find\n");
44 return;
45 }
46 else
47 {
48 STU *pb = head;
49 while(pb != NULL)
50 {
51 printf("%d %s %f\n", pb‐>num, pb‐>name,pb‐>score);
52 //pb指向下一个节点
53 pb = pb‐>next;
54 }
55
56 }
57
58 return;
59 }
link.h
1 //防止头文件重复包含
2 #ifndef __LINK_H__
3 #define __LINK_H__
4 //链表节点类型 定义
5 typedef struct stu
6 {
7 //数据域
8 int num;
9 char name[32];
10 float score;
11
12 //指针域
13 struct stu *next;
14 }STU;
15
16 extern STU* insert_link(STU *head, STU tmp);
17 extern void print_link(STU *head);
18 #endif
19窗前明月光
疑似地上霜
举头望明月
低头思故乡知识点1【数据类型转换】
1、自动类型转换
知识点2【运算符】
1、算术运算符
2、逻辑运算符
知识点3【位运算符】二进制位操作(重要)
&:按位与
|:按位或
~:按位取反
^:按位异或
<< 左移运算符: 左边丢弃 右边补0
>> 右移运算符:
知识点4【?:】
知识点5【复合运算符】
知识点6【++ -- 运算符】
知识点6【优先级】分析已有的代码
知识点7【if语句】
1、如果只在乎项目的一个结果 选择 if
2、如果项目的只有两种结果 且不会同时出现。 请选择 if。。。。。。else......
3、如果一个项目 有多个结果 且 不同时 出现。选择if...else if...else if ...else
4、一个项目有多个结果 不确定 是否同时出现。
作业:键盘输入一个数 能否被3和7同时整除。
知识点8【switch选择语句】
作业:键盘输入1~7的的数 用switch判断今天是星期几
知识点1【数据类型转换】
1、自动类型转换
案例:有符号 和无符号的转换
1 void test02()
2 {
3 int data1 = ‐20;
4 unsigned int data2 = 10;
5 //有符号data1和无符号data2参加计算的时候
6 //会先将data1转换成无符号(‐20的补码很大的正数)
7 //很大的数 + 10 必然 >0
8 if(data1+data2 > 0)
9 {
10 printf(">0\n");
11 }
12 else if(data1+data2<0)
13 {
14 printf("<0\n");
15 }
16 }
运行结果:>0
案例:int double的转换
1 void test03()
2 {
3 int data1 = 10;
4 printf("%d\n",sizeof(data1+3.14));//8字节
5 }
6 int main(int argc,char *argv[])
7 {
8 test03();
9 return 0;
10 }
案例:char 和short的类型转换
1 void test04()
2 {
3 char ch = 'a';
4 short data = 20;
5
6 //由于char short自身字节数 过小 很容易溢出
7 //所以 只要char short参加运算 都会将自身转换成int
8 printf("%d\n", sizeof(ch + ch));//4
9 printf("%d\n", sizeof(ch + data));//4
10 printf("%d\n", sizeof(data + data));//4
11 }
案例:强制类型转换
1 void test05()
2 {
3 float x = 3.14f;
4 int j = 0;
5 //强制 类型转换 只是临时的转换 当前语句有效 在后面的语句中不会更改x的值
6 j = (int)x;
7 printf("j = %d,x = %f\n", j, x);//
8 }
9 int main(int argc,char *argv[])
10 {
11 test05();
12 return 0;
运行结果:
知识点2【运算符】
1、算术运算符
a /b (a,b可以为整数,也可以为浮点数 如果a b为整数 表示取整 如果 a b只要有一个为浮
点数 则表示除法运算) 取整 %取余 必须为整数
1 void test06()
2 {
3 printf("%d\n", 5/2);//取整 2
4 printf("%d\n", 5%2);//余数 1
5 }
6 int main(int argc,char *argv[])
7 {
8 test06();
9 return 0;
10 }
运行结果:
案例:键盘输入一个数 是否能被3整除
1 void test07()
2 {
3 int num = 0;
4 //键盘输入一个int数
5 printf("请输入一个整形数据:");
6 scanf("%d", &num);
7
8 //判断是否能被3整数
9 if(num%3 == 0)
10 {
11 printf("ok\n");
12 }
13 else
14 {
15 printf("no\n");
16 }
17 }
18 int main(int argc,char *argv[])
19 {
20 test07();
21 return 0;
22 }
运行结果:
晚上的练习:键盘输入一个4位数 请取出每位上的数值
1 void test08()
2 {
3 printf("%d\n", 5/2);//取整
4 printf("%f\n",5/2.0f);//除
5 }
运行结果:
2、逻辑运算符
!逻辑非 !0 == 真 !真 == 假0
1 void test09()
2 {
3 printf("%d\n", !1);//0
4 printf("%d\n", !0);//1
5
6 //C语言 0为假 其他都为真 ‐1
7 printf("%d\n", !‐1);//0
8 }
9 int main(int argc,char *argv[])
10 {
11 test09();
12 return 0;
13 }
运行结果:
逻辑 && (重要)
A && B A B同时为真 整个表达式结果为真。 A B只要有一个为假 整个表达式为假
1 void test10()
2 {
3 if( (2>3) && (5>4) )
4 {
5 printf("ok\n");
6 }
7 else
8 {
9 printf("no\n");
10 }
11 }
12 int main(int argc,char *argv[])
13 {
14 test10();
15 return 0;
16 }
运行结果:no
逻辑&& 短路特性
A && B 如果A为假 系统不会执行B 这就是&&的短路特性
1 void test11()
2 {
3 int num=10;
4 printf("比较之前num = %d\n",num);
5
6 (2>3) && (num=100);
7 printf("比较之后num = %d\n",num);
8
9
10 }
11 int main(int argc,char *argv[])
12 {
13 test11();
14 return 0;
15 }
运行结果:
逻辑||
A || B 只要A B任意一个为真 整个表达式结果为真,A B同
时为假的时候 结果才为假。
逻辑|| 也有短路特性:只要A为真 编译器不会判断B的真
假。
1 void test12()
2 {
3 int num=10;
4 printf("比较之前num = %d\n",num);//10
5
6 (3>2) || (num=100);//num=100得不到执行
7 printf("比较之后num = %d\n",num);//10
8 }
9 int main(int argc,char *argv[])
10 {
11 test12();
12 return 0;
13 }
运行结果:
综合案例:
知识点3【位运算符】二进制位操作(重要)
&:按位与
语法:全1为1 其他为0
1010 1010
& 1111 0000
--------------------
1010 0000
特点:和1相与 保持不变 和0相与 清零
应用场景:将固定为清零。
|:按位或
语法:有1就为1 全0才为0
1010 1010
| 1111 0000
----------------------
1111 1010
特点:和0相或 保持不变 和1相或 置1.
应用场景:将固定为置1.
案例:将1010 1010的第2、3为置1 其他为保持不变
1010 1010
| 0000 1100
-------------------
1010 1110
~:按位取反
语法:0变1 1变0
~1010 1010 == 0101 0101
应用场景:配合 & | 操作
^:按位异或
语法:相同为0 不同为1
1010 1010
^ 0000 1111
----------------------
1010 0101
特点:和0异或 保持不变 和1异或 取反。
应用场景:将固定的为 发生高低电频 翻转。
案例:将1010 1010 的第0位 发生翻转.
1010 1010
^ 0000 0001
----------------------
1010 1011
^ 0000 0001
---------------------
1010 1010
<< 左移运算符: 左边丢弃 右边补0
注意:移动的位数 不要 超过自身长度
1010 1100 << 2
>> 右移运算符:
1010 1100 >> 2
右移分类:逻辑右移 算术右移
逻辑右移:右边丢弃 左边补0.
算术右移:
无符号数:右边丢弃 左边补0。
有符号数:
正数:右边丢弃 左边补0。
负数:右边丢弃 左边补1。
右移基本上是右边丢弃 左边补0 只有负数且算术右移 左边
才会补1.
逻辑右移和算术右移 是编译器决定,但是我们可以检测。
作业:自己写代码 判断你的编译器 是逻辑右移还是算术
右移。
综合案例:将data的第1、5清0, 第3, 4位置1 其他为保持
不变
unsigned char data = 0xaa;//1010 1010
将data的第1、5清0:
data = data & 1101 1101;
1101 1101 = ~(0010 0010) =~(0010 0000 | 0000
0010)
0010 0000 = 0000 0001 << 5
0000 0010 = 0000 0001 << 1
1101 1101 = ~(0x01 <<5 | 0x01<<1)
data = data & ~(0x01<<5 | 0x01<<1);
第3, 4位置1:
data = data | 0001 1000;
0001 1000 = 0001 0000 | 0000 1000
= 0x01<<4 | 0x01<<3
data = data | (0x01<<4 | 0x01<<3);
知识点4【?:】
表达式1 ? 值1:值2
语法:如果表达式1 为真 整个表达式的值为“值1” 否则
为“值2”
案例:
1 void test01()
2 {
3 int ret = 0;
4 ret = 3>2 5:6;
5
6 printf("ret = %d\n", ret);//5
7 }
8 int main(int argc,char *argv[])
9 {
10 test01();
11 return 0;
案例:逗号运算符
1 void test02()
2 {
3 int data1 = 0;
4 int data2 = 0;
5
6 data1 = 3,4,5,6;
7 data2 = (3,4,5,6);
8 printf("data1 = %d\n",data1);//3
9 printf("data2 = %d\n",data2);//6
10
11 }
12 int main(int argc,char *argv[])
13 {
14 test02();
15 return 0;
16 }
知识点5【复合运算符】
+= -= *= /= %= 等等
a += b; ==> a = a + b;
a *= b; ==> a= a * b;
注意:=号 右边必须看成一个整体
案例:
1 void test03()
2 {
3 int data = 3;
4 //将=右边看成一个整体
5 data *= 3+5;//data = data * (3+5);
6 printf("data = %d\n",data);//24
7 }
8 int main(int argc,char *argv[])
9 {
10 test03();
11 return 0;
12 }
知识点6【++ -- 运算符】
++i 或 --i 先加、减 后使用。
i++ 或 i-- 先 使用 后 加、减
案例:
1 void test04()
2 {
3 int i = 3;
4 int j = 0;
5
6 //++左边 先加 后使用
7 j = ++i;//i=i+1; j=i;
8
9 printf("i = %d, j = %d\n", i,j);//4 4
10 }
案例:
1 void test04()
2 {
3 int i = 3;
4 int j = 0;
5
6 //先使用 后 加、减
7 j = i++;//j=i; i=i+1;
8
9 printf("i = %d, j = %d\n", i,j);//4 3
10 }
11 int main(int argc,char *argv[])
12 {
13 test04();
14 return 0;
15 }
注意:i++ 或 ++i 作为单独的指令 没有区别
1 void test05()
2 {
3 int i=3;
4 int j=3;
5
6 i++;//i=i+1;
7 printf("i = %d\n",i);//4
8
9 ++j;//j=j+1
10 printf("j = %d\n",j);//4
11
12
13 }
14 int main(int argc,char *argv[])
15 {
16 test05();
17 return 0;
18 }
知识点6【优先级】分析已有的代码
优先级 高 先执行, 同级别的优先级 要看结合性。
自己写代码, 尽量加()。
知识点7【if语句】
1、如果只在乎项目的一个结果 选择 if
if(表达式)
{
语句1;
}
表达式为真 才会执行语句1;
案例:
1 void test06()
2 {
3 int data = 0;
4 printf("请输入一个int数据:");
5 scanf("%d", &data);
6
7 //判断能被2整除
8 if(data % 2 == 0)
9 {
10 printf("被2整除");
11 }
12 }
13 int main(int argc,char *argv[])
14 {
15 test06();
16 return 0;
17 }
2、如果项目的只有两种结果 且不会同时出现。 请选择 if。。。。。。
else......
if(表达式)
{
语句1;
}
else
{
语句2;
}
表达式为真 执行语句1 否则执行 语句2
1 void test06()
2 {
3 int data = 0;
4 printf("请输入一个int数据:");
5 scanf("%d", &data);
6
7 //判断data对2的余数
8 if(data % 2 == 0)
9 {
10 printf("余数为0\n");
11 }
12 else
13 {
14 printf("余数为1\n");
15 }
16 }
17 int main(int argc,char *argv[])
18 {
19 test06();
20 return
3、如果一个项目 有多个结果 且 不同时 出现。选择if...else if...else if
...else
if(表达式1)
{
语句1;
}
else if(表达式2)
{
语句2;
}
else//可省略
{
语句n;
}
只有表达式1为真 执行语句1,只有表达式2为真 执行语句2,所有表达式都为假 才执行语
句n;
案例:
1 void test06()
2 {
3 int data = 0;
4 printf("请输入一个int数据:");
5 scanf("%d", &data);
6
7 //判断data对3的余数 0 1 2
8 if(data % 3 == 0)
9 {
10 printf("余数为0\n");
11 }
12 else if(data %3 == 1)
13 {
14 printf("余数为1\n");
15 }
16 else if(data %3 == 2)
17 {
18 printf("余数为2\n");
19 }
20 }
注意:只有前面的条件不满足 才会判断后面的条件,如果
前面的条件满足 后面的条件不管真假 都不会执行。
1
4、一个项目有多个结果 不确定 是否同时出现。
if(表达式1)
{
语句1;
}
if(表达式2)
{
语句2;
}
if(表达式3)
{
语句3;
}
每个if语句是独立的。
作业:键盘输入一个数 能否被3和7同时整除。
知识点8【switch选择语句】
1 switch(表达式)
2 {
3 case 值1://不能是实型、字符串
4 语句1;
5 break;
6 case 值2:
7 语句2;
8 break;
9 case 值3:
10 语句3;
11 break;
12 default://可以省略
13 语句n;
14 break;
15 }
案例:
1 void test06()
2 {
3 int data = 0;
4 printf("请输入一个int数据:");
5 scanf("%d", &data);
6
7 switch(data%3)
8 {
9 case 0:
10 printf("余数为0\n");
11 break;
12 case 1:
13 printf("余数为1\n");
14 break;
15 case 2:
16 printf("余数为2\n");
17 break;
18 }
案例:
1 #include
2
3 void test01()
4 {
5 int ret = 0;
6 ret = 3>2 5:6;
7
8 printf("ret = %d\n", ret);//5
9 }
10
11 void test02()
12 {
13 int data1 = 0;
14 int data2 = 0;
15
16 data1 = 3,4,5,6;
17 data2 = (3,4,5,6);
18 printf("data1 = %d\n",data1);//3
19 printf("data2 = %d\n",data2);//6
20
21 }
22 void test03()
23 {
24 int data = 3;
25 //将=右边看成一个整体
26 data *= 3+5;//data = data * (3+5);
27 printf("data = %d\n",data);//24
28 }
29
30 void test04()
31 {
32 int i = 3;
33 int j = 0;
34
35 //先使用 后 加、减
36 j = i++;//j=i; i=i+1;
37
38 printf("i = %d, j = %d\n", i,j);//4 3
39 }
40
41 void test05()
42 {
43 int i=3;
44 int j=3;
45
46 i++;//i=i+1;
47 printf("i = %d\n",i);//4
48
49 ++j;//j=j+1
50 printf("j = %d\n",j);//4
51
52
53 }
54
55 void test06()
56 {
57 char ch = 0;
58 printf("请输入你的方向:wasd\n");
59 ch = getchar();
60 switch(ch)
61 {
62 case 'w':
63 case 'W':
64 printf("向上移动\n");
65 break;
66 case 'a':
67 case 'A':
68 printf("向左移动\n");
69 break;
70 case 's':
71 case 'S':
72 printf("向下移动\n");
73 break;
74 case 'd':
75 case 'D':
76 printf("向右移动\n");
77 break;
78 }
79 }
80 int main(int argc,char *argv[])
81 {
82 test06();
83 return 0;
84 }
作业:键盘输入1~7的的数 用switch判断今天是星期几知识点1【作业讲解】
知识点2【for循环】
循环嵌套循环(了解)
作业:
1、求出1~100中能被7整除的数。
知识点3【while 循环、do...while】
while
do....while();
goto 跳转
作业:重复输入1~7的数值判断 星期几,注意输入0,就结束程序,如果是0或1~7以外的
数,请提示“请重新输入有效数值”。
总结:for while 我们如何选择呢?
知识点4【数组的概述】
知识点5【一维数值数组】
1、数组的定义
案例:遍历数组:
案例:数组的初始化
全部初始化
部分初始化
扩展:初始化
数组的空间大小:(重要)
数组元素的操作
案例:定义数组 5个元素 每个元素int类型 获取键盘输入
作业:定义数组 5个元素 每个元素int类型 获取键盘输入 请求出数组的最大值、最小值、平
均值。
知识点6【二维数组】
二维数组的定义
二维数组的遍历:
二维数组的初始化
分段初始化:用{}里面的{}明确的表示一行
连续初始化:放满一行 才能放下一行
作业:定义int arr[3][4]的二维数组,获取键盘输入,并求出每一行的平均值。
知识点1【作业讲解】
作业1:
1 void test01()
2 {
3 char ch =0x85 ;//1000 0101>>4
4 //逻辑右移: 1000 0101>>4 == 0000 1000 == 0x08
5 //算术右移:1000 0101>>4 == 1111 1000 == 0xf8
6 ch = ch>>4;
7 printf("ch = %#x\n",ch);//0xf8是算术右移
8
9 //逻辑右移、算术右移是编译器决定 我们只能测试 不能改变
10 }
11 int main(int argc,char *argv[])
12 {
13 test01();
14 return 0;
15 }
作业:
1 void test02()
2 {
3 //需求:请输入1~7的数 判断是星期几
4 int date = 0;
5 printf("请输入1~7的数:");
6 scanf("%d", &date);
7
8 switch(date)
9 {
10 case 1:
11 printf("星期一\n");
12 break;
13 case 2:
14 printf("星期二\n");
15 break;
16 case 3:
17 printf("星期三\n");
18 break;
19 case 4:
20 printf("星期四\n");
21 break;
22 case 5:
23 printf("星期五\n");
24 break;
25 case 6:
26 printf("星期六\n");
27 break;
28 case 7:
29 printf("星期日\n");
30 break;
31 default:
32 printf("请输入有效数值\n");
33 break;
34
35 }
36
37 }
38 int main(int argc,char *argv[])
39 {
40 test02();
41 return 0;
42 }
知识点2【for循环】
1 for( 初始语句; 循环条件 ; 步进条件)
2 {
3 //循环语句
4 }
5 //初始语句:只在循环开始时 执行一次
6 //循环条件:每次循环都要执行 如果循环条件为真 进入循环体 如果为假 退出循环
7 //步进条件:每次循环结束的时候 要执行的语句
案例:求1~100的和
1 void test03()
2 {
3 //1+2+3+4+ ...+ 100
4 int i=0;
5 int sum = 0;
6
7 for(i=1 ; i<=100 ; i++)//i++ ==> i=i+1
8 {
9 sum = sum+i;
10 }
11
12 printf("sum = %d\n",sum);//5050
13
14 }
案例:
1 void test03()
2 {
3 //1+2+3+4+ ...+ 100
4 int i=1;
5 int sum = 0;
6
7 //如果变量 提前初始化了 for的初始化语句 可以省略
8 for( ; ; )//建议别这么做
9 {
10 //如果for省略循环条件 必须在循环语句中实现 退出循环的条件
11 if(i>100)
12 break;//跳出循环
13
14 sum = sum+i;
15 i++;//如果for省略步进条件 必须在循环语句中实现 步进动作
16 }
17 printf("sum = %d\n",sum);//5050
18
19 }
20 int main(int argc,char *argv[])
21 {
22 test03();
23 return 0;
24 }
案例:
1 void test04()
2 {
3 for(;;)
4 {
5 for(;;)
6 {
7 break;//只能跳出离它最近的一层循环
8 }
9 }
10 }
案例:
1 void test05()
2 {
3 int i=0;
4 int sum = 0;
5 for(i=1 ;i<=100 ; i++)
6 {
7 //sum = sum+i;
8 if(i == 50)
9 continue;//结束本次循环 立即从当前位置直接 进入下一次循环
10
11 sum += i;
12 }
13
14 printf("sum = %d\n",sum);//5000 仅仅是少加了一个50
15 }
案例:求出0~100的所有偶数的和
1 void test06()
2 {
3 //求0 2 4 6 8......100的和
4 int i=0;
5 int sum = 0;
6 for(; i<=100 ; i=i+2)//步进条件不是单纯的 ++i
7 {
8 sum += i;
9 }
10
11 printf("sum = %d\n",sum);//2550
12 }
循环嵌套循环(了解)
1 void test07()
2 {
3 int i=0;
4 int j=0;
5
6 //总结 外层循环的次数 * 内层循环的次数 == 总循环次数
7 for(i=0;i<10;i++)//0~9 =10
8 {
9 for(j=0;j<10; j++)//0~9 =10
10 {
11 printf("i=%d, j=%d\n", i,j);//循环100次
12 }
13 }
14 }
作业:
1、求出1~100中能被7整除的数。
知识点3【while 循环、do...while】
while
1 while(循环条件)
2 {
3 //循环语句
4 }
5 //如果“循环条件”为真 就进入循环体执行循环语句
注意:
1、while没有初始化语句 用户提前初始化好。
2、while没有步进语句 用户必须在循环语句中 写好 步进语句。
案例:
1 void test08()
2 {
3 int i=1;//提前初始化
4 int sum = 0;//局部变量如果不初始化 内容不确定
5
6 while(i<=100)
7 {
8 sum += i;
9 i++;//不仅条件
10 }
11 printf("sum = %d\n", sum);//5050
12 }
do....while();
1 do
2 {
3 //循环语句;
4 }while(循环条件);
先执行循环语句 再判断循环条件是否为真 如果为真 进行下次循环 如果为假 直接退出循环
1 void test09()
2 {
3 int num = 0;
4 do
5 {
6 printf("ok\n");
7 }while(num>0);
8 }
goto 跳转
1 void test10()
2 {
3 printf("‐‐‐‐‐‐‐001‐‐‐‐‐‐‐\n");
4 printf("‐‐‐‐‐‐‐002‐‐‐‐‐‐‐\n");
5 goto here;
6 printf("‐‐‐‐‐‐‐003‐‐‐‐‐‐‐\n");
7 printf("‐‐‐‐‐‐‐004‐‐‐‐‐‐‐\n");
8 printf("‐‐‐‐‐‐‐005‐‐‐‐‐‐‐\n");
9 here:
10 printf("‐‐‐‐‐‐‐006‐‐‐‐‐‐‐\n");
11 printf("‐‐‐‐‐‐‐007‐‐‐‐‐‐‐\n");
12
13 }
运行结果:
作业:重复输入1~7的数值判断 星期几,注意输入0,就结束程序,如果
是0或1~7以外的数,请提示“请重新输入有效数值”。
总结:for while 我们如何选择呢?
如果循环的次数是确定的 建议选择for。
如果循环的次数不确定,知道退出的条件 建议选择while.
知识点4【数组的概述】
知识点5【一维数值数组】
1、数组的定义
需求:请定义一个数组 该数组 有10个元素 每个元素为int
在定义的时候:
a、arr[] arr和[]结合是数组
b、将确定的元素的个数放入 []中
c、用元素的类型 定义一个普通变量
d、从上往下整体替换。
int arr[10];
注意:
1、数组名 arr 不能和其他变量名 同名
2、数组的元素小标是从0开始:0~9
3、数组的元素 分别是:arr[0]、arr[1]~arr[9], 如果访问arr[10]数组越界。
4、数组的元素 等价于 普通变量
5、在定义数组的时候 ,[]里面的值 不能是变量(c89)。
案例:遍历数组:
1 void test01()
2 {
3 //局部数组 如果不初始化 内容不确定
4 int arr[10];
5 int i=0;
6
7 //遍历数组
8 for(i=0;i<10; i++)
9 {
10 printf("%d ",arr[i]);//输出的都是不确定的值
11 }
12 printf("\n");
13 }
运行结果:
案例:数组的初始化
初始化:定义的时候 给变量 或数组元素 赋值的动作 叫初始化。
全部初始化
1 int arr[5] = {10,20,30,40,50};
2 //如果是 全部初始化 数组元素的个数 可以省略 实际的元素个数由 初始化个数决定
3 int arr[] = {10,20,30,40,50};//少用
4 //错误显示
5 int arr[];//错误无法确定数组的大小
部分初始化
1 //部分初始化 未被初始化 部分自动补0 (重要)
2 int arr[5] = {10,20,30};
3
4 //初始化数组 常见的操作(将数组的所有元素清零)
5 int arr[5] = {0};//只初始化arr[0]=0,未被初始化部分 自动补0
6 int arr[5] = {2};//2 0 0 0 0
扩展:初始化
1 int arr[5]={[2]=3, [4]=7};//0 0 3 0 7
2 //[2]=3将数组的第2个元素 初始化为3
3 //[4]=7将数组的第4个元素 初始化为7
数组的空间大小:(重要)
1 //arr数组名 作为数组类型 代表的是数组空间的总大小
2 int arr[5] = {10,20,30,40,50};
3 int n = 0;
4
5 //数组的总大小 = 元素的个数 * 每个元素的大小
6 printf("数组的总大小=%d\n",sizeof(arr));//20
7
8 //数组元素的大小 arr[0]是数组的第0个元素
9 printf("数组元素的大小 = %d\n",sizeof(arr[0]));//4
10
11 //数组元素的个数 = 数组总大小/每个元素的大小
12 n = sizeof(arr)/sizeof(arr[0]);
13 printf("数组元素的个数为:%d\n", n );
案例:
1 void test05()
2 {
3 int arr[] = {10,20,30,40,50,1,2,10};
4 int n = sizeof(arr)/sizeof(arr[0]);//是求数组的元素个数
5 int i=0;
6
7 //遍历数组
8 for(i=0;i9 {
10 printf("%d ",arr[i]);//输出的都是不确定的值
11 }
12 printf("\n");
13 }
14 int main(int argc,char *argv[])
15 {
16 test05();
17 return 0;
18 }
运行结果:
案例:
1 void test06()
2 {
3 int arr1[5]={1,2,3};
4 int arr2[]={1,2,3};
5 char arr3[5]={1,2,3};
6 int arr4[]={[5]=7};
7
8 printf("%d\n",sizeof(arr1));//20
9 printf("%d\n",sizeof(arr2));//12
10 printf("%d\n",sizeof(arr3));//5
11 printf("%d\n",sizeof(arr4));//24 //vc++6.0不支持
12 }
13 int main(int argc,char *argv[])
14 {
15 test06();
16 return 0;
17 }
数组元素的操作
数组的元素 等价于 普通变量
1 void test07()
2 {
3 int arr[5]={1,2,3,4,5};
4 int n = sizeof(arr)/sizeof(arr[0]);
5 int i=0;
6
7 //给数组元素 赋值
8 arr[0] = 100;
9
10 //num++
11 arr[1]++;//arr[1]=arr[1]+1;
12
13 //scanf("%d",&num)
14 scanf("%d",&arr[2]);
15
16 for(i=0;i17 {
18 printf("%d ",arr[i]);
19 }
20 printf("\n");
21 }
案例:定义数组 5个元素 每个元素int类型 获取键盘输入
1 void test08()
2 {
3 //数值数组 只能逐个元素操作 不能整体操作
4 int arr[5]={0};
5 int n = sizeof(arr)/sizeof(arr[0]);
6 int i=0;
7
8 printf("请输入%d个int数据\n", n);
9 for(i=0;i10 {
11 scanf("%d",&arr[i]);//&arr[i] 表示取第i个元素的地址
12 }
13
14 for(i=0;i15 {
16 printf("%d ", arr[i]);//arr[i] 访问数组的第i个元素
17 }
18 }
作业:定义数组 5个元素 每个元素int类型 获取键盘输入 请求出数组的最大值、最小值、
平均值。
知识点6【二维数组】
二维数组的定义
1 int arr[3][4];
2 //第一个[]里面的值 表示行数 ,第二个[]里面的值 表示列数
3 //arr数组有3行4列
4 //3行:0~2行 4列:0~3列
二维数组的遍历:
1 int i=0;
2 for(i=0;i<3;i++)
3 {
4 //遍历第i行
5 int j = 0;
6 for(j=0;j<4; j++)
7 {
8 printf("%d ",arr[i][j])
9 }
10 }
案例:
1 void test01()
2 {
3 int arr[3][4];//局部数组不初始化元素内容不确定
4 int i=0,j=0;
5
6 for(i=0;i<3;i++)//行的变化
7 {
8 for(j=0;j<4;j++)//列的变化
9 {
10 printf("%d ",arr[i][j]);
11 }
12 printf("\n");
13 }
14 }
运行结果:
二维数组的初始化
分段初始化:用{}里面的{}明确的表示一行
1 int arr[3][4]={ {1,2,3,4}, {5,6,7,8}, {9,10,11,12} };
连续初始化:放满一行 才能放下一行
1 int arr[3][4]={1,2,3,4,5,6,7,8,9,10,11,12};
案例:
1 void test01()
2 {
3 int arr1[3][4]={{1,2},{3},{4,5}};
4 int arr2[3][4]={1,2,3,4,5};
5
6 printf("%d\n", arr1[1][2]+arr2[1][2]);
7 }
8 int main(int argc,char *argv[])
9 {
10 test01();
11 return 0;
12 }
作业:定义int arr[3][4]的二维数组,获取键盘输入,并求出每一行的平
均值。知识点1【防止头文件重复包含】
方式一:#pragma once 编译器决定 晚
方式二:c/c++的标准制定 早
知识点2【原码 反码 补码】
计算机 为啥 要补码?
总结:补码意义:将减法运算 变加法运算 同时统一了0的编码。
知识点3【计算机对数据的存储 与 读取】
知识点4【内存地址的概述】
知识点5【指针变量概念】(重要)
知识点6【定义指针变量】(重要)
定义指针变量的步骤:
定义指针变量(前提)
知识点7【指针变量的使用】通过过p 对所保存的地址空间 进行读写操作(重要)
知识点8【指针变量的类型】(重要)
案例:指针变量取值宽度。(重要)
案例:指针变量的跨度(重要)
知识点1【防止头文件重复包含】
方式一:#pragma once 编译器决定 晚
#pragma once放在头文件的最前方
main.c
1 #include
2 #include"a.h"
3 #include "b.h"
4 int main(int argc,char *argv[])
5 {
6 printf("num = %d\n",num);
7 return 0;
8 }
a.h
1 #pragma once //防止头文件重复包含
2 #include "b.h"
b.h
1 #pragma once//防止头文件重复包含
2 int num = 10;
方式二:c/c++的标准制定 早
#ifndef 宏
#define 宏
头文件具体的内容
#endif
案例:
main.c
1 #include
2 #include"a.h"
3 #include "b.h"
4
5 int main(int argc,char *argv[])
6 {
7 printf("num = %d\n",num);
8 return 0;
9 }
a.h
1 #ifndef __A_H__
2 #define __A_H__
3 #include "b.h"
4 #endif
b.h
1 #ifndef __B_H__
2 #define __B_H__
3
4 int num = 10;
5
6 #endif
总结:
#pragma once 编译器决定 强调的文件名
#ifndef c/c++标准制定 强调的宏 而不是文件
知识点2【原码 反码 补码】
注意:无符号数,正数,他们的 原码==反码==补码
负数:反码=原码的符号位不变 其他位取反
补码=反+1.
(重要):负数在计算机中存储的是补码。
计算机 为啥 要补码?
以1字节分析:
如果没有补码:
6-10== -4
6+(-10) == -4
0000 0110
1000 1010
--------------
1001 0000 == -16(错误)
如果有补码:
0000 0110
1111 0110
----------------------
1111 1100----->1000 0011--->1000 0100==>-4
总结:补码的意义-将减法运算 变加法运算
以1字节分析:
有符号符:1111 1111~1000 0000~0000 0000~0111
1111
-127 ~ -0 ~ +0 ~
+127
计算机为了扩数据的表示范围:故意将-0看
成-128
-128~127
无符号数:0000 0000 ~ 1111 1111 == 0~255
总结:补码统一 0 的编码。
+0 == 0000 0000==0000 0000(反码)==0000
0000(补码)
-0 == 1000 0000 ==1111 1111(反码)==0000
0000(补码)
总结:补码意义:将减法运算 变加法运算 同时统一了0的编码。
知识点3【计算机对数据的存储 与 读取】
存储:
1 #include
2
3 void test01()//存储
4 {
5 //负数 以补码 存储
6 char data = ‐10;
7 //正数 以原码 存储
8 char data2 = 10;
9 //十六进制 以 原码存储
10 char data3 = 0xae;//0xae==1010 1110
11 //八进制 以 原码存储
12 char data4 = 0256;//0256==1010 1110
13 //每3位二进制 代表 一位八进制
14 //如果数据越界 以原码 存储
15 char data5 = 129;//1000 0001
16
17 unsigned char data6 = ‐10;//0000 1010
18
19
20 //取 %x %u %o 都是输出内存的原样数据
21 //每4位二进制 代表 一位十六进制(记住)
22 printf("%x\n", data);//0xf6==1111 0110
23
24 printf("%x\n",data2);//0x0a ==0000 1010
25
26 printf("%x\n",data3);//0xae
27
28 printf("%x\n",data4);//0xae
29
30 printf("%x\n",data5);//0x81
31 printf("%x\n",data6);//0xfb
32
33 }
34 int main(int argc,char *argv[])
35 {
36 test01();
37 return 0;
38 }
取:
1 void test02()
2 {
3 char data1 = ‐10;
4 char data2 = 10;
5
6 //取:%d %hd %ld有符号取 %u %x %o %lu都是无符号取
7 //有符号取:%d %hd %ld
8 //首先看内存的最高位如果为1 将内存数据符号位不变取反+1到原码
9 //最高位如果为0 将数据原样输出。
10 //无符号取:将内存数据原样输出
11 printf("%d\n",data1);//‐10
12
13 //data1&0x000000ff 只取低8位
14 printf("%u\n",data1&0x000000ff);//246==1111 0110
15
16 printf("%d\n",data2);//10
17
18 }
知识点4【内存地址的概述】
系统给内存的每一个字节 分配一个编号 而这个编号 就是
内存地址。
内存地址 也叫 指针。 指针 就是 地址 地址 就是 指针。
知识点5【指针变量概念】(重要)
指针变量:本质就是一个变量 只是这个变量 存放 是内存的地址编号(地址/指针)。
在32位平台 任何类型的地址编号 都是4字节。
知识点6【定义指针变量】(重要)
定义指针变量的步骤:
1、*修饰 指针变量名
2、保存啥类型变量的地址 就用该类型定义一个普通变量。
3、从上 往下 整体 替换
定义指针变量(前提)
1、明确保存 啥类型变量的地址。
案例:
1 void test02()
2 {
3 //num拥有一个合法的空间
4 int num = 10;
5
6 //需求:请定义一个指针变量 保存num的地址
7 //p就是指针变量 变量名为p 不是*p
8 //在定义的时候:*修饰p 表示p为指针变量
9 int *p;
10
11 //建立p和num的关系:p保存num的地址
12 printf("&num = %p\n",&num);
13 p = #//&num 代表的是num变量起始地址(首地址)
14 printf("p = %p\n", p);
15 }
运行结果:
知识点7【指针变量的使用】通过过p 对所保存的地
址空间 进行读写操作(重要)
案例:
1 void test02()
2 {
3 //num拥有一个合法的空间
4 int num = 10;
5
6 //需求:请定义一个指针变量 保存num的地址
7 //p就是指针变量 变量名为p 不是*p
8 //在定义的时候:*修饰p 表示p为指针变量
9 int *p;
10
11
12 //建立p和num的关系:p保存num的地址
13 printf("&num = %p\n",&num);
14 p = #//&num 代表的是num变量起始地址(首地址)
15 printf("p = %p\n", p);
16
17 //*p 等价 num
18 printf("*p = %d\n", *p);//10==num
19
20 //*p = 100
21 *p = 100;//num = 100
22 printf("num = %d\n", num);
23
24 //scanf("%d", &num);
25 scanf("%d", p);//如果此处为 &p表示键盘给p赋值 而不是给num赋值
26 printf("num = %d\n",num);
27 }
运行结果:
知识点8【指针变量的类型】(重要)
int *p;
在定义中:
指针变量 自身类型。只将指针变量名拖黑 剩下啥类型 指针变量自身就是啥类型。
p自身的类型是int *。
指针变量 所指向的类型。将指针变量名和离它最近的一个*一起拖黑 剩下啥类型 就指向啥
类型。
p指向的类型为int ==== p保存int类型变量的地址。
指向......类型 ==保存......类型变量的地址
int num = 10;
int *p;
p = #//p指向num ==p保存了num的地址
案例:指针变量取值宽度。(重要)
1 void test03()
2 {
3 int num = 0x01020304;
4 int *p;
5 short *p2;
6 char *p3;
7
8 p = #
9 p2 = #
10 p3 = #
11
12 printf("*p = %#x\n", *p);
13 printf("*p2 = %#x\n", *p2);
14 printf("*p3 = %#x\n", *p3);
15 }
案例:指针变量的跨度(重要)
1 void test04()
2 {
3 int num = 0x01020304;
4 int *p;
5 short *p2;
6 char *p3;
7
8 p = #
9 p2 = #
10 p3 = #
11
12 printf("p=%u\n",p);
13 printf("p+1=%u\n",p+1);
14 printf("‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐\n");
15 printf("p2=%u\n",p2);
16 printf("p2+1=%u\n",p2+1);
17 printf("‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐\n");
18 printf("p3=%u\n",p3);
19 printf("p3+1=%u\n",p3+1);
20 }
运行结果:
综合案例:

展开更多......

收起↑

资源列表