大家好呀,最近学校要考数据结构,虽然我们在日常游戏开发或者其他软件开发中几乎用不到太多数据结构的知识用到也是多叉树 , A* , 深度优先, 广度优先什么的(但是这些东西很多人已经为我们提前做过准备好了),不过技多不压身如是说也 ; =_= ;

这里也是我一直在看的B站的一个老师的视频,也就是说,这些文章其实是我的笔记 狗头

数据结构和算法的定义

定义如下:

数据结构是一种存储,组织数据的方式,旨在便于访问和修改.
没有一种单一的数据结构对所有用途均有效,所以需要知道几种 数据结构优势和局限

算法是一个有穷规则的集合,它用规则规定了解某一特定类型问题的运算序列,
或者规定了任务执行或问题求解的一系列步骤

数据结构和算法属于相互依存的关系,数据结构是算法的基础,算法是数据结构的应用

我们就回顾一下C语言学过的知识吧,但是以后不一定会只用C语言实现,还会用到其他语言

函数

1
2
3
4
5
6
7
8
9
10
11
//函数
//可以实现某个具体功能的代码块
//增加代码的复用性
//降低编程难度
//函数不被调用是不会执行的
//对内隐藏细节,对外暴露接口

//<返回值类型|void>函数名([参数列表]){
// //函数体
// [return 返回值]
//}

这个不多说了,想必大家炉火纯青,什么有参函数无参函数,嗯,全都是最基本的东西

C语言中的字符串

字符串基础知识点

一,字符串末尾有反斜杠0即 ‘\0’,即在C语言中用字符型数组来表示字符串
二,如果直接对字符型数组赋值字符串是不能的,字符串是不能直接赋值的,需要用strcpy 函数来实现,同时加上#include<stdlib.h>头文件

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
#include <stdio.h>
#include <string.h> // 需要包含此头文件使用strcpy等函数
#include <stdlib.h> // 需要包含此头文件使用malloc

int main() {
// 方法1:初始化时直接赋值(最简单安全)
char str1[] = "Hello World";
printf("方法1: %s\n", str1);

// 方法2:使用strcpy函数(常用方法)
char str2[20]; // 确保数组足够大
strcpy(str2, "C Programming");
printf("方法2: %s\n", str2);

// 方法3:使用strncpy函数(更安全,防止溢出)
char str3[10];
strncpy(str3, "Safe Copy", sizeof(str3) - 1);
str3[sizeof(str3) - 1] = '\0'; // 确保字符串结束
printf("方法3: %s\n", str3);

// 方法4:字符指针指向字符串常量
char *str4 = "Pointer String";
printf("方法4: %s\n", str4);
// 注意:str4指向的是常量,不能修改:str4[0] = 'A'; // 错误!

// 方法5:逐个字符赋值
char str5[6];
str5[0] = 'H';
str5[1] = 'i';
str5[2] = '\0'; // 千万别忘记字符串结尾符
printf("方法5: %s\n", str5);

// 方法6:动态分配内存后赋值
char *str6 = malloc(20 * sizeof(char));
if (str6 != NULL) {
strcpy(str6, "Dynamic Memory");
printf("方法6: %s\n", str6);
free(str6); // 使用完必须释放内存
}

return 0;
}

虚拟内存地址

内存条,显卡,各种适配卡都有其各自的存储地址空间
操作系统将这些设备的存储地址空间抽象成一个巨大的一维数组空间
对于内存的每一个字节会分配一个32位或64位的编号,这个编号称为内存地址

这里先用一些代码来直观表述,不过我们不需要理解目前这些代码的意思,等以后学到栈的时候你可以再看看这些东西这样更好理解

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
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

int main() {
// 虚拟内存四大区域演示

// 1. 栈区(Stack)- 局部变量
char stack_str[] = "Stack Memory"; // 内容可修改
char stack_buffer[20];

// 2. 堆区(Heap)- 动态分配
char *heap_str = malloc(20 * sizeof(char));
strcpy(heap_str, "Heap Memory"); // 内容可修改

// 3. 数据段(Data Segment)- 已初始化全局变量
static char data_str[] = "Data Segment";

// 4. 代码段(Text Segment)- 字符串常量(只读)
char *const_str = "Read-Only Constant"; // 指向代码段

// ========= 打印地址信息 =========
printf("=== 虚拟内存地址分布 ===\n");

// 栈区地址(向下增长)
printf("栈区地址:\n");
printf(" stack_str: %p (内容: %s)\n", (void*)stack_str, stack_str);
printf(" stack_buffer: %p\n\n", (void*)stack_buffer);

// 堆区地址(向上增长)
printf("堆区地址:\n");
printf(" heap_str: %p (内容: %s)\n\n", (void*)heap_str, heap_str);

// 数据段地址
printf("数据段地址:\n");
printf(" data_str: %p (内容: %s)\n\n", (void*)data_str, data_str);

// 代码段地址(字符串常量)
printf("代码段地址(只读):\n");
printf(" const_str: %p (内容: %s)\n", (void*)const_str, const_str);
printf(" 字符串常量: %p (内容: %s)\n\n", (void*)"Literal String", "Literal String");

// ========= 地址操作演示 =========
printf("=== 指针操作演示 ===\n");
printf("stack_str[0]的地址: %p,值: %c\n", (void*)&stack_str[0], stack_str[0]);
printf("heap_str指针的地址: %p,值: %p\n", (void*)&heap_str, (void*)heap_str);

// 验证可修改性
stack_str[0] = 'S'; // ✅ 允许修改
heap_str[0] = 'H'; // ✅ 允许修改
// const_str[0] = 'R'; // ❌ 会Segmentation Fault(段错误)

printf("\n修改后:\n");
printf(" stack_str: %s\n", stack_str);
printf(" heap_str: %s\n", heap_str);

free(heap_str); // 释放堆内存
return 0;
}

