前言
涉及指针,结构体深层应用
结构体文章:写文章-CSDN创作中心
指针文章:写文章-CSDN创作中心
相信这两篇文章会方便你学习顺序表,链表
**************************************************************************************************************
补充:动态内存的开辟
掌握malloc free calloc realloc
头文件为<stdlib.h>
malloc
void* malloc (size_t size);
这个函数向内存申请一块连续可用的空间,并返回指向这块空间的指针。
1.如果开辟成功,则返回一个指向开辟好空间的指针。
2. 如果开辟失败,则返回一个NULL指针,因此malloc的返回值一定要做检查。
3.返回值的类型是 void* ,所以malloc函数并不知道开辟空间的类型,具体在使用的时候使用者自己 来决定。
int* p = (int*)malloc(40);
malloc返回void*型所以要根据类型进行强转
free
void free (void* ptr);
free函数用来释放动态开辟的内存。
如果参数 ptr 指向的空间不是动态开辟的,那free函数的行为是未定义的。
如果参数 ptr 是NULL指针,则函数什么事都不做。
用free把开辟的空间还回去,但是指针P还是指向那个地址
所以要把它主动置空
int* p = (int*)malloc(40);
printf("%p\n", p);
free(p);
printf("%p\n", p);
p = NULL;
calloc
void* calloc (size_t num//开辟几个元素, size_t size//每个元素多大);
例子:
int* p2 = (int*)calloc(10/*开辟多少元素*/, sizeof(int)/*每个元素多大*/);
free(p2);
p2 = NULL;
calloc返回void*型所以要根据类型进行强转
********malloc和calloc区别:
malloc申请空间没有初始化,直接返回起始地址
calloc申请好空间后,会把空间初始化为0,返回起始地址
开辟失败返回NULL
realloc
void* realloc(void* ptr/*要修改的起始地址*/, size_t size/*多大空间,一共的算上之前的*/);
返回新的地址
realloc是如何开辟内存:
情况1 :后面空间够用,要扩展内存就直接原有内存之后直接追加空间,原来空间的数据不发生变化。
返回的是其malloc开辟空间的起始地址
情况2 :原有空间之后没有足够多的空间时,扩展的方法是:在堆空间上另找一个合适大小 的连续空间来使用。这样函数返回的是一个新的内存地址。
此时返回新开辟的起始地址
常见的动态内存错误:
1 对NULL指针的解引用操作
2 对动态开辟空间的越界访问
3 对非动态开辟内存使用free释放
4 使用free释放一块动态开辟内存的一部分
5 对同一块动态内存多次释放
6 动态开辟内存忘记释放(内存泄漏)
顺序表:
顺序表定义:
顺序表是在计算机内存中以数组的形式保存的线性表,线性表的顺序存储是指用一组地址连续的存储单元依次存储线性表中的各个元素、使得线性表中在逻辑结构上相邻的数据元素存储在相邻的物理存储单元中,即通过数据元素物理存储的相邻关系来反映数据元素之间逻辑上的相邻关系,采用顺序存储结构的线性表通常称为顺序表。顺序表是将表中的结点依次存放在计算机内存中一组地址连续的存储单元中。
线性表分为顺序表和数组,简单点看的话,我们可以将顺序表看作一维数组。 数组可以随机访问任何节点 (随机存取); 当数组中不存在元素时,插入和删除操作不用移动元素 (一定要考虑这种特殊情况); 数组的存储空间可以是离散分布的
补充:指针[]与指针+数字 ( 指针文章:写文章-CSDN创作中心)
int main() {
int arr[] = { 1,2,3,4,5 };
int* p = arr;
printf("%d\n", p[1]);
printf("%d",* (p + 1));
}
指针此时接受了数组首元素的地址
对其解引用为2。此处*(p+x)==p[x];
这是前提:*(p+x)==p[x]
顺序表功能:
常见的图书管理系统,通讯录,QQ等等都是可以通过顺序表来实现;
一般具有增,删,查,改
定义一个顺序表类型:(动态开辟)
typedef struct sxb
{
int * a;//用来接收开辟内存的起始地址
int size; //size为元素个数
int capacity;//容量
}sxb;
主函数里面定义顺序表变量ptr
int main() {
sxb ptr;
}
对顺序表变量进行初始化:
开辟4个整型来存放元素
void intit(sxb*ptr)//传地址调用,实现对顺序表进行实际改变,用顺序表指针来接收 {
ptr->a = (int*)malloc(sizeof(int) * 4);//开辟空间
if (ptr->a == NULL) {
printf("开辟失败\n");
return;
}
ptr->size = 0;
ptr->capacity = 4;//记录容量
printf("开辟成功\n");
}
int main() {
sxb ptr;
intit(&ptr);//传地址调用,实现对顺序表进行实际改变
}
动态开辟,结束时要释放空间复原数据:
void voids(sxb* ptr) {
free(ptr->a);
ptr->a = NULL;
ptr->capacity = ptr->size = 0;
}
打印元素(方便检查错误):
void print1(sxb* ptr) {
if (ptr->a == NULL) {
printf("出错\n");
return;
}
int i = 0;
for (i = 0; i < ptr->size; i++) {
printf("%d\n", ptr->a[i]);
}
printf("打印完成\n");
}
检查开辟的空间不够用:
用size与capacity的大小进行比较实现
void checkcap(sxb* ptr) {
if (ptr->size == ptr->capacity) {
int* b = NULL;
b = (int*)realloc(ptr->a, sizeof(int) * ptr->capacity * 2);
if (b == NULL) {
printf("开辟失败\n");
}
ptr->capacity = ptr->capacity * 2;
ptr->a = b;
printf("开辟成功\n");
}
}
前面准备工作完成,进行具体功能实现
增加(尾增):
void endadd(sxb* ptr) {
checkcap(ptr);
int num = 0;
scanf("%d", &num);
ptr->a[ptr->size] = num;
ptr->size++;
printf("尾插成功\n");
}
删除(尾删):
void enddele(sxb* ptr) {
if (ptr->size < 0) {
printf("列表为空\n");
return;
}
ptr->size--;
printf("删除成功\n");
}
这里我们并没有真正删除数据:
5还在,可以访问到,但是这样写方便,使用者不会轻易访问越界
头部增加:
考虑:
我们并不是在前面增加空间(我们可以将顺序表看作一维数组,1.数组是相同数据类型的元素的集合。 2.数组中的各元素的存储是有先后顺序的,它们在内存中按照这个先后顺序连续存放在一起。 3.数组元素用整个数组的名字和它自己在数组中的顺序位置来表示。)而是把数据往后移动
从4往后移动保证数据不丢失:
代码实现:
void headadd(sxb* ptr) {
if (ptr->size + 1 == ptr->capacity) {
int* b = NULL;
b = (int*)realloc(ptr->a, sizeof(int) * ptr->capacity * 2);
if (b == NULL) {
printf("开辟失败\n");
return;
}
ptr->capacity = ptr->capacity * 2;
ptr->a = b;
printf("开辟成功\n");
}
int num = 0;
scanf("%d", &num);
int i = 0;
for (i = ptr->size; i >0; i--) {
ptr->a[i] = ptr->a[i-1];
}
ptr->size++;
ptr->a[0] = num;
printf("头增加成功\n");
}
头部删除:
与头部增加同理,直接把后面数据往前面覆盖即可实现
从1往前移动保证数据不丢失:
void headdele(sxb* ptr) {
if (ptr->size > 0) {
int i = 0;
for (i = 0; i < ptr->size; i++) {
ptr->a[i - 1] = ptr->a[i];
}
ptr->size--;
}
else {
printf("没数据\n");
}
}
实现前面的功能,实际生活中,还有从中间增加
中增:
情况1:(至少两个数据实现增加)
情况2:只有一个数据
可以通过头增尾增实现
情况3:没有数据
可以通过头增尾增实现
void midadd(sxb* ptr) {
if (ptr->size + 1 == ptr->capacity) {
int* b = NULL;
b = (int*)realloc(ptr->a, sizeof(int) * ptr->capacity * 2);
if (b == NULL) {
printf("开辟失败\n");
return;
}
ptr->capacity = ptr->capacity * 2;
ptr->a = b;
printf("开辟成功\n");
}
printf("你想从哪里增加\n");
int num = 0;
scanf("%d", &num);
if (ptr->size >= 0) {
if (num < ptr->size+1) {
int num2 = 0;
printf("你想改成什么\n");
scanf("%d", &num2);
int i = 0;
for (i = ptr->size; i >num-1; i--) {
ptr->a[i] = ptr->a[i - 1];
}
ptr->size++;
ptr->a[num - 1] = num2;
printf("修改成功\n");
}
else if(num=ptr->size+1){
printf("将进行尾插\n");
endadd(ptr);
}
else {
printf("将进行头插\n");
headadd(ptr);
}
}
}
中间删除:
情况1:(至少三个数据实现)
情况2:至少一个元素实现
判断删除数据是否为size用尾删实现;
下面为我初始写的:
void middele(sxb* ptr) {
printf("你想从哪里删除\n");
int num = 0;
scanf("%d", &num);
if (ptr->size > 0) {
if (num < ptr->size ) {
int num2 = 0;
printf("你想删除什么\n");
scanf("%d", &num2);
int i = 0;
for (i = num-1; i <ptr->size; i++) {
ptr->a[i] = ptr->a[i + 1];
}
ptr->size--;
printf("修改成功\n");
}
else {
printf("将进行尾删\n");
enddele(ptr);
}
}
}
经过改良的代码:尾删的功能可以融入情况1,用一个功能实现;
void middele(sxb* ptr) {
printf("你想从哪里删除\n");
int num = 0;
scanf("%d", &num);
if (ptr->size > 0) {
if (num <= ptr->size ) {
int num2 = 0;
printf("你想删除什么\n");
scanf("%d", &num2);
int i = 0;
for (i = num-1; i <ptr->size; i++) {
ptr->a[i] = ptr->a[i + 1];
}
ptr->size--;
printf("修改成功\n");
}
/* else {
printf("将进行尾删\n");
enddele(ptr);
}*/
}
}
总代码:
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>
typedef struct sxb
{
int * a;
int size;
int capacity;
}sxb;
void intit(sxb*ptr) {
ptr->a = (int*)malloc(sizeof(int) * 4);
if (ptr->a == NULL) {
printf("开辟失败\n");
return;
}
ptr->size = 0;
ptr->capacity = 4;
printf("开辟成功\n");
}
void voids(sxb* ptr) {
free(ptr->a);
ptr->a = NULL;
ptr->capacity = ptr->size = 0;
}
void print1(sxb* ptr) {
if (ptr->a == NULL) {
printf("出错\n");
return;
}
int i = 0;
for (i = 0; i < ptr->size; i++) {
printf("%d\n", ptr->a[i]);
}
printf("打印完成\n");
}
void checkcap(sxb* ptr) {
if (ptr->size == ptr->capacity) {
int* b = NULL;
b = (int*)realloc(ptr->a, sizeof(int) * ptr->capacity * 2);
if (b == NULL) {
printf("开辟失败\n");
}
ptr->capacity = ptr->capacity * 2;
ptr->a = b;
printf("开辟成功\n");
}
}
void endadd(sxb* ptr) {
checkcap(ptr);
int num = 0;
scanf("%d", &num);
ptr->a[ptr->size] = num;
ptr->size++;
printf("尾插成功\n");
}
void enddele(sxb* ptr) {
if (ptr->size < 0) {
printf("列表为空\n");
return;
}
ptr->size--;
printf("删除成功\n");
}
void headadd(sxb* ptr) {
if (ptr->size + 1 == ptr->capacity) {
int* b = NULL;
b = (int*)realloc(ptr->a, sizeof(int) * ptr->capacity * 2);
if (b == NULL) {
printf("开辟失败\n");
return;
}
ptr->capacity = ptr->capacity * 2;
ptr->a = b;
printf("开辟成功\n");
}
int num = 0;
scanf("%d", &num);
int i = 0;
for (i = ptr->size; i >0; i--) {
ptr->a[i] = ptr->a[i-1];
}
ptr->size++;
ptr->a[0] = num;
printf("头增加成功\n");
}
void headdele(sxb* ptr) {
if (ptr->size > 0) {
int i = 0;
for (i = 0; i < ptr->size; i++) {
ptr->a[i - 1] = ptr->a[i];
}
ptr->size--;
}
else {
printf("没数据\n");
}
}
void midadd(sxb* ptr) {
if (ptr->size + 1 == ptr->capacity) {
int* b = NULL;
b = (int*)realloc(ptr->a, sizeof(int) * ptr->capacity * 2);
if (b == NULL) {
printf("开辟失败\n");
return;
}
ptr->capacity = ptr->capacity * 2;
ptr->a = b;
printf("开辟成功\n");
}
printf("你想从哪里增加\n");
int num = 0;
scanf("%d", &num);
if (ptr->size >= 0) {
if (num < ptr->size+1) {
int num2 = 0;
printf("你想改成什么\n");
scanf("%d", &num2);
int i = 0;
for (i = ptr->size; i >num-1; i--) {
ptr->a[i] = ptr->a[i - 1];
}
ptr->size++;
ptr->a[num - 1] = num2;
printf("修改成功\n");
}
else if(num=ptr->size+1){
printf("将进行尾插\n");
endadd(ptr);
}
else {
printf("将进行头插\n");
headadd(ptr);
}
}
}
void middele(sxb* ptr) {
printf("你想从哪里删除\n");
int num = 0;
scanf("%d", &num);
if (ptr->size > 0) {
if (num <= ptr->size ) {
int num2 = 0;
printf("你想删除什么\n");
scanf("%d", &num2);
int i = 0;
for (i = num-1; i <ptr->size; i++) {
ptr->a[i] = ptr->a[i + 1];
}
ptr->size--;
printf("修改成功\n");
}
/* else {
printf("将进行尾删\n");
enddele(ptr);
}*/
}
}
int main() {
sxb ptr;
//intit(&ptr);
//midadd(&ptr);
//midadd(&ptr);
//midadd(&ptr);
//print1(&ptr);
//middele(&ptr);
//middele(&ptr);
//print1(&ptr);
/*功能实现区*/
}
顺序表的优点:
(1 )无须增加额外的存储空间表示结点间的逻辑关系。
(2 )可以方便地随机存取表中任一结点。
顺序表的缺点:
(1 )插入和删除运算不方便,通常须移动大量结点,效率较低。
(2 )难以进行连续的存储空间的预分配,尤其是当表变化较大时
顺序表总结到这里了,该顺序表我调试大部分情况,可能会有没有考虑到的地方,欢迎指出
求三连!!!!!!!!!!!!!!!!!!!!!!!1接下来更新单链表