数据结构补习
一、内存空间划分
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int a;//全局,未初始化,.bss段
int b=10;//全局,初始化 .data段
static int c;//静态变量,未初始化 .bss段
static int k=10;//静态变量 ,初始化 .data段
const int e=10;//全局变量 初始化 .ro段
int *p=(int *)malloc(4);//报错,全局不可以调用函数
char str[]="hello"//str全局 初始化 str空间在.data段,字符串hello在只读段,把只读段的值复制一份给str //可以通过str修改值
char *p="hello"//全局 初始化 p在.data段,字符串常量hello在ro段,指针p直接指向字符串常量的首地址 //不可以改变
int main(int argc, const char *argv[])
{
int a;//局部变量 栈区
int b=10;//局部变量 栈区
static int c;//静态变量 在静态区,未初始化在.bss段
static int k=10;//静态变量 在静态区 ,初始化在.data段
const int e=10;//cons修饰的局部变量在栈区
int *q=(int *)malloc(4);//q在栈区,malloc申请4字节空间在堆区,通过指针q指向堆区空间的首地址
char str[]="hello"//str在栈区,字符串常量hello在只读段,把hello复制一份给str
char *p="hello"//p在栈区,字符串hello在只读段,通过指针p指向只读段hello的首地址
/*
//计算栈区的地址:从大到小申请空间
int a;
char b;
printf("&a=%p\n",&a);
printf("&b=%p\n",&b);
//计算堆区的地址:从小到大申请空间
int *p=(int *)malloc(4);
char *q=(char *)malloc(1);
printf("p=%p\nq=%p\n",p,q);
*/
return 0; }
二、动态申请和释放空间
typedef unsigned int size_t unsigned int==> size_t
动态申请:
头文件:#include <stdlib.h>
格式:
void *malloc(size_t size); 0x10--0x90 0x50
返回值:当在堆区申请失败默认返回NULL,
如果申请成功,默认返回堆区空间的首地址,注意在使用时,
必须保存堆区的首地址,方便后期空间释放,否则会造成
空间泄露问题。
参数:size_t size: size_t:unsigned int]
申请空间大小字节
使用格式:
1.单个分配空间
int *p=(int *)malloc(sizeof(int));
int *q=(int *)malloc(sizeof(int));
注意:p和q只是一个简单的指针,不可以偏移
2.多个空间申请
int *p=(int *)malloc(sizeof(int)*5);
注意:当申请多个空间时,就可以指针偏移
动态释放:
头文件:#include <stdlib.h>
格式:
void free(void *ptr);
返回值:无返回值函数
参数:void *ptr: 需要释放空间的指针
使用格式:free(p);//释放:堆区的空间不被p指向,但是空间还在
p=NULL;//防止p为野指针
野指针
1.直接使用未初始化的指针
int *p;
printf("*p=%d",*p);
2.指针指向数组,通过指针越界访问
int arr[5];
int *p=&arr[5];
printf("*p=%d\n",*p);
3.指针函数,返回局部变量的地址
int *fun()
{
int arr[3];
return arr;//因为arr是局部变量,调用函数分配空间,
//函数调用结点空间释放,当返回地址时,那么就是野指针
}
4.指着指向堆区的空间,释放空间以后,就是野指针
int *p=(int *)malloc(sizeof(int)*5);
free(p);//此时p没有指向,则p不可以在使用堆区的首地址,变成野指针
三、类型重定义typedef
作用:起别名,起小名
格式:typedef 数据类型 别名;
数据类型:基本类型、构造类型、指针、空类型
别名:满足命名规范,可以是多个
3.1 类型推导
数据定义:
int a;
int arr[10];
int arr[2][3]
int *p
int **p
int (*p)[3]
int *p[3]
int (*p)(int a,int b)
int (*p[2])(int a,float b)
数据类型:
int ;
int [10];
int [2][3]
int *
int **
int (*)[3]
int *[3]
int (*)(int ,int )
int (*[2])(int ,float )
typedef和数据类型结合
typedef int a;//int --->a
typedef int arr[10];//arr --->int [10]
typedef int arr_t[2][3]//arr_t -->int [2][3]
typedef int *p_t
typedef int **p
typedef int (*p)[3]
typedef int *p[3]
typedef int (*p)(int ,int )
typedef int (*p[2])(int ,float )
练习:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
typedef int size_4;//int-->size_4
typedef int arr_t[5];//arr_t --->int [5]
int main(int argc, const char *argv[])
{
int a=10;
// size_4 c=100;
// printf("c=%d\n",c);
arr_t b={11,22,33,44,55};//int b[5]
for(int i=0;i<5;i++)
{
printf("%d\t",b[i]);
}
return 0;
}
3.2 宏和类型重定义的区别
typedef int size_4 等价 #define size_4 int
1>宏只属于宏替换,类型重定义属于类型重定义
2> 宏不属于C语句,类型重定义属于C语句
3> 宏在预处理阶段,类型重定义在编译阶段
4> 宏只可以做简单类型的替换,但是类型重定义可以重定义任意一种类 型
四、结构体struct
结构体:存储多个类型相同或不同的构造数据类型
构造类型:可以拆分
定义格式:
struct 结构体名
{
数据类型 成员1;
数据类型 成员2;
.....
数据类型 成员n;
};
//定义学生:姓名 年龄 分数 性别...
struct student
{
char name[10];
int age;
float score;
char sex;
};
struct student a;
1,struct: 结构体的关键字
2,结构体名:满足命名规范,可有可无
3,{}不可以省略
4,数据类型:基本类型、构造类型、指针、空类型
5,成员变量的个数任意
6,结构体后面的分号必须存在
7,结构体的描述位置任意,一般在全局
8,结构体描述计算机不分配空间,直到定义结构体变量才分配
4.1 结构体普通变量
4.1.1 定义结构体同时定义变量,并初始化
1> 按顺序初始化
struct student
{
char name[10];
int age;
float score;
char sex;
}stu={"张三",12,99,'M'};
2> 不按顺序初始化
struct student
{
char name[10];
int age;
float score;
char sex;
}stu={.sex='w',.age=12,.name="李四",.score=100};
3> 定义结构体变量后一个一个初始化
struct student
{
char name[10];
int age;
float score;
char sex;
}stu;
结构体不可以直接全部引用
stu:表示结构体的所有信息
stu.name//表示姓名
strcpy(stu.name,"李四");
stu.age=22;
stu.score=100
stu.sex='m'
4> 结构体输入和输出
struct student
{
char name[10];
int age;
float score;
char sex;
}stu;
输入:
//输入姓名
scanf("%s",stu.name);
//输入年龄
scanf("%d",&stu.age);
//输入分数
scanf("%f",&stu.score);
//输入性别
scanf("%c",&stu.sex);
printf("姓名=%s 年龄=%d 分数=%f 性别=%c\n",stu.name,stu.age,stu.score,stu.sex)
5> 只有当直接定义时,才可以省略结构体名
struct
{
char name[10];
int age;
float score;
char sex;
}stu;
练习:
struct student
{
char name[20];
int age;
float score;
char sex;
}stu1={"张三",12,100,'M'},//方法1:按顺序赋值
stu2={.age=11,.name="李四"},//方法2:不安顺序赋值
stu3,stu4;
void output(struct student stu)
{
printf("姓名=%s 年龄=%d 分数=%f 性别=%c\n",stu.name,stu.age,stu.score,stu.sex);
}
int main(int argc, const char *argv[])
{
//方法3:使用结构体变量单个赋值
strcpy(stu3.name,"望去");
stu3.age=34;
stu3.score=90;
stu3.sex='W';
//方法4:使用结构体变量输入赋值
scanf("%s",stu4.name);
scanf("%d",&stu4.age);
scanf("%f",&stu4.score);
scanf(" %c",&stu4.sex);
output(stu1);//结构体变量名做参数
output(stu2);
output(stu3);
output(stu4);
return 0;
}
4.1.2 定义结构体后定义变量,并初始化[重点]
1> 按顺序初始化
struct student
{
char name[10];
int age;
float score;
char sex;
};
struct student stu={"张三",12,99,'M'};
2> 不按顺序初始化
struct student
{
char name[10];
int age;
float score;
char sex;
};
struct student stu={.sex='w',.age=12,.name="李四",.score=100};
3> 定义结构体变量后一个一个初始化
struct student
{
char name[10];
int age;
float score;
char sex;
};
struct student stu;
结构体不可以直接全部引用
stu:表示结构体的所有信息
stu.name//表示姓名
strcpy(stu.name,"李四");
stu.age=22;
stu.score=100
stu.sex='m'
4> 结构体输入和输出
struct student
{
char name[10];
int age;
float score;
char sex;
};
struct student stu;
输入:
//输入姓名
scanf("%s",stu.name);
//输入年龄
scanf("%d",&stu.age);
//输入分数
scanf("%f",&stu.score);
//输入性别
scanf("%c",&stu.sex);
printf("姓名=%s 年龄=%d 分数=%f 性别=%c\n",stu.name,stu.age,stu.score,stu.sex)
4.2 结构体数组
4.1.1 定义结构体同时定义变量,并初始化
1> 按顺序初始化
struct student
{
char name[10];
int age;
float score;
char sex;
}arr1[2]={{"张三",12,100,'m'},{"李四",13,98,'w'}},
arr2[2]={"张三",12,100,'m',"李四",13,98,'w'} ,【多使用】
arr3[2]={[0]={"张三",12,100,'m'},[1]={"李四",13,98,'w'}} ;
2> 不按顺序初始化
struct student
{
char name[10];
int age;
float score;
char sex;
}arr3[2]={[0]={.name="张三",.sex='m'},[1]={.age=12,.name="李四"}} ;
3> 直接定义结构体数组,单个赋值
struct student
{
char name[10];
int age;
float score;
char sex;
}arr[2];
arr[0]//表示第一个的所有信息
arr[0].name//表示第一个人的姓名
arr[1].sex//表示第二个人的性别
strcpy(arr[0].name,"李四");
arr[0].age=12;
arr[0].score=100;
arr[0].sex='w';
strcpy(arr[1].name,"李四");
arr[1].age=12;
arr[1].score=100;
arr[1].sex='w';
4> 结构数组循环输入输出
struct student
{
char name[10];
int age;
float score;
char sex;
}arr[2];
for(int i=0;i<2;i++)
{
scanf("%s",arr[i].name);
scanf("%d",&arr[i].age);
scanf("%f",&arr[i].score);
scanf("%c",&arr[i].sex);
}
4.1.1 定义结构体同后定义变量,并初始化【重点】
1> 按顺序初始化
struct student
{
char name[10];
int age;
float score;
char sex;
};
struct student arr1[2]={{"张三",12,100,'m'},{"李四",13,98,'w'}},
arr2[2]={"张三",12,100,'m',"李四",13,98,'w'} ,【多使用】
arr3[2]={[0]={"张三",12,100,'m'},[1]={"李四",13,98,'w'}} ;
2> 不按顺序初始化
struct student
{
char name[10];
int age;
float score;
char sex;
};
struct student arr3[2]={[0]={.name="张三",.sex='m'},[1]={.age=12,.name="李四"}} ;
3> 直接定义结构体数组,单个赋值
struct student
{
char name[10];
int age;
float score;
char sex;
};
struct student arr[2];
arr[0]//表示第一个的所有信息
arr[0].name//表示第一个人的姓名
arr[1].sex//表示第二个人的性别
strcpy(arr[0].name,"李四");
arr[0].age=12;
arr[0].score=100;
arr[0].sex='w';
strcpy(arr[1].name,"李四");
arr[1].age=12;
arr[1].score=100;
arr[1].sex='w';
4> 结构数组循环输入输出【重点】
struct student
{
char name[10];
int age;
float score;
char sex;
};
struct student arr[2];
for(int i=0;i<2;i++)
{
scanf("%s",arr[i].name);
scanf("%d",&arr[i].age);
scanf("%f",&arr[i].score);
scanf("%c",&arr[i].sex);
}
练习:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
struct student
{
char name[10];
int age;
float score;
char sex;
};
void output(struct student arr[])
{
for(int i=0;i<2;i++)
{
printf("姓名:%s 年龄:%d 分数:%.1f 性别:%c\n",arr[i].name,arr[i].age,arr[i].score,arr[i].sex);
}
printf("\n");
}
int main(int argc, const char *argv[])
{
struct student arr1[2]={{"张三",12,100,'m'},{"李四",13,98,'w'}},//方法1
arr2[2]={"张三",12,100,'m',"李四",13,98,'w'} ,//【多使用】方法1
arr3[2]={[0]={"张三",12,100,'m'},[1]={"李四",13,98,'w'}},//方法1
arr4[2]={[0]={.age=12,.name="kkk"},[1]={.score=100,.name="ppp"}},//方法2
arr5[2],arr6[2];
//方法3:
strcpy(arr5[0].name,"李四");
arr5[0].age=12;
arr5[0].score=100;
arr5[0].sex='w';
strcpy(arr5[1].name,"李四");
arr5[1].age=12;
arr5[1].score=100;
arr5[1].sex='w';
//方法4: 重点
for(int i=0;i<2;i++)
{
scanf("%s",arr6[i].name);
scanf("%d",&arr6[i].age);
scanf("%f",&arr6[i].score);
scanf(" %c",&arr6[i].sex);
}
output(arr1);
output(arr2);
output(arr3);
output(arr4);
output(arr5);
output(arr6);
return 0;
}
练习2:定义结构体数组存储5辆车的信息:品牌,价格,颜色
1定义函数实现循环输入
2定义函数,计算最贵的车品牌和价格
思路:计算价格的最大值,得到下标,在根据下标输出品牌和单价
3定义函数,对5辆车的单价实现排序
思路:价格冒泡排序,但是在交换时需要交换,车的所有信息
struct car temp;
if(arr[j].price >arr[j+1].prce)
{
t= arr[j];
arr[j]=arr[j+1];arr[j+1]=t
}
4,定义函数,实现输出
4.3 结构体指针【重点】
结构体指针使用"->"调用
地址使用-> 值使用“.”
1> 直接和间接定义
struct student
{
char name[10];
int age;
float score;
char sex;
}*p;//直接定义
struct studnet *p;//间接定义
2> 指针的使用
2.1 结构体指针指向普通变量的地址
struct student stu={"张三",14,100,'W'};
struct student *p=&stu;
p->name 等价于 stu.name: 表示名字 (*p).name &stu->name
p->sex 等价于 stu.sex :表示性别
2.2结构体指针指向结构体数组的首地址
struct student stu[2]={"李四",12,98,'m',"小王",15,100,'W'};
struct student *p=stu;
for(int i=0;i<2;i++)
{
重点printf("%s %d %f %c",(p+i)->name,(p+i)->age,(p+i)->score,(p+i)->sex);
了解printf("%s %d %f %c",(*(p+i)).name,(*(p+i)).age,(*(p+i)).score,(*(p+i)).sex);
}
2.3结构体指针指向堆区的空间【重点】
int *p=(int *)malloc(sizeof(int));
//结构体指针指向单个的结构体空间
struct student *p=(struct student *)malloc(sizeof(struct student));
//结构体指针指向连续的结构体空间
struct student *p=(struct student *)malloc(sizeof(struct student)*5);
练习:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
struct studnet
{
char name[20];
int age;
char sex;
};
struct studnet * create(int n)
{
struct studnet *p=(struct studnet *)malloc(sizeof(struct studnet)*n);
if(p==NULL)
{
return NULL;
}
//成功
return p;
}
void input(struct studnet *p,int n)
{
for(int i=0;i<n;i++)
{
printf("请输入姓名:");
scanf("%s",(p+i)->name);
printf("亲输入年龄:");
scanf("%d",&(p+i)->age);
printf("请输入性别:");
scanf(" %c",&(p+i)->sex);
}
}
void output(struct studnet *p,int n)
{
for(int i=0;i<n;i++)
{
printf("%s\t%d\t%c\n",(p+i)->name,(p+i)->age,(p+i)->sex);
// printf("%s\t%d\t%c\n",(*(p+i))->name,(*(p+i))->age,(*(p+i))->sex);
}
}
int main(int argc, const char *argv[])
{
//申请空间函数
struct studnet *p=create(5);
//在堆区循环输入数据
input(p,5);
//输出
output(p,5);
//添加供一个:
//查找名字key,是否在堆区中出现,
//如果出现则返回下表,并在主函数输出该学生信息,否则返回-1
return 0;
}
4.4 结构体的引用
1> 普通结构体变量时使用“.”引用
2> 结构体数组需要使用结构体数组名加“.”调用
3> 结构体指针,需要使用“->”引用
4> 结构体变量不可以整体输出,常用来实现交换,或者做参数实现传递
struct student a={"张三",12,100,'M'};
struct studnet b;
b=a;
4.5 typedef和结构体结合
typedef struct student
{
char name[20];
int age;
}stu,stu_arr[3],*stu_p;
//stu===>struct student
//stu_arr===>struct student [3]
//stu_p ===>struct student *
stu a;
stu arr[3] ---> stu_arr arr;
stu *p --->stu_p p;
4.5 普通结构体嵌套
//定义一个人:姓名,年龄,出生年月日
typedef struct BIRTH
{
int year;
int month;
int day;
}bir;//bir--->struct BIRTH
typedef struct person
{
char name[10];
int age;
bir birthday;
}per;
a.birthday.year
per a={"战三",12,2000,2,2}; --->struct person a;
printf("姓名:%s 年龄%d 年:%d 月%d 日:%d\n",a.name,a.age,a.birthday.year,a.birthday.month,a.birthday.day)
4.6 结构体嵌套结构体数组
//定义一个人:姓名,年龄,车(3)【品牌,价格 颜色】
typedef struct //结构体名可以省略不写
{
char name[10];
int price;
char color[20];
}car;
typedef struct
{
char name[10];
int age;
car arr[3];
}person;
person a={"战三",14,"大众",111111,"黑色","奔驰",222222,"白色","奥迪",33333,"红色"};
a.name//引用姓名
a.age
a.arr[0].name//引用第一辆车的品牌
a.arr[2].price//引用第三辆车的单价
4.7 结构体嵌套指针
//定义一个人:姓名,年龄,出生年月日
typedef struct BIRTH
{
int year;
int month;
int day;
}bir;//bir--->struct BIRTH
typedef struct person
{
char name[10];
int age;
bir *birthday;
}per;
per a;
strcpy(a.name,"李四");
a.age=14;
bir b={200,12,12};
a.birthday=&b;
printf("姓名:%s 年龄%d 年:%d 月%d 日:%d\n",a.name,a.age,a.birthday->year,a.birthday->month,a.birthday->day)
4.7 结构体存储空间[重点]
64位操作系统
1> 结构体变量的地址是结构体第一个成员的首地址
2> 结构体每个成员的地址是连续
3> 结构体的大小是各个成员的总和
4> 满足字节对齐原则:
1.结构体的总字节数是最宽成员的整数倍,否则使用空字节填充
2.结构体的首地址是最宽成员的整数倍
3.结构体各个成员的偏移量是该成员字节大小的倍数,否则使用空字节填充
偏移量:该成员的地址编号到起始地址直接的差
如果成员是char,则偏移量是1的倍数
如果成员short,则偏移量是2的倍数
如果成员是int float ,则偏移量是4的倍数
如果成员是double,long,指针则偏移量是8的倍数
32位操作系统
满足字节对齐原则:
1.结构体的总字节数是最宽成员的整数倍,否则使用空字节填充
如果成员最宽的是8字节,则按照4的倍数计算
2.结构体的首地址是最宽成员的整数倍
如果最宽成员是8字节,则首地址按照4的倍数计算
3.结构体各个成员的偏移量是该成员字节大小的倍数,否则使用空字节填充
偏移量:该成员的地址编号到起始地址直接的差
如果成员是char,则偏移量是1的倍数
如果成员short,则偏移量是2的倍数
如果成员是int float double,long,指针【4字节或者8字节】,则偏移量是4的倍数
4.8共用体union
共用体:存储类型相同或不同的构造类型
特点:成员共用同一块空间
1> 共用体的各个成员占用同一空间
2> 共用的空间是最宽成员的字节大小
定义格式:
union 共用体名
{
数据类型 成员1;
数据类型 成员2;
.....
数据类型 成员n;
};
1,union: 共用体的关键字
2,共用体名:满足命名规范,可有可无
3,{}不可以省略
4,数据类型:基本类型、构造类型、指针、空类型
5,成员变量的个数任意
6,共用体后面的分号必须存在
7,共用体的描述位置任意,一般在全局
8,共用体描述计算机不分配空间,直到定义结共用体变量才分配
4.8.1 共用体定义和初始化
1> 直接和间接定义和初始化
union A
{
int a;
char b;
float c;
}value={100};//方式1:默认把100赋值给共用体的第一个成员
union A value1={.b=2.2};//方式2:表示指定成员b进行赋值
//方式3:
union A value2;
value2.a=100;
value2.b='k';
value2.c=66.66;//最终结果是c的值
4.8.2 结构体和共用体结合
1> 共用体在结构体外嵌套
typedef union
{
int a;
int b;
}A;
typedef struct
{
int kk;
A pp;
}B;
B value={100,.pp.a=666}
printf("kk=%d b=%d\n",value.kk,value.pp.b);
2> 共用体在结构体内嵌套
typedef struct
{
char name[20];
char sex;
char job;
union //建议不加共用体
{
float score;
char position[20];
};
}A;
A value={"李四",'M','s',.position="语文老师"};
printf("%s %c %c %s\n",value.name,value.sex,value.job,value.position);
作业:
有若干个学校人员的信息,包括学生和教师。其中学生的数据包括:姓名、性别、职业s/S、分数。教师的数据包括:姓名、性别、职业t/T、职务。要求用同一个表格来处理以上数据。
typedef struct
{
char name[20];
char sex;
char job;
union
{
float score;
char position[20];
};
}B;
1,定义函数在堆区申请空间n
B *p= (struct B * )malloc(sizeof(struct B)* n );
2,定义函数实现录入学校人员信息
在输入分数或者职务是,需要判断职业。
3,定义函数输出学校人员信息
4,定义函数计算学生平均成绩
5,定义函数计算老师的个数
6,释放存储空间