c语言动态分布数据类型,C语言利用动态数组实现顺序表(不限数据类型)

实现任意数据类型的顺序表的初始化,插入,删除(按值删除;按位置删除),销毁功能。、

顺序表结构体

实现顺序表结构体的三个要素:(1)数组首地址;(2)数组的大小;(3)当前数组元素的个数。

1 //顺序表结构体

2 structDynamicArray{3 void **addr; //指向数组的首地址(指向数组的指针)

4 int capacity; //数组的大小

5 int size; //当前数组元素的个数

6 };

注意事项:void **addr为二级指针,即数组的元素也为指针,因为我们并不知道用户的输入数据是什么类型,操作数据的地址是最安全的方法。

初始化

对顺序表进行初始化,实际上为初始化顺序表内的各个成员,另外对输入的参数做边界处理。

1 //初始化数组,初始化结构体和里面的元素。初始化之后返回该数组,写为void*

2 void *Init(intcapacity_){3 if (capacity_ <= 0){4 returnNULL;5 }6

7 struct DynamicArray *arr = malloc(sizeof(struct DynamicArray));//开辟一个结构体就可以了

8 if (NULL ==arr){9 returnNULL;10 }11

12 arr->capacity =capacity_;13 arr->addr = malloc(sizeof(void*)*arr->capacity);//对数组开辟内存

14 arr->size = 0;15

16 returnarr;17 }

插入操作

对于顺序表的插入操作,需要在pos位置处开始的元素统一往后移动一个位置,处理方式为:从后往前挨个移动,从前往后会覆盖。

注意:(1)考虑顺序表的顺序插入和乱序插入,12行的代码;(2)若顺序表被填满,则需要对现有数组进行扩大空间,这里涉及到四步操作:开辟内存、拷贝数据(尽量使用memcpy)、释放原内存、修改指针指向。(3)对输入参数做边界处理。

1 //插入值,从后往前挨个移动一位,如果插入的值过大,则扩大数组

2 void Insert(void *arr_, int pos, void *data){3

4 struct DynamicArray *arr = (struct DynamicArray *)arr_;5

6 if (NULL == arr || NULL ==data){7 return;8 }9

10 //对于无效的pos,默认插入到现有元素的后面一个11 //if (pos < 0 || pos > arr->capacity)

12 if (pos < 0 || pos > arr->size)13 {14 pos = arr->size;15 }16

17 //每次调用插入函数都会插入值,因此arr->size++,size一直增长,且没有限制

18 if (arr->size >= arr->capacity)19 //if (arr->size > arr->capacity)

20 {21

22 //开辟新内存

23 int newcapacity = arr->capacity * 2;24 void **newaddr = malloc(sizeof(void *)*newcapacity);25

26 //拷贝数据,尽量使用内存拷贝函数

27 memcpy(newaddr, arr->addr, sizeof(void *) * arr->capacity);28

29 //释放原空间

30 if (arr->addr !=NULL){31 free(arr->addr);32 arr->addr =NULL;33 }34

35 //修改指针指向

36 arr->addr =newaddr;37 arr->capacity =newcapacity;38 }39

40 for (int i = arr->size - 1; i >= pos; --i){41 arr->addr[i + 1] = arr->addr[i];42 }43 arr->addr[pos] = data; //添加过后,size变化

44 arr->size++;45 }

遍历操作

遍历一般的作用为打印数据,但这里并不知道用户的是什么数据,这里由回调函数进行打印(C语言函数指针和回调函数)。

1 //遍历

2 void Foreach(void *arr_, void(*_callback)(void *)){3 struct DynamicArray * arr = (struct DynamicArray *)arr_;4 if (NULL == arr || NULL ==_callback){5 return;6 }7 for (int i = 0; i < arr->size; ++i){8 _callback(arr->addr[i]);9 }10 }

删除操作

这里分为按值删除和按位置删除,其中按值操作调用了按位置操作的代码,因此需要注意size--的问题。另外,按值操作也使用了回调函数。

