如何把现实中大量而复杂的问题以特定的数据类型和特定的存储结构保存到主存储器(内存)中,以及在此基础上为实现某个功能(比如查找、删除某个元素,对所有元素排序)而执行的相应操作,这个操作也叫算法。
特定的数据类型: 个体元素
特定的存储结构:个体和个体之间的关系
数据结构=个体+个体的关系
算法=对存储数据的操作
算法:解题的方法和步骤
1.时间复杂度
大概程序执行的次数,而非执行时间。
2.空间复杂度
算法执行过程中大概所占用的最大内存。
3.难易程度
4.健壮性
数据结构是软件心中最核心的课程
程序=数据结构+数据的操作+可以被计算机执行的语言
1.什么是堆?什么是栈?
很多人以为是内存里的2块儿空间,一块儿叫堆,一块叫栈,其实不对。实际上是指内存分配的方法不同的2种方式。如果是压栈出栈的方式分配和释放的内存就叫栈内存。如果是以堆排序分配的内存就叫堆内存。
**注意:数据结构里是没有堆这个概念的,堆是什么?是分配内存的一种方式,而不是存储数据的一种结构。
2.函数调用,如何调用呢?
压栈和出栈。
1.指针
指针的重要性
指针是C语言的灵魂
定义
指针
指针就是地址,地址就是指针。
指针的本质是一个操作受限的非负整数
指针变量
指针变量是存放内存单元地址(编号)的变量
指针的分类
1.基本类型的指针
#include
int main(void)
{
int *p; //p是个变量名字,int * 表示该P变量只能存储int类型变量的地址
int i = 10;
int j;
//(1)❌此时p还没有被赋值,里面是个垃圾值,这个垃圾值很可能正好是某个变量的地址
//所以应该在使用 *p 之前给p赋值地址:p = &i;
j = *p;
//(2)❌ 给垃圾值地址的变量赋值一个新值,垃圾值应不受你控制的,随意改内存很危险 。
//若是 先写了p = &i; 则下面等价于 i = i;
*p = i;
}
#include
change_arr(int *p, int len)
{
p[0] = -1; //p[0] = *(p + 0) = *p
p[2] = -9; //p[2] = *(p + 2) = *(a + 2) = a[2]
}
int main(void)
{
int a[5] = {
1, 2, 3, 4, 5};
printf("%d\n", a);
printf("%d\n", a + 1); //#####70 int型占4个字节
printf("%d\n", a + 2); //#####74
printf("%d\n", a + 3); //#####78
a + n = addr(a) + n * sizeof(typeof(a))
change_arr(a, 5);
printf("%d\n", a[0]); // -1
printf("%d\n", a[2]); //-9
}
#include
int main(void)
{
doueble arr[5] = {
1.1, 2.2, 3.3, 4.4, 5.5};
double *p = &arr[0];
printf("%p\n", p); // %p以16进制的形式输出地址(012FF5C)
p = &arr[1];
printf("%p\n", p); // %p以16进制的形式输出地址(012FF64)64-5C=8字节
//int 类型的话差4个,因为int 是4字节长度
return 0;
}
无论要通过函数改写什么类型的值,只需要传递它的地址就行了。
#include
int main(void)
{
int i = 10;
int *p = &i; // int * p; p = & i;
printf("%p\n", p);
f(&p);
printf("%p\n", p);
return 0;
}
void f(int *q)
{
*q = (int *)0 x FFFFFFFF;
}
为了表示一些复杂的数据,而普通的基本类型变量无法满足需求。
结构体是用户根据实际需要自己定义的复合数据类型。结构体给人感觉模拟事物模拟的不彻底(没有办法)但是仍然有好处;以算法为核心,血手结构。算法在面向过程的结构语言里最好。而面向对象的语言算法就不是其核心了。
3.如何使用结构体
两种方式:
struct Student st = {1000,"LeeQiang",20};
struct Student * pst = &st;
#include<stdio.h> struct Student
{
int sid;
char name[20];
int age;
};
int min(void)
{
struct Student st = {
1,“LeeQiang” , 20};
printf("%d %s %d\n", st.sid, st.name, st.age);
//第一种访问方式
st.sid = 99;
// st.name = "LeeSi" // ❌error
strcpy(st.name, "LeeSi");
st.age = 28;
printf("%d %s %d\n", st.sid, st.name, st.age);
struct Student *pst; //第二种方式最常用
pst = &st;
pst->age = 22; // pst->age等价于 (*pst).age 而(*pst).age=st.age 所以pst->age = st.age;
} //这种方式好,因为只需要4个字节的参数变量
void printStudent2(struct Student *st)
{
printf("%d %s %d\n", st.sid, st.name, st.age);
}
结构体的2种变量方式
1.st.age
2.pst ->age //pst 所指向的结构体变量中的age这个成员;
注意事项
1.结构体变量不能相互之间加减乘除,但能相互赋值。
2.普通结构体变量和结构体变量指针变量作为函数传参问题。
假设构造一个 int 型的一维数组
int len; int * pArr = (int *)malloc(sizeof(int *)len)
静态内存不可跨函数使用。
静态内存在函数执行期间可以被其他函数所使用。
静态内存在函数执行完毕之后就不能在被其他函数使用。
动态内存可以跨函数使用
动态内存在函数执行完毕之后仍可以被其他函数使用,除非使用free()方法动态分配的内存
#include
#include
int main(void)
{
int a[5] = {
4, 10, 2, 8, 6};
int len;
printf("请输入需要分配的数组长度:len = ");
scanf("%d", &len);
int *pArr = (int *)malloc(szieof(int *) len);
//* pArr = 4; //类似于 a[0] = 4;
// pArr[1] = 10; //类似于 a[1] = 10;
// printf("%d%d\n",*pArr,pArr[1]);
//我们可以把pARR 当做一个普通数组来使用
for (int i = 0; i < len; ++i)
scanf("%d", &pArr[i]);
for (i = 0; i < len; ++i;)
printf("%d\n", *(pArr + i));
free(pArr); // 把pArr所代表的动态分配的20个字节的内存释放
return 0;
}
#include
struct Student
{
int sid;
int age;
};
struct Student *CreateStudent(void);
void ShowStudent(struct Student *);
int main(void)
{
struct Student *pS;
pS = CreateStudent();
ShowStudent(ps);
return 0;
}
void ShowStudent(struct Student *pS)
{
printf("%d%d\n", pst->sid, pst->age);
}
struct Student *CreateStudent(void)
{
struct Student *p = (struct Student *)malloc(sizeof(struct Student));
p->sid = 99;
p->age = 88;
return p;
}