数据结构(2) -动态线性结构表

1、相关代码

#include <stdio.h>
#include <stdlib.h>

#define MAXSIZE 100 // 定义顺序表的最大容量

typedef int ElemType; // 定义元素类型为int

typedef struct{
    ElemType *elem; // 存储空间基址
    int length;//表长度
    int listsize;//表容量
} SqList;

SqList L; // 定义一个顺序表

int main() {
    /* (ElemType *)malloc(...):
    malloc 是C语言中的一个标准库函数,用于分配字节数大小的动态内存。它声明在头文件 <stdlib.h> 中。
    malloc 返回 void* 类型的指针,该指针指向分配的内存块。为了将其转换为 ElemType* 类型(也就是 int* 类型,在我们的定义中),我们进行指针类型转换 (ElemType *)。
    这个强制类型转换是必要的,因为 malloc 函数默认返回的指针类型是 void* ,需要将其转换成当前所需的具体数据类型的指针。 
    */
    // L.elem = (ElemType *)malloc(MAXSIZE * sizeof(ElemType)); // 分配存储空间
    // L.length = 0; // 初始化表长为0
    // L.listsize = 100; // 初始化表容量为100
    // 在这里可以进行顺序表的插入、删除、查找等操作

    ElemType *ptr = (ElemType *)malloc(MAXSIZE * sizeof(ElemType));
    if (ptr == NULL) {
        fprintf(stderr, "Memory allocation failed.\n");
        return 1;
    }

    // 使用指针
    ptr[0] = 10; // 例如,初始化第一个元素

    printf("First element value: %d\n", ptr[0]);
    
    return 0;
}

这个代码的核心部分就是ElemType *ptr = (ElemType *)malloc(MAXSIZE * sizeof(ElemType));
可以看见线性表就是基于指针的操作,而malloc就是用于在内存中划分一块区域进行操作的方式,而这个类型可以是int也可以是char更可以是用户自定义的类型,通过n*type可以推断出总的内存占用大小从而在内存中划分出指定大小的区域用于操作,说起来真的非常底层。

如果需要也可以打印出指针指向的地址,用于佐证上述的说法。

    printf("%p\n",(void *)&ptr[0]);//0x14b704290
    printf("%p\n",(void *)&ptr[1]);//0x14b704294
    printf("%p\n",(void *)&ptr[2]);//0x14b704298

当顺序表的容量超标了就需要扩容

#include <stdio.h>
#include <stdlib.h>

#define MAX_LIST_SIZE 1 // 定义顺序表的最大容量

typedef int ElemType; // 定义元素类型为int

typedef struct{
    ElemType *elem; // 存储空间基址
    int length;//表长度
    int listsize;//表容量
} SqList;

SqList L; // 定义一个顺序表

void printList(SqList L){

    printf("List Content: ");
    for (int i = 0; i < L.length; i++) {
        printf("%d ", L.elem[i]);
    }
    printf("\nList Length: %d\n", L.length);
    printf("List Capacity: %d\n", L.listsize);
}

void InitializeList(SqList *L) {
    //划分顺序表的内存地址
    L->elem = (ElemType *)malloc(MAX_LIST_SIZE * sizeof(ElemType));
    L->length = 0;
    L->listsize = MAX_LIST_SIZE;
}

void Insert(SqList *L,int index,ElemType value){
    if(index<0 || index>L->length){
        printf("index error!\n");
        return;
    }

    if(L->length>=L->listsize){
        printf("index offside!\n");
        //这里是可变长度列表的关键点,一般扩展10%的长度
        int new_size = L->listsize*1/2 + L->listsize;
        int *temp = (ElemType *)realloc(L->elem,new_size*sizeof(ElemType));
        if(temp == NULL){
            printf("Failed to allocate memory.\n");
            return;
        }

  
        L->elem = temp;
        L->listsize = L->listsize+new_size;
        printf("new list listsize:%d\n", L->listsize);
    }

    for(int i=L->length;i>=index;i--){
        L->elem[i+1] = L->elem[i];
    }

    L->elem[index] = value;
    L->length++;
}

int Remove(SqList *L,int index,ElemType *value){
    if(index<0||index>L->length-1){
        printf("remove index is error\n");
        return 0;
    }

    *value = L->elem[index];

    for(int i=index;i<L->length-1;i++){
        L->elem[i] = L->elem[i+1];
    }

    L->length--;

    return 1;
}

int main() {

    //将指针传给init方法
    InitializeList(&L);
    Insert(&L,0,1000);
    Insert(&L,1,1001);
    Insert(&L,2,1002);
    printList(L);
    ElemType e;
    if(Remove(&L,3, &e)){
        printf("%d had been remove\n",e);
    }
    printList(L);

    return 0;
}

注意观察Insert,这里主要的代码就是”realloc”关键字,这个关键字的作用是,创建一个指定长度的新表,并将旧表中的数据拷贝到新的表中,这样就实现了扩容。

2、总结

  • malloc
    • 用途: malloc 用于在堆上划分指定的字节数的内存。
    • 行为:
      • 仅分配内存:malloc 不初始化分配的内存块。分配到的内存中的内容是未定义的,可能包含垃圾值。
      • 必须检查返回的指针:如果返回 NULL,则表示内存分配失败。
  • realloc
    • 用途: realloc 用于调整先前分配的内存块的大小,可能会在堆上新划分一个更大的内存块,并把旧内存块的内容复制过来。
    • 行为:
      • 如果新大小大于旧大小:尽量在原地扩展,否则在新的位置分配更大的内存并复制旧内存的内容。
      • 如果新大小小于旧大小:内存被截断,但内容依然保留。
      • 如果新大小为 0:行为类似于 free,释放原有内存块。
      • 必须检查是否返回 NULL:表示扩展失败,原有指针仍然指向有效内存块。

发表评论

您的邮箱地址不会被公开。 必填项已用 * 标注

滚动至顶部