1. 数据结构的概述
1.1 数据结构的定义
我们如何把现实中大量而复杂的问题以特定的数据类型(个体)和特定的存储结构(个体间的关系)保存到计算机的内存中,以及在此基础上为了实现某个功能而执行的相应的操作,这个相应的操作也叫作算法。
- 数据结构 = 个体 + 个体间的关系
- 算法 = 对存储数据的操作
- 解题的方法和步骤
衡量算法的标准
1.3 指针
int main(void){
// p是个指针变量的名字,int表示该p变量只能存储int类型的变量地址,p会有一个默认随机地址,此时p则代表了一个随机地址对应的数据值
int *p;
int i = 10;
int j;
// &i为取i的地址,将i的地址赋给了p,p就指向了i,*p就是取i的值,修改i的值不会影响p的值,修改p的值不会影响i的值,但是会影响p的指向p = &i;j = *p; // *p就代表i的值,等价于j=iprintf("i=%d,j=%d,*p=%d\n",i,j,*p);return 0 ;
}
<a name="JkqF9"></a>#### 1.3.2.1 如何通过被调函数修改主调函数中普通变量的值- 实参为相关变量的地址- 形参为以改变了的类型为类型的指针变量- 在背调函数中通过 *形参变量名 的方式就可以修改主调函数变量的值```python# include <stdio.h>// 如何通过被调函数修改主调函数中普通变量的值// - 实参为相关变量的地址// - 形参为以改变了的类型为类型的指针变量// - 在背调函数中通过 *形参变量名 的方式就可以修改主调函数变量的值void f(int *p){ // 定义了一个指针类型的形参p,p可以接收int类型变量的地址// 将100赋值给指针变量p所指向的地址的变量,也就是将传入函数f的地址所对应的变量的值修改为100*p = 100;}int main(void){int i = 9;f(&i);printf("i=%d",i);return 0;}
1.3.3 指针和一维数组
1.3.3.1 数组名:
一维数组名是一个指针常量,它存放的是一维数组的第一个元素的地址,他的值不能被改变
一维数组名指向的是数组的第一个元素
1.3.3.2 数组下标和指针的关系
a[i] == *(a+i)
假设指针变量的名字为p,则p+i的值是第i+1个元素的地址
指针变量的运算
指针变量不能进行加,乘除运算
如果两个指针变量同属于一个数组则可以相减
指针变量可以加上或者减去一个数字
# include <stdio.h>int main(void){int a[5] = {1,2,3,4,5};printf("a[0]=%d,*a=%d\n",a[0],*a);printf("%p\n",a);printf("%p\n",a+1);return 0;}
1.3.3.3 如何通过被调函数修改主调函数中的一维数组
// 如何通过背调函数修改主调函数中的一位数组 // 两个参数 存放数组首元素的指针变量 // 存放数组元素长度的整型变量 void showArray(int p, int len){ // p[i]就等于a[i] int i =0; for(i=0;i<len;i++) printf(“a[%d]=%d\n”,i,(p+i)); // p[i]等价于*(p+i) }
int main(){ int a[5] = {1,2,3,4,5}; showArray(a,5);
return 0;
}
<a name="EfoYd"></a>## 1.4 结构体<a name="ebSWv"></a>### 1.4.1 为什么会出现结构体为了表示一些复杂的数据,而普通的基本类型无法满足<a name="8cSco"></a>### 1.4.2 什么是结构体结构体是用户根据实际需求自己设计的复合数据类型<a name="mRXhH"></a>### 1.4.3 如何使用结构体```c// struct Student st = {1000,"张三",20};struct Student st;st.sid = 1001;strcpy(st.name,"李四");st.age = 21;struct Student * pst = &st;
1.4.3.1 方式一:st.sid
1.4.3.2 方式二:pst -> sid :pst所指向的结构体变量中的sid这个成员
struct Student{ // 结构体名称首字母大写与java中的类一样 // 结构体中可以有多个成员 int sid; char name[50]; int age; }; // 结构体反括号后面的分号不能省略,必须写上
int main(void){ // 定义struct Student类型的结构体变量 // struct Student st = {1000,”张三”,20}; 直接赋值 // 先定义,后赋值 struct Student st; // 在定义结构体变量st的时候操作系统就为st分配了内存,内存大小为58个字节 st.sid = 1001; strcpy(st.name,”李四”); // st.name = “李四”; C语言中不能直接这样赋值 需要使用字符串复制函数strcpy(变量名,变量值)进行赋值 st.age = 21; printf(“%d %s %d”,st.sid,st.name,st.age); return 0; }
- code2:用指针访问结构体变量```c# include <stdio.h>struct Student{ // 结构体名称首字母大写与java中的类一样// 结构体中可以有多个成员int sid;char name[50];int age;}; // 结构体反括号后面的分号不能省略,必须写上int main(void){// 定义struct Student类型的结构体变量struct Student st = {1000,"张三",20}; // 直接赋值// 结构体名.成员名的方式访问// struct Student st;// st.sid = 1001;// strcpy(st.name,"李四"); // st.name = "李四"; C语言中不能直接这样赋值 需要使用字符串复制函数strcpy(变量名,变量值)进行赋值// st.age = 21;// 指针访问结构体struct Student * pst; // pst->sid 等价于 (*pst).sid 而(*pst).sid等价与st.sid 所以pst->sid等价于st.sidpst = &st;pst -> sid = 1002;printf("%d",*pst);return 0;}
struct Student{ // 结构体名称首字母大写与java中的类一样 // 结构体中可以有多个成员 int sid; char name[50]; int age; }; // 结构体反括号后面的分号不能省略,必须写上
// 通过指真修改结构体变量的成员的值
void f(struct Student pst){
(pst).sid = 1001; // pst 为指针变量 (*pst)就是指针pst指向的那个变量
strcpy(pst->name,”张三”);
pst->age = 20;
}
// 直接传递结构体变量的方式 // 打印结构体变量 这种在函数列表里 直接传递struct Student st 这种类型的结构体变量,对内存的消耗很大,因为struct Studen // 这个结构体他需要的内催空间为两个int型变量的空间加上一个长度为50的char类型的空间,所以这种传参的方式会在函数调用的时候 // 需要向计算机内存空间申请58个字节的空间,所以我们应该把这个参数写成用指针的形式,因为32位的电脑的话指针固定只占4个字节 void g(struct Student st){ printf(“%d, %s, %d\n”,st.sid,st.name,st.age); }
// 用指针的方式打印 void pg(struct Student *pst){ printf(“%d, %s, %d\n”,pst->sid,pst->name,pst->age); }
int main(void){ // 定义struct Student类型的结构体变量 struct Student st; // 定义结构体变量 st 给结构体变量st分配内存空间
f(&st);printf("%d, %s, %d\n",st.sid,st.name,st.age);g(st);pg(&st);return 0;
}
<a name="1Gktr"></a>## 1.5 动态内存的分配和释放动态内存分配和释放(动态分配的内存一定要手动释放,否则造成内存泄露。)<a name="5hJ4K"></a>### 1.5.1 malloc(需要的字节数)动态内存分配<a name="mYjjN"></a>#### 1.5.1.1 code1:malloc()简单示例```c# include <stdio.h># include <malloc.h>// malloc(需要的字节数)动态内存分配int main(void){int a[5] = {4,10,2,8,6}; // 静态内存分配,数组a固定了长度为5,给数组a分配了20个字节,程序运行期间不能动态增加int len;printf("请输入你需要分配的数组的长度:len=");scanf("%d",&len);// sizeof(int),代表求的是int类型所占的字节数 在乘以 len 的长度就可以得到需要的内存大小// malloc()函数只返回获得的存储空间的第一个字节的地址// 所以(int *) 则表示通过malloc()获得的这么多个字节的空间用来分配给什么类型的变量来使用// 这里就表示分配给int类型的变量来使用,// 那么就可以通过获得总共空间的字节数,除以类型的字节大小,就可以知道可以存多少该类型的变量了int *pArr = (int *)malloc(sizeof(int) * len);*pArr = 4; // 假设有一个数组叫做Arr,则*pArr = 4 就类似于 Arr[0] = 4pArr[1] = 10; // 就类类似于Arr[1] = 10*(pArr+2) = 5; // 就类似于Arr[2] =5,这个时候其实pArr就可以当做数组名来使用,因为它*pArr指针变量就指向了malloc()获得的空间的第一个位置printf("%d %d",*pArr,pArr[2]);// 这个时候就就可以把pArr当做一个数组来使用for(int i=0; i<len; i++)pArr[i] = i*i;for(int i=0; i<len; i++)printf("%d\n",*(pArr+i));free(pArr); // 主动释放内存return 0;}
1.5.1.2 深刻理解动态内存的申请和释放
# include <stdio.h># include <malloc.h>void main(void){int *pArr = (int *)malloc(sizeof(int)*4);printf("打印指针地址:%d\n",pArr);printf("打印指针长度:%d\n",sizeof(pArr));for (int i=0; i<4; i++)pArr[i] = i;printf("打印指针的第一个数:%d\n",pArr[0]);free(pArr);printf("释放内存后打印指针的第一个数:%d\n",pArr[0]);printf("释放内存后打印指针地址:%d\n",pArr);printf("释放内存后打印指针长度:%d\n",sizeof(pArr));return 0;}
1.5.2 跨函数使用内存
1.5.2.1 静态局部变量
# include <stdio.h>int f(){int j = 20;return j;}int main(void){int i = 10;i = f();printf("i=%d\n",i);/*在静态变量的情况下,主调函数调用完背调函数,被调函数中的变量就会被操作系统释放如果是在动态分配内存的情况下,如果不在背调函数中主动释放内存,那么当主调函数调用完了被调函数,背调函数中的局部变量将继续在内存中,不会被操作系统收回*/for(int i=0; i<200; i++)f();return 0;}
1.5.2.2 跨函数使用内存
# include <stdio.h># include <malloc.h>struct Student{int sid;int age;};struct Student * creatStudent(){struct Student *ps = (struct Student *)malloc(sizeof(struct Student));ps->age = 20;ps->sid = 1001;return ps;}void showStudent(struct Student *ps){printf("%d %d\n",ps->sid,ps->age);}// 跨函数使用内存void main(void){struct Student *ps;ps = creatStudent();showStudent(ps);free(ps);printf("%d",ps);return 0;}
1.6 typedef 别名
/** @Descripttion:* @version: Beta-v1.0.0* @Author: jhong.tao* @Date: 2020-11-29 18:52:00* @LastEditTime: 2020-11-29 19:03:06*/# include <stdio.h>typedef struct Student{int sid;char name[40];char sex;}* PST,ST; // PST 等价于 struct Student * , 也就是说PST为一个结构体指针类型;ST等价于struct Studentint main(void){// struct Student st;ST st; // 等价于struct Student st;PST ps = &st; // 等价于 struct Student * psps->sid =1001;printf("%d",st.sid);return 0;}
2. C语言C如何在一个文件里调用另一个源文件中的函数
当程序大了代码多了之后,想模块化开发,不同文件中存一点,是很好的解决办法,那我们如何做才能让各个文件中的代码协同工作呢?我们知道,main函数是程序入口,我们希望把不同的功能写在不同的函数中,并把这些函数统一放到另外一个文件里,以便main函数显得太长,main函数可以在用到某方法的时候调用来处理。为了实现这个步骤,我们这样做。
首先定义一个c代码的头文件,如function.h,在里面声明将要实现的函数,如int add(int a,int b);
然后新建一个源文件为function.c,在function.c的开头#include “function.h”,然后下面写头文件中已声明的函数的实现。
这样写完了之后,main函数如果要调用这个源文件中的函数,只需要在main函数的开头部分加入#include
2.1 实现步骤
int add(int a,int b);
2. 创建工具函数文件:function.c```c// 文件名function.c#include<function.h>int add(int a,int b){return a+b;}
int main(){ int a = 1,b =2; int c = add(a,b); //这里是对function.c中的add函数的调用 printf(“c=%d”,c);
return 0;
} ```