计算机体系结构

计算机体系结构简化

数组

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
////相同数据类型的集合
////数组的长度一旦定义就不能改变
////数组中的每一个元素可以用 下标表示位置,如果一个为数组中有n个元素
////那么下标的取值范围是0~(n-1)
//
//
////使用取地址符"&"来获取数组的地址时,返回的是数组第0个元素的内存地址
//
//#include <stdio.h>
//
//int main()
//{
// int a[]={16,47,89,42,38};
// printf("%p\n",&a);
// printf("%p\n",&a[0]);
//
//}

数组我们也不说了,这个也是我们所熟知的离不开的东西

不过提起数组还有一个概念我们也是经常使用到的——-那就是数组长度

这就离不开一个关键字 sizeof

这个大致看看就行了,我们主要需要的是c语言的数组的长度求法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include<stdio.h>
int main()
{
// int a[]={16,47,89,42,38};
// printf("%zu\n",sizeof(a));
// printf("%zu\n",sizeof(a[0]));
// int len=sizeof(a)/sizeof(a[0]);
// printf("数组长度为%d\n",len);

int a[]={16,47,89,42,38};
for(int i=0;i<sizeof(a)/sizeof(a[0]);i++){
//TODO
printf("%d\n",a[i]);
}
}

指针

这个在C语言中非常重要其实

指针是用来存放内存地址的变量 ,这里我们就先了解最基本的就行

指针基本符号运用

这里先了解指针的一些基本符号比如“ * ” 和 “ &

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
//int a;    声明一个整型变量
//int *p; 声明一个指针变量,该指针指向一个int类型值的内存地址

//星号*两边的空格无关紧要,下面的声明是等价的
//int* p;
//int *p;
//int * p;
//int*p;

#include <stdio.h>
int main(){
int a=5;
int *p=&a;
printf("a值为%d,地址为%p\n",a,&a);
printf("p值为%p,地址为%p",p,&p);

// int a=5;
// int *p=&a;
// printf("%d\n",*p);
// *p=100;
// printf("%d\n",a);

return 0;
}

//间接应用操作符 " * " 返回指针变量的指向地址的值
//通常把这个操作叫做"解引用指针"
//英文叫做dereferencing a pointer

指针与函数

同样的,指针在函数里使用也是及其广泛的,这里我们就以最经典的swap 交换函数为例

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
#include<stdio.h>
//void swap(int a,int b){
// int temp;
// temp=a;
// a=b;
// b=temp;
// printf("交换后%d %d",a,b);
//}

void swap(int *a,int *b){
int temp;
temp=*a;
*a=*b;
*b=temp;
printf("交换后%d %d",*a,*b);

}
int main(){
int m;
int n;
printf("m值为:");
scanf("%d",&m);
printf("\nn值为:");
scanf("%d",&n);
// swap(m,n);
swap(&m,&n);
printf("\n交换前%d %d",m,n);
return 0;
}

指针与数组

同样的指针在数组里运用也是非常多的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//在C语言中,指针和数组的关系十分密切
//通过数组下标能完成的操作都可以通过指针完成
//一般来说,用指针编写的程序比用数组下标编写的程序执行速度快
#include <stdio.h>
int main(){
int a[]={15,22,67,43,87};
int *p;
p=a;
// printf("%p\n",a);
// printf("%p\n",p);
// printf("%d\n",*p);
int len=sizeof(a)/sizeof(a[0]);
for(int i=0;i<len;i++){
printf("%d\n",a[i]);
}
for(int i=0;i<len;i++){
printf("%d\n",*(p+i));
}
}

结构体

如果你学过Java JavaScript 和C#的话,会发现结构体和 面向对象中的类 是不是很像呢

因此在c中,结构体和typedef使用也是及其广泛的,假如你使用c语言加其他拓展库制作游戏的话,这个绝对是离不开的

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
//结构体是一个或多个变量的集合,这些变量可以是不同的类型
//声明语法


//struct 结构体名 //例如
//{ //struct point
// 数据类型 变量名; //{
// 数据类型 变量名; // int x;
// ...... // int y;
//} //}

//结构体的初始化与调用
//struct 结构体名 变量名;
//例如:struct point p;

