嵌入式基础(二)
C语言相关
查看相关知识
14.预处理指令和多文件编程
查看相关知识
(1)预处理指令
以
#
开头的都是预处理指令,在预处理阶段会处理的指令预处理—gcc -E xxx.c -o xxx.i
宏
#define
预处理指令可以用来定义宏- 格式:#define 宏名称 (宏值)
- 宏名通常都是由大写英文字符构成,宏名不可包括空格;用宏给数字命名时,不可使用赋值操作符,不自增自减
- 宏能够给表达式命名,宏的参数可以用来表示表达式的未知数字;格式:#define 宏名称(参数列表) (宏值表达式)
- 宏只检查参数个数,不检查参数类型
- 可以在编译命令里使用-D选项指定宏所代表的数字;由于有些数字在编写程序时无法确定,只有在编译的时候才知道,这个时候需要在程序里使用宏名称代表它们,然后再编译时用数字替换这些宏
宏操作符
#
是一个宏操作符,能够将宏的参数转换成字符串##
是一个宏操作符,可以将一个代表标识符的参数和其他内容连接为一个新的标识符- 编译器内置宏—预定义宏;默认已经定义好的宏,直接使用即可,如下表
| 宏 | 占位符 | 含义 |
| —————— | ——— | ——————— |
|FILE| %s | 所在文件名 |
|LINE| %d | 所在行号 |
|FUNCTION| %s | 所在函数名 |
|func| %s | 所在函数名 |
|DATE| %s | 编译该文件日期 |
|TIME| %s | 编译该文件时间 |案例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54/********************************/
int main(void){
int r=10;
printf("周长=%lg\n",2*PI*r);
return 0;
}
/******************************/
int main(void){
printf("%d\n",SQUARE(100));//10000
printf("%lg\n",SQUARE(5.6));//31.36
printf("%d\n",SQUARE(15+5));//400
printf("%d\n",SQUARE1(15+5));//95
printf("99-76=%d\n",SUB(99,76));//96-76=23
int a=100,b=20;
printf("%d-%d=%d\n",a,b,SUB(a,b));//100-20=80
PRINT(a);//(printf("a""=%d\n",N)//a=100
int ID(1)=100,ID(2)=101;
printf("id1=%d,id2=%d\n",id1,id2);//id1=100,id2=101
return 0;
}
/**********************************/
int main(void){
int arr[SIZE];
for(int i=0;i<SIZE;i++){
arr[i]=i+100;
}
for(int i=0;i<SIZE;i++){
printf("%d ",arr[i]);
}
printf("\n");
printf("%s\n",TODAY);
}
/*************命令行**************************************/
gcc -DSIZE=10 define2.c -o define2
gcc -DSIZE=10 -DTODAY=\"Friday\" define2.c -o define2 //字符的输入需要注意
/*******************************************************/
#include <stdio.h>
int main ()
{
printf("File :%s\n", __FILE__ );
printf("Date :%s\n", __DATE__ );
printf("Time :%s\n", __TIME__ );
printf("Line :%d\n", __LINE__ );
return(0);
}条件编译—-条件成立编译,不成立不编译;条件编译应用:一套代码可以适应不同的硬件平台,根据硬件平台生成代码;案例如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
int main(void){
printf("1\n");
printf("2\n");
printf("3\n");
printf("4\n");
printf("5\n");
printf("6\n");
printf("7\n");
printf("8\n");
printf("9\n");
printf("10\n");
return 0;
}
(2)多文件编程
1.单文件程序改造多文件程序步骤
- 把所有函数分散在多个不同的源文件里(主函数main.c)
- 为每个源文件编写配对的以.h作为扩展名的头文件(主函数不需要),头文件使用的宏名称必须根据文件名称变化得到,例如a.h→__A__H
- 为所有源文件使用#include预处理指令包含必要的头文件
2.头文件卫士
1 |
|
1 |
|
3.多文件编译
快捷指令:vim xxx xxx xxx 同时打开多个文件;使用gt在多个文件之间切换
1 | gcc main.c swap.c -o swap |
4.Makefile
可以将多文件程序的编译步骤记录在Makefile文件中,使用make工具按照Makefile文件里记录的步骤完成编译
Makefile文件每个编译命令都不可使用空格字符而应该使用tab键
Makefile是一个文本文件
1
2
3
4
5
6
7
8目标:依赖文件
由依赖文件生成目标的命令
main:main.o cal.o
gcc main.o cal.o -o main
main.o:main.c
gcc -c main.c -o main.o
cal.o:cal.c
gcc -c cal.c -o cal.o由
.c
文件得到对应的.o
文件的小技巧1
2
3
4
5上述代码可缩短为以下代码
main:main.o cal.o
gcc main.o cal.o -o main
%.o:%.c
gcc -c $< -o $@伪目标即没有依赖的目标,终端输入
make clean
指令,即可执行Makefile文件的如下代码执行删除文件的指令1
2clean:
rm main main.o cal.o使用变量-宏简化操作
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16#变量 -宏
OBJ=main.o cal.o
BIN=main
CC=gcc
RM=rm
$(BIN):$(OBJ)
$(CC) $(OBJ) -o main
%.o:%.c
$(CC) -c $< -o $@
#伪目标
#没有依赖的目标
#make clean
clean:
$(RM) $(BIN) $(OBJ)
15.结构体
查看相关知识
结构体类型的存储区可以包含多个子存储区,不同子存储区的类型可以不同,子存储区也可以是结构体类型的存储区
结构体里不可包含函数
结构体声明语句因为不分配内存可以写在头文件里
结构体变量的声明、定义以及输入输出
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28/**********声明方式1***********/
struct student{
char name[32];
int age;
};
struct student s1={"MYAN",25};
/************声明方式2*********/
struct student{
char name[32];
int age;
};
typedef struct student stu_t;
stu_t s2={"CSC",24};
/************声明方式3*********/
typedef struct student{
char name[32];
int age;
}stu_t;
stu_t s2={"CSC",24};
/*************输出************/
printf("%s,%d\n",s1.name,s1.age);
/*************输入************/
stu_t s3={0};
printf("please input name:");
scanf("%s",s3.name);//由于name成员变量是数组,因此s3.name表示地址
printf("please input age:");
scanf("%d",&s3.age);//由于age是整型变量,因此&s3.age表示地址结构体指针可以记录结构体存储区的地址,建立对应的捆绑关系,以下写法可以通过结构体指针表示结构体的子存储区
1
2stu_t *ps2=&s2;
printf("%s,%d\n",ps2->name,ps2->age);结构体作为函数参数,可以直接使用结构体类型的形式参数从调用函数向被调用函数传递结构体数据,直接使用结构体类型的形式参数会造成时间和空间的浪费,使用结构体指针类型的参数可以避免浪费,使用结构体指针作为形式参数的时候,如果不希望通过结构体指针修改结构体的数据,可以添加const关键字声明
1
2
3
4
5
6
7
8
9
10
11
12
13typedef struct student{
char name[32];
int age;
}stu_t;
/*************结构体作为形式参数*************/
void show(stu_t s){
printf("%s,%d\n",s.name,s.age);
}
/***********结构体指针作为形式参数***********/
void show_p(const stu_t* ps){
printf("%s:%d\n",ps->name,ps->age);
// ps->age++;//ok 加const关键字则不可以
}结构体作为返回值,可以把结构体类型的变量直接作为返回值使用,这样可以从被调用函数向调用函数传递结构体数据,但是这样会造成时间和空间的浪费,使用结构体存储区的地址作为返回值可以避免这个问题,但是需要注意的是不可以把局部结构体存储区的地址作为返回值使用
1
2
3
4
5
6
7
8
9
10
11
12
13
14typedef struct student{
char name[32];
int age;
}stu_t;
/*************结构体作为返回值*************/
stu_t get_stu_info(void){
stu_t s={"LX",27};
return s;
}
/***********结构体指针作为返回值***********/
stu_t* get_stu_info_p(void){
static stu_t s={"HL",18};
return &s;//直接返回局部变量的地址,不可行,加static可行
}结构体数组
1
2
3
4
5
6
7
8stu_t a[3]={
{"MYAN",25},
{"GMN",26},
{"LX",27}
};
for(int i=0;i<sizeof(a)/sizeof(a[0]);i++){
printf("%s:%d\n",a[i].name,a[i].age);
}结构体嵌套结构体:
.
前面是变量名,->
前面是指针名1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
typedef struct birthday{
int year;
int month;
int day;
}birth_t;
typedef struct student{
char name[32];
int age;
birth_t birth;
}stu_t;
int main(void){
stu_t s={"JACK",18,{2006,5,18}};
printf("%s:%d\n",s.name,s.age);
printf("%d:%d:%d\n",s.birth.year,s.birth.month,s.birth.day);
stu_t* ps=&s;
printf("%s:%d\n",ps->name,ps->age);
/*ps->birth是变量,因此使用ps->birth.month表示月份*/
printf("%d:%d:%d\n",ps->birth.year,ps->birth.month,ps->birth.day);
ps->birth.year=2005;
return 0;
}内存对齐和补齐
- 4字节对齐:结构体中每个成员的起始地址必须是其自身大小的整数倍,超过4字节的按照4字节算
- 4字节补齐:结构体存储区的大小必须是4的整数倍
- 8字节补齐:结构体存储区的大小必须是8的整数倍(64位系统)
#pragma pack(n)
代码以n字节对齐补齐
16.联合体
查看相关知识
联合体成员变量对应的存储区在内存里互相重叠,联合体成员变量的开始地址相同,联合体的大小为占内存最多的成员变量的大小
联合体成员不是相互独立的,且同一时间只能使用一个联合体成员
联合体在解决判断内存存储是大段存储还是小端存储的优势
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22union U
{
char c;
int i;
}u;//联合体变量创建方法类比结构体
int main()
{
u.i = 1; //0x 00 00 00 01(变量i为int类型,有四个字节,32bit)
/* 低地址----------------------------------------------------->高地址
hex:01 00 00 00 bin:0000 0001 0000 0000 0000 0000 0000 0000 小端存储 低位放低地址
hex:00 00 00 01 bin:0000 0000 0000 0000 0000 0000 0000 0001 大端存储 低位放高地址
*/
//变量c为char类型,只有一个字节,取前8bit即可
if (u.c == 1) //即u.c = 0000 0001
{
printf("小端");
}
else
{
printf("大端");
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
//声明联合体数据类型 不占内存空间
struct person{
char name[20];
char sex;
char job;
union{
int class; //班级
char position[10];//位置 讲师 教授
}category; //定义了联合体类型变量category
};
int main(void){
struct person persons[2] ={
{.name="小明", .sex='m', .job='s', .category.class = 2007},
{.name="老张", .sex='f', .job='t', .category.position="教授"},
};
for(int i=0; i<sizeof(persons)/sizeof(persons[0]); i++){
if(persons[i].job == 's'){
printf("姓名: %s 性别:%c 班级:%d\n", persons[i].name,
persons[i].sex, persons[i].category.class);
}
else{
printf("姓名: %s 性别:%c 职称:%s\n", persons[i].name,
persons[i].sex, persons[i].category.position);
}
}
return 0;
}
17.枚举
查看相关知识
- 枚举是一个有限整型常量的列表,每个枚举值都是一个符号常量,默认从0开始,向后依次加1
- 枚举定义了一些符号,这些符号的本质就是int类型的常量,每个符号和一个常量绑定。这个符号就表示一个自定义的一个识别码,编译器对枚举的认知就是符号常量所绑定的那个int类型的数字
使用枚举其实就是对一些数字进行符号化编码,这样的好处就是编程时可以不用看数字而直接看符号。符号能够见名知意,而数字所代表的含义需要看文档或者注释
枚举能够实现的,宏定义也能实现,但是二者之间有所区别,区别如下:
1、枚举是将多个有关联的符号封装在一个枚举中,而宏定义是完全散的。即枚举是多选一,且只能在这里面选,能防止不符合的数据输入
2、当要定义的常量是一个有限集合时,最适合用枚举
3、不适合用枚举的情况下(比如定义的常量符号之间无关联,或者无限的)使用宏定义
1 | //先定义类型 |
18.二级指针
查看相关知识
二级指针用来记录一级指针的地址
定义
int ** ppa=&pa
二级指针变量名称前写
**
可以表示它所捆绑的普通类型存储区函数中修改外部一级指针的值,需要传递二级指针才可修改有效
函数名称可以用来表示函数地址,函数指针用来记录函数的地址;函数指针格式
返回类型 (*函数指针变量)(形参表)=函数名
,通过函数指针调用函数的格式函数指针变量(实参表)
使用函数指针作为形参的函数被称为回调函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88/**************************************/
int main(void){
int a=100;
int *pa=&a;
int **ppa=&pa;
printf("a=%d,*pa=%d,**ppa=%d\n",a,*pa,**ppa);
printf("&a=%p,pa=%p,*ppa=%p\n",&a,pa,*ppa);
printf("ppa=%p,&pa=%p\n",ppa,&pa);
printf("&ppa=%p\n",&ppa);
return 0;
}
/**************************************/
int main(int argc,char** argv){//字符指针数组可以使用二级指针代替
for(int i=0;i<argc;i++){
printf("argv[%d]=%s\n",i,argv[i]);
}
return 0;
}
/**************************************/
void swap(char** s1,char **s2){
char* temp="";
temp=*s1;
*s1=*s2;
*s2=temp;
}
int main(void){
char* s1="hello";
char* s2="world";
printf("s1=%s,s2=%s\n",s1,s2);
swap(&s1,&s2);
printf("s1=%s,s2=%s\n",s1,s2);
return 0;
}
/**************************************/
int add(int x,int y){
return x+y;
}
int sub(int x,int y){
return x-y;
}
int main(void){
int (*p)(int,int)=add;//函数指针
int sum=p(100,200);
printf("%d\n",sum);
p=sub;
int ret =p(400,200);
printf("%d\n",ret);
return 0;
}
/**************************************/
typedef int (*pfunc_t)(int,int);
int add(int x,int y){
return x+y;
}
int sub(int x,int y){
return x-y;
}
int main(void){
pfunc_t p=add;//函数指针
int sum=p(100,200);
printf("%d\n",sum);
pfunc_t p1=sub;
int ret =p1(400,200);
printf("%d\n",ret);
return 0;
}
/**************************************/
typedef int (*pfunc_t)(int,int);
int add(int x,int y){
return x+y;
}
int sub(int x,int y){
return x-y;
}
int cal(int a,int b,pfunc_t pfunc){//函数指针作为形参--回调函数
return pfunc(a,b);
}
int main(void){
printf("%d\n",cal(100,200,add));
printf("%d\n",cal(100,200,sub));
return 0;
}
19.动态分配内存
查看相关知识
- 动态内存分配:在程序运行时,临时决定需要分配的存储区个数,为了管理动态分配内存需要使用一组标准函数,这些标准函数位于
stdlib.h
头文件中 - malloc函数能够动态分配一组连续的存储区,该函需要一个整型参数表示分配的字节数,它的返回值表示分配好的第一个字节的地址,如果内存分配失败就返回NULL,返回值记录在无类型指针存储中,需要强制类型转换成有类型指针才可使用
- 计算机不会主动回收动态分配的内存,因此防止内存泄漏,需要使用free函数释放动态内存分配,该函数需要地址作为参数,另外释放完动态内存后,需要将指针设置成空指针
- calloc函数,需要两个参数,第一个参数为分配存储区的个数,第二个参数时分配存储区的大小;calloc与malloc函数的区别为,前者分配的存储区全部初始化为0,而malloc函数分配的存储区为随机数
- realloc函数需要两个参数,第一个参数为首地址,第二个参数为从首地址上调整的字节个数;若扩充内存大小,原有字节的内容也不会发生改变;若缩小内存大小,则自动舍弃后几个字节的内容
- 局部变量位于栈区,全局变量和静态变量位于常量区,动态分配存储区位于堆区
1 |
|
18.文件操作
查看相关知识
文件操作步骤
打开文件—fopen:该函数需要两个参数,第一个参数文件路径,第二个参数打开方式,返回值为
FILE*
类型,表示打开的文件;如果文件打开失败,则返回NULL- r—只读,文件必须存在,从头开始读
- w—只写,文件不存在就创建,存在就清空,从头开始写
- a—追加,文件不存在就创建,存在不清空,从尾开始写
- r+—读写,文件必须存在,从头开始读写
- w+—读写,文件不存在就创建,存在就清空,从头开始读写
- a+—追读,文件不存在就创建,存在不清空,从头开始读,从尾开始写
操作文件—fwrite/fread
fwrite写入操作,第一个参数是向文件中写入的数据的存储区的首地址,第二个参数是写入元素占据的字节数,第三个参数是写入的元素个数,第四个参数是写入的文件的文件指针,返回值为实际写入的元素个数
fread读操作,第一个参数是从文件中读取的数据要存储到的存储区的首地址,第二个参数是读取的每个元素占据的字节数,第三个参数是要读取的元素个数,第四个参数是要读取文件的文件指针,返回值是实际读取元素的个数
文件读写位置:计算机里为每个打开的文件保留一个整数,这个整数表示下一次文件读写操作的开始位置;这个整数就是文件头到这个位置之间包含的字节个数,这个整数叫做文件的位置指针;每当从文件里获取n个字节或向文件里写入n个字节以后,位置指针都会向后移动n个字节;(1)ftell函数可以用来获取位置指针的数值(返回值为long类型);(2)rewind函数可以把位置指针移动到文件开头;(3)fseek函数可以把位置指针移动到文件里的任何位置,该函数需要三个参数:第一个参数为文件指针,表示要操作的文件;第二个参数为偏移量;第三个参数为基准位置(SEEK_SET:把文件头作为基准位置;SEEK_CUR:把当前位置作为基准位置;SEEK_END:把文件尾作为基准位置)
fprintf将数字记录到文件中,fscanf从文本里获得的数据记录到存储区,这两个函数执行效率比较低,不适合处理大数据量的文件
三个标准文件指针:标准输入—stdin—键盘;标准输出—stdout—终端窗口;标准错误—stderr—终端窗口;
1
2printf("%d\n",a);/**等价于**/fprintf(stdout,"%d\n",a);
scanf("%d",&a);/**等价于**/fscanf(stdin,"%d",&a);
关闭文件—fclose,该函数的参数为文件指针即fopen的返回值;若文件正常关闭,则返回值为0,否则返回值为非0;同时文件指针需要设置为NULL
案例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56/****************************************/
int main(void){
/**************打开文件***********/
FILE* fp= fopen("a.txt","w+");
if(fp==NULL){
printf("can not open the file\n");
return -1;
}
printf("open the file success\n");
/**************文件读写操作***********/
int size=0;
int a[8]={1,2,3,4,5,6,7,8};
int len=sizeof(a)/sizeof(a[0]);
size= fwrite(a,sizeof(int),len,fp);
printf("实际写入%d个数据\n",size);
rewind(fp);
int b[8]={};
size=fread(b,sizeof(int),8,fp);
printf("实际读取%d个数据\n",size);
for(int i=0;i<8;i++)
printf("%d ",b[i]);
printf("\n");
/**************文件读写位置***********/
int c[2]={0};
fseek(fp,8,SEEK_SET);
fread(c,sizeof(int),2,fp);//3 4
printf("%d,%d\n",c[0],c[1]);
fseek(fp,8,SEEK_CUR);
fread(c,sizeof(int),2,fp);//7 8
printf("%d,%d\n",c[0],c[1]);
fseek(fp,-12,SEEK_END);
fread(c,sizeof(int),2,fp);//6 7
printf("%d,%d\n",c[0],c[1]);
printf("current position:%ld\n",ftell(fp));
fclose(fp);
fp=NULL;
return 0;
}
/****************************************/
int main(void){
FILE* fp=fopen("b.txt","r");
int a=0;
char str[20]={};
double d=0.0;
fscanf(fp,"%d%s%lg",&a,str,&d);
printf("a=%d,str=%s,d=%lg\n",a,str,d);
fclose(fp);
fp=NULL;
FILE* fpw=fopen("c.txt","w+");
fprintf(fpw,"%d,%s,%lg\n",a,str,d);
fclose(fpw);
fpw=NULL;
return 0;
}
/****************************************/
数据结构与算法
查看相关知识
一、数据结构
查看相关知识
1.基本概念
查看相关知识
- 数据结构分为逻辑结构与物理结构
- 逻辑结构:数据与数据之间的相互关系;集合结构、线性结构、树形结构(一对多)、图形结构(多对多)
- 物理结构:数据在计算机中的存储形式;顺序存储结构、链式存储结构
2.栈
查看相关知识
栈特点:先进后出,后进先出
1 | //stack.h:栈的各种声明 |
1 | //stack.c:栈的各种定义 |
1 | //main.c:测试 |
3.队列
查看相关知识
队列特点:先进先出,后进后出
1 | //queue.h:队列的声明 |
1 | //queue.c:队列定义 |
1 | //main.c:测试 |
4.单链表
查看相关知识
- 为了便于对链表的操作,会在链表的第一个节点前再附设一个节点称为头节点;最后一个节点再辐射一个节点,称为尾节点;头尾节点不用来存储数据,除头尾节点外的其他节点称为有效节点
pnode
和pfirst
指针变化范围head节点至最后一个有效节点结束;pmid
指针的变化范围从第一个有效节点开始到尾节点结束
1 | //list.h:单链表的声明 |
1 | //list.c:单链表的定义 |
1 | //main.c:单链表测试 |
5.双链表
查看相关知识
- 每个节点设置一个指向前面节点的指针,一个指向后面节点的指针,即双链表
1 | //list.h:双链表的各种声明 |
1 | //list.c:双链表的各种定义 |
1 | //main.c:双链表测试 |
6.二叉树
查看相关知识
- 树中的每个元素称为节点,每个节点最多含有两个子树的树称为二叉树
- 有序二叉树:左子树元素的值小于根节点,右子树的元素值大于根节点
遍历:前序遍历—根左右;中序遍历—左根右;后序遍历—左右根
有序二叉树的删除步骤:找所要删除的二叉树节点,找新的父节点(如果要删除的节点存在左子树,则将其左子树插入到右子树上,如果不存在左子树,则无需操作),提一级(将要删除的节点的父节点指向删除节点的右子树上)
1 | //tree.h:二叉树各种声明 |
1 | //tree.c:有序二叉树各种定义 |
1 | //main.c:有序二叉树测试 |
二、算法
查看相关知识
1.排序算法
查看相关知识
冒牌排序
重复地走过要排序的数列,依次比较俩个元素,若顺序错误,则交换位置,直至无需要交换为止,则排序完成
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//冒泡排序
void bubble_sort(int arr[],int size){
for(int i=0;i<size-1;i++){
int flag=1;
for(int j=0;j<size-i-1;j++){
if(arr[j+1]<arr[j]){
int temp=0;
temp=arr[j];
arr[j]=arr[j+1];
arr[j+1]=temp;
flag=0;
}
}
if(flag)
break;
}
}插入排序
从无序数列中取出数据单独放到有序的数列中,从有序数列的最后一个元素起依次进行扫描比较,因此第一个元素本身有序,无需插入
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
void insert_sort(int data[],int num)
{
for(int i=1;i<num;i++)
{
if(data[i]<data[i-1])
{
int j=i-1;
while(data[j+1]<data[j])
{
int insert_data=data[i];
data[i]=data[j];
data[j]=insert_data;
j--;
}
}
}
}
void main()
{
int data[6]={10,5,30,15,20,60};
insert_sort(data,6);
for(int i=0;i<6;i++)
printf("%d\t",data[i]);
}选择排序
从无序的数列中选择最大的放置有序的内存的第一个,紧接着放置第二大的数,依次类推
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
void select_sort(int data[],int num)
{
for(int i=0;i<num-1;i++)
{
for(int j=i+1;j<num;j++)
{
if(data[i]<data[j])
{
int temp=data[i];
data[i]=data[j];
data[j]=temp;
}
}
}
}
void main()
{
int data[6]={20,15,5,32,1,66};
select_sort(data,6);
for(int i=0;i<6;i++)
{
printf("%d\t",data[i]);
}
}快速排序
快速排序采用的是分治思想,即在一个无序的序列中选取一个任意的基准元素pivot,利用pivot将待排序的序列分成两部分,前面部分元素均小于或等于基准元素,后面部分均大于或等于基准元素,然后采用递归的方法分别对前后两部分重复上述操作,直到将无序序列排列成有序序列
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27void quick_sort(int data[], int left, int right) {
int p = (left+right)/2; //定义基准值对应的下标,可以随意指定
int i = left; //left=0,左边界
int j = right; //right=size-1,右边界
int pivot = data[p]; //暂存基准值
while(i < j) { //进行一次分组,i=j停止分组
//i不做i++运算的两种情况:i=p重合或者i对应的值大于基准值
for(; !(i>=p || pivot < data[i]); i++);
//此种情况就是i对应的值大于基准值而退出了上面的for循环,而不是i=p重合的情况
if(i < p) {
data[p] = data[i]; //将i对应的值挪到p的位置
p = i; //将p调整到i的位置
}
//j不做j--运算的两种情况:j=p重合或者j对应的值小于基准值
for(; !(j<=p || pivot > data[j]); j--);
//此种情况就是j对应的值小于基准值而退出了上面的for循环,而不是j=p重合的情况
if(j > p) {
data[p] = data[j]; //将j对应的值挪到p的位置
p = j;//调整p到j的位置
}
}
data[p] = pivot; //一次分组结束将基准值挪到p的位置
if(p - left > 1)//基准值左边的数据个数大于等于2个继续分组直到剩余1个或者没有无需分组
quick_sort(data, left, p-1);//对左边继续分区,左边界还是0,右边界是p-1,不包含p
if(right - p > 1)//基准值右边的数据个数大于等于2个继续分组直到剩余1个或者没有无需分组
quick_sort(data, p+1, right);//对右边继续分组,右边界还是right,左边界是p+1,不包含p
}
2.查找算法
查看相关知识
线性查找
1
2
3
4
5
6
7//定义线性查找函数
int line_find(int data[], int size, int key) {
for(int i = 0; i < size; i++)
if(data[i] == key)
return i; //找到并且返回对应的下标
return -1; //没有找到
}二分查找
在一个有序表中,每次取中间记录数同目标值进行比较。若该记录数等于目标值,则查找成功;若大于目标值,则说明目标值在该查找表的左半区,此时我们往左半区进行查找;若小于目标值,则说明目标值在该查找表的右半区此时我们往右半区进行查找。如此往复,直至查找成功;若无和目标值相等的记录数,则查找失败
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18//定义递归二分查找函数
static int recu_find(int data[], int left, int right, int key) {
if(left <= right) {
int mid = (left+right)/2; //折半
if(key < data[mid]) //左边找
return recu_find(data, left, mid-1, key);
else if(key > data[mid]) //右边找
return recu_find(data, mid+1, right, key);
else
return mid; //找到了
}
return -1; //没有找到
}
//定义二分查找函数
int half_find(int data[], int size, int key) {
//递归来实现
return recu_find(data, 0, size-1, key);
}