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=i
printf("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.sid
pst = &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] = 4
pArr[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 Student
int main(void){
// struct Student st;
ST st; // 等价于struct Student st;
PST ps = &st; // 等价于 struct Student * ps
ps->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;
} ```