//变量名.结构体内部变量名=值;
//例如: p.x=10;
// p.y=15;

//#include <stdio.h>
//
//struct point{
// int x;
// int y;
//};
//
//
//int main(){
// struct point p;
// p.x=5;
// p.y=15;
// printf("%d\n",p.x);
// printf("%d\n",p.y);
// return 0;
//}

#include<stdio.h>

struct point{
int x;
int y;
};
struct point creatPoint(int x,int y){
struct point temp;
temp.x=x;
temp.y=y;
return temp;
}
int main(){
struct point p;
p=creatPoint(5,10);
printf("%d\n",p.x);
printf("%d\n",p.y);
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
//在一些场景中,如果传递给函数的结构体很大,使用指针方式的效率通常更高
//pp指向一个point结构体

//struct point *pp;

//为pp所指向的结构体中的属性赋值
//(*pp).x=10;
//(*pp).y=5;

//为了使用方便,C语言提供了另外一种简写方式
//pp->x=10;
//pp->y=5;

#include <stdio.h>
struct point{
int x;
int y;
};
int main(){
struct point p;
p.x=5;
p.y=10;

struct point *pp;
pp=&p;

(*pp).x=10;
(*pp).y=5;

printf("x=%d,y=%d\n",p.x,p.y);
printf("x=%d,y=%d\n",pp->x,pp->y);
return 0;
}

对了,提起结构体struct就不得不得不提另一个关键字 ——-typedef

这个主要和struct一起使用(毕竟每次使用struct都要写一个struct关键字,非常麻烦,所以我们可以直接用typedef定义的别名尽量来模仿面向对象语言里的类的属性调用的写法)

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
//typedef 数据类型 别名
//例如 typedef int zx

//#include <stdio.h>

//typedef int myTest1;
//typedef char myType2;
//
//int main(){
// myTest1 a=5;
// myType2 b='o';
// printf("%d\n",a);
// printf("%c\n",b);
// return 0;
//}

//每次声明结构体变量都要写struct关键字很麻烦,而且逻辑上也很难受
//typeof可以解决这个问题


//#include<stdio.h>
//struct point{
// int x;
// int y;
//};
//
//int main(){
// struct point p;
// p.x=5;
// p.y=10;
// printf("%d\n",p.x);
// printf("%d\n",p.y);
// return 0;
//}


//typedef struct 结构体名{
// 数据类型 变量名;
// 数据类型 变量名;
//}别名;

//当然结构体的名字可以忽略如下
//typedef struct{
// 数据类型 变量名;
// 数据类型 变量名;
//}别名;


#include<stdio.h>
typedef struct{
int x;
int y;
}po;

int main(){
po p;
p.x=5;
p.y=10;
printf("%d\n",p.x);
printf("%d\n",p.y);
return 0;
}

内存

C程序编译后会以三种形式使用内存

静态/全局内存

静态声明的变量和全局变量使用这部分内存,这些变量在程序开始运行时分配,直到程序终才消失

自动内存(栈内存)

函数内部声明的变量使用这部分内存,在函数被调用时才创建

动态内存(堆内存)

根据需求编写代码动态分配内存可以编写代码释放,内存中的内容直到释放才消失

1
2
3
4
5
6
7
8
//动态内存分配
//在C语言中,动态分配内存的基本步骤:
//1,使用malloc(memory allocate)函数分配内存
// void*malloc(size_t)
// 如果成功,会返回从堆内存上分配的内存指针
// 如果失败,会返回空指针
//2,使用分配的内存
//3,使用free函数释放内存

这里就用具体的代码来看看如何使用malloc函数吧

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
//指针和字符串
//#include<stdio.h>
//#include<stdlib.h>
//#include<string.h>
//int main(){
//// int *p;
//// p=(int*)malloc(sizeof(int));
//// *p=15;
//// printf("%d\n",*p);
//// free(p);
//// return 0;
//
// char *s;
// s=(char*)malloc(10);
// strcpy(s,"Hello");
// printf("%s\n",s);
// return 0;
//}


//指针与数组
//#include<stdio.h>
//#include<stdlib.h>
//int main(){
// int *arr=(int*)malloc(5*sizeof(int));
// for(int i=0;i<5;i++){
// arr[i]=0;
// }
// for(int i=0;i<5;i++){
// printf("%d\n",arr[i]);
// }
// return 0;
//}

#include<stdio.h>
#include<stdlib.h>

typedef struct{
int x;
int y;
}po;

int main(){
po *p;
p=(po*)malloc(sizeof(po));
p->x=5;
p->y=10;
printf("%d\n",p->x);
printf("%d\n",p->y);
}

文件的存储与读取

这个由于是我们在学数据结构所以不提了,包括C语言数据类型什么的 switch case什么的 循环什么的 if else什么的 宏定义什么的也不需要多说了,因为我不仅仅只用c语言来写数据结构

以后会把这些挖的坑填上,OK,随便扯了一些主要的基本回顾知识 see ya ~ ~ ~

f19c93c1cba523c7c5ddb3f67b512a2d355275186