文章目录
- 前言
- 1.动态空间申请
- 1.1 静态空间申请
- 1.2 动态分配
- 2.字符串处理函数
- 3.结构体
- 3.1 结构体的浅拷贝
- 3.2 结构体的深拷贝
- 3.3 结构体变量在堆区 结构体指针成员也指向堆区(先释放成员,再释放结构体)
- 3.4 结构体的对齐规则
- 3.5 结构体的位域(了解 放几张图 可以看看 通常用在嵌入式中)
- 3.6 共用体union
- 3.7 枚举 enum(需要了解,可以测试)
- 4.链表
- 4.1 为什么要预设首元结点,只想head节点?
- 4.2 动态链表的插入、删除
前言
日期:2023年7月3日 下午开始~平时还有论文和实验代码要看 进度缓慢 还没到正经内容。
1.动态空间申请
1.1 静态空间申请
事先知道大小,分配在栈区或者全局区
int num = 100;
int arr[4] = {0};
1.2 动态分配
关键字:
- new 申请堆区空间
- delete 释放空间
- 释放申请是数组空间 需要 加上[]
- new delete 是成对使用的
使用实例:
void test08()
{
// new delete 动态申请空间
//动态申请int
int *p = new int(10);
cout<<*p<<endl;
//释放
delete p;
/*------------------------------------------*/
//动态申请数组
int *arr = new int[4]{10,20,30,40};
for (int i = 0; i < 4; ++i) {
cout<<*(arr+i)<<endl;
}
//释放
delete [] arr;
}
2.字符串处理函数
头包含
#include<string.h>
各种操作:
#include<string.h>
//测量字符长度
//size_t strlen(const char *s); // s指测量字符串的首元素地址 '\0'停止
char str1[128]="hello";
strlen(str1);//5
//字符串拷贝
/*
char *strcpy(char *dest, const char *src);
char *strncpy(char *dest, const char *src, size_t n); dest:目的空间地址
src:原字符串的首元素地址
*/
char dst[128]="";
char src[]="hello\0world";
strcpy(dst,src);
cout<<dst<<endl;//hello
//字符串追加
/*
char *strcat(char *dest, const char *src);
char *strncat(char *dest, const char *src, size_t n); 将src指向的字符串 追加到 dest指向的字符串尾部
*/
char dst[128]="hello";
char src[]="world";
strcat(dst,src);
cout<<dst<<endl;//helloworld
//字符串比较
/*
int strcmp(const char *s1, const char *s2);
int strncmp(const char *s1, const char *s2, size_t n);
返回值:
比较的ASCII值
>0 s1字符串 > s2字符串
<0 s1字符串 < s2字符串
==0 s1字符串==s2字符串
*/
3.结构体
定义&使用:
#include<string.h>
sturct Student{
int num;
char name[32];
}
Student lucy; //不初始化通过lucy.num 获取到的是随机值
Student zhang = {01,"zhang"} //按照顺序初始化
//name成员时数组名 位符号常量 不允许用=给name赋值
//zhang.name = "xiaozhang";//错误
strcpy(zhang.name, "xiaozhang");
// 获取值的方式
//zhang.num zhang.name
清空结构体变量:
//void *memset(void *_Dst,int _Val,size_t _Size);
//将地址从_Dst开始 长度位_Size的所有字节赋值位_Val
memset(&zhang,0,sizeof(zhang));
结构体嵌套:
struct Address{
char province[32];
char region[32];
int num;
}
struct Student{
int num;
char name[32];
Address add;
}
结构体数组:
Student stus[3] = {01,"zhangxueyou",02,"zhangtielin",03,"zhangguorong"};
//stus[0] 与数组使用类似
结构体指针变量:
struct Student{
int num;
char name[32];
}
Student zhang = {01,"zhang"};
Student *stu = &zhang;
//通过指针访问成员变量
//stu->num
//stu->name
结构体指针成员,指针文字常量区(只读)
struct stu{
int num;
char *name;
}
Stu zhang;
zhang.num = 100;
zhang.name = new char[32];
strcpy(zhang.name, "xiaozhang");
cout<<zhang.num<<" "<<zhang.name<<endl;
delete [] zhang.name;
3.1 结构体的浅拷贝
整体赋值就是浅拷贝,结构体中没有指针成员变量的时候,浅拷贝不会带来任何问题;如果结构体中有指针成员变量时,会出现多次释放堆区空间的问题
struct stu{
int num;
char *name;
}
stu zhang;
zhang.num = 100;
zhang.name = new char[32];
strcpy(zhang.name, "xiaozhang");
stu wang;
wang = zhang;
//操作完毕后多次释放空间
delete [] zhang.name;
delete [] wang.name;
3.2 结构体的深拷贝
如果结构体中有指针成员,尽量使用深拷贝
深拷贝就是为结构体中的指针成员分配独立的空间,然后再进行内容拷贝
struct stu{
int num;
char *name;
}
stu zhang;
zhang.num = 100;
zhang.name = new char[32];
strcpy(zhang.name, "xiaozhang");
stu wang;
wang.num = zhang.num;
wang.num = new char[32];
strcpy(wang.name, zhang.name);
//操作完毕后多次释放空间
delete [] zhang.name;
delete [] wang.name;
3.3 结构体变量在堆区 结构体指针成员也指向堆区(先释放成员,再释放结构体)
struct stu {
int num;
char *name;
};
//结构体在堆区
stu *p = new stu;
//结构体中指针成员指向堆区
p->name = new char[32];
//赋值
p->num = 100;
strcpy(p->name, "hello world");
cout<<p->num<<" "<<p->name<<endl;
//释放空间
delete [] p->name;//先释放成员指向
delete p;//再释放结构体
3.4 结构体的对齐规则
对齐规则是按照字节来的 char1 short2 int4
自动对齐:
- 一行多少字节,由结构体中最大的基本类型决定
- 成员偏移量:最大成员的类型的正数倍
- 结构体的总大小=分配单位整数倍
struct Data {
char a;
short b;
int c;
char d;
short e;
};
结构体内存分布图:
强制对齐:
#pragma pack (value)时的指定对齐值value。
- 分配单位:min(最大基本类型,value)
- 成员偏移量= 成员自身类型的整数倍
- 结构体总大小:分配单位的整数倍
3.5 结构体的位域(了解 放几张图 可以看看 通常用在嵌入式中)
位域是以二进制位为单位的
使用案例:
3.6 共用体union
结构体:所有成员拥有独立的空间
共用体:所有成员共享同一块空间
共用体空间,由最大的成员类型决定
union data{
char a;
short b;
int c;
}
void test01(){
Data ob;
ob.a = 10;
ob.b = 20;
ob.c = 30;
cout<<ob.a+ob.b+ob.c<<endl;//90==30+30+30
}
void test02(){
Data ob;
ob.c = 0x01020301;
ob.b = 0x0102;
ob.a = 0x01;
cout<<ob.a+ob.b+ob.c<<endl;//0x01020203
}
3.7 枚举 enum(需要了解,可以测试)
enum province{JIANGSU,ZHEJIANG,ANHUI,HENAN};//0,1,2,3
4.链表
每个节点通过指针域 保存下一个节点的位置 达到逻辑上连续
链表数组优缺点:
数组:
- 优缺点:查询效率高,插入效率低
链表 - 优缺点:插入效率高,查询效率低
静态链表的定义:
struct p{
//数据域
int num;
char name[32];
//指针域
struct p *next;
}
p p1 = {1, "aaa", NULL};
p p2 = {2, "bbb", NULL};
p p3 = {3, "ccc", NULL};
p p4 = {4, "ddd", NULL};
p p5 = {5, "eee", NULL};
//定义一个数据域为空的链表头
p *head = &p1;
p1.next = &p2;
p2.next = &p3;
p3.next = &p4;
p4.next = &p5;
p5.next = NULL;
//遍历
p *pd = head; // 添加首元节点
while(pd->next != NULL){
cout<<pd->num<<" "<<pd->name<<endl;
pd = pd->next;
}
4.1 为什么要预设首元结点,只想head节点?
-
便于首元结点的处理:增加头结点后,首元结点的地址保存在头结点的指针域中,则对链表的第一个元素的操作和其他元素相同,无需进行特殊处理。(若单链表不带头结点,则首元结点无前驱结点,在其前插入结点和删除该结点操作复杂些。)
-
便于空表和非空表的统一处理:增加头结点后,无论链表是否为空,头指针都是指向头结点的非空指针
- 注:首元结点是指链表中存储线性表中第一个数据元素a1的结点(头结点的下一个结点)。为了操作方便,通常在链表的首元结点之前附设一个结点(头结点)。
4.2 动态链表的插入、删除
struct stu{
//数据域
int num;
char name[32];
//指针域
stu *next;
}
插入:
# if 0
//插入节点 头插
stu *linkInsert(stu *head, stu tmp) {
//开辟堆空间
stu *pi = new stu;
*pi = tmp;
pi->next = NULL; //下个节点指向空
//判断头节点是否为空
if (head == NULL) {
//直接令插入节点为头结点
head = pi;
} else {
//头结点不为空
pi->next = head;
head = pi;
}
return head;
}
#endif
# if 0
//插入节点 尾插
stu *linkInsert(stu *head, stu tmp) {
//开辟堆空间
stu *pi = new stu;
*pi = tmp;
pi->next = NULL; //下个节点指向空
//判断头节点是否为空
if (head == NULL) {
//直接令插入节点为头结点
head = pi;
} else {
//找到尾结点
stu *pb = head; // 必须这么做 保留前面的数据
while (pb->next != NULL) {
pb = pb->next;
}
pb->next = pi;
}
return head;
}
#endif
#if 1
//有序插入
stu *linkInsert(stu *head, stu tmp) {
stu *pi = new stu;
*pi = tmp;
pi->next = NULL;
//判空
if (head == NULL) {
head = pi;
} else {
//链表存在
stu *pf = head;
stu *pb = head;
while (pi->num > pb->num && pb->next != NULL) {
//保存pb节点
pf = pb;
//pd像后移动
pb = pb->next;
}
//判断插入点位置
if(pi->num <= pb->num){
if(pb == head){
//pb->next == NULL 的情况
//最开头插入
pi->next = head;
head = pi;
}else{
//中间插入(pf&pd之间搭桥)
pf->next = pi;
pi->next = pb;
}
}else{
//尾部插入(值最大)
pb->next = pi;
}
}
return head;
}
#endif
删除节点
stu *linkDelete(stu *head, int num) {
//判空
if (head == NULL) {
cout << "link not exit" << endl;
}
stu *pf = head;
stu *pb = head;
//遍历链表
while (pb->num != num && pb->next != NULL) {
pf = pb;
pb = pb->next;
}
//找到删除点
if (pb->num == num) {
if (pb == head) {
//删除头节点
head = pb->next;
} else {
//不是头结点
pf->next = pb->next;
}
delete pb;
} else {
cout << "未找该节点" << endl;
}
return head;
}