1 //删除(按值删除,按位置删除)

2 void DeletePos(void *arr_, intpos){3 struct DynamicArray *arr =arr_;4 if (NULL ==arr){5 return;6 }7

8 for (int i = pos; i < arr->size - 1; ++i){9 arr->addr[i] = arr->addr[i + 1];10 }11

12 arr->size--;//size会减小

13 }14 void DeleteValue(void *arr_, void * data, int(*_compare)(void *, void*)){15 struct DynamicArray *arr =arr_;16 if (NULL == arr || NULL == data || NULL ==_compare){17 return;18 }19 for (int i = 0; i < arr->size; i++){20 if (_compare(arr->addr[i], data)){21 DeletePos(arr, i);22 break;23 }24 }25 //arr->size--; DeletePos里面已经有size--了

26 }

销毁操作

注意事项:需要先释放成员函数,再释放结构体。

1 //销毁

2 void Destory(void *arr_){3 struct DynamicArray *arr =arr_;4 if (NULL ==arr){5 return;6 }7 if (arr->addr !=NULL){8 free(arr->addr);9 arr->addr =NULL;10 }11 if (arr !=NULL){12 free(arr);13 arr =NULL;14 }15 }

元素个数和数组大小函数

之所以提供这两个函数,是因为不希望用户能直接看到我们定义的结构体内部,也不希望用户可以随便更改,因此我们各个函数的返回值都是void类型,这里提供两个函数,以便用户可以查看元素的个数和数组的大小。

1 int capaArray(void *arr_){2 struct DynamicArray *arr = (struct DynamicArray *)arr_;3 return arr->capacity;4 }5

6 int sizeArray(void *arr_){7 struct DynamicArray *arr = (struct DynamicArray *)arr_;8 return arr->size;9 }

测试

进行测试时,需要自定义打印和比较回调函数,不需要关心void*data是什么,仅仅实现自定义数据的比较和打印即可。另外回调函数使用时不需要任何参数,只需要函数名;另外,测试了结构体和整型顺序表,可以对顺序表结构体中的void **addr为二级指针有更好的理解。

1 structPerson{2 char name[100];3 intage;4 };5

6 //自定义输出函数

7 void print(void *data_){8 if (NULL ==data_){9 return;10 }11 struct Person *data = (struct Person *)data_;12 printf("name:%s, age:%d", data->name, data->age);13 }14 //整型自定义输出函数,访问时需要解引用

15 void printInt(void *data_){16 if (NULL ==data_){17 return;18 }19 int *data = (int *)data_;20 printf("age:%d", *data);21 }22

23 //自定义比较函数

24 int compare(void *d1, void *d2){25 if (NULL == d1|| NULL ==d2){26 return 0;27 }28 struct Person *p1 =d1;29 struct Person *p2 =d2;30

31 return (strcmp(p1->name, p2->name) == 0 && (p1->age == p2->age));32 }33

34 voidtest(){35 struct Person p1 = {"aaa", 10};36 struct Person p2 = { "bbb", 20};37 struct Person p3 = { "ccc", 30};38 struct Person p4 = { "ddd", 40};39 struct Person p5 = { "eee", 50};40 struct Person p6 = { "fff", 60};41 //int p1 = 1;42 //int p2 = 2;43 //int p3 = 3;44 //int p4 = 4;45 //int p5 = 5;

46

47

48 void * arr = Init(4);49 Insert(arr, 1, &p1);50 Insert(arr, 2, &p2);51 Insert(arr, 3, &p3);52 Insert(arr, 4, &p4);53 printf("%d", capaArray(arr));54 Insert(arr, 100, &p5);55 printf("%d", capaArray(arr));56 Insert(arr, 1, &p6);57 Foreach(arr, print);58 printf("-----------------");59 DeleteValue(arr, &p2, compare);60 Foreach(arr, print);61 Destory(arr);62

63 }64

65

66 intmain(){67

68 test();69 system("pause");70 return 0;71 }

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值