文章目录
- 前言
- 一、接口定义
- 1、创建切片
- 2、销毁切片
- 3、添加元素
- 4、切片长度
- 5、切片容量
- 二、完整代码
- 三、使用示例
- 1、一般使用流程
- 2、直接append
- 3、自定义类型
- 总结
前言
由于c语言没有集合类的标准库,需要用时只能自己实现,由于c语言没有泛型,使得实现的集合类接口通常比较另类,很多时候都需要二级指针作为参数,且不支持字面量作为参数,使用时心智负担较重。本文参考go语言的slice,找到了一种非常简化的动态数组接口,可以极大的方便使用。
一、接口定义
1、创建切片
指定元素类型,以及容量即可以创建切片,返回是一个数组
/// <summary>
/// 创建切片
/// </summary>
/// <param name="t">元素类型</param>
/// <param name="cap">切片容量</param>
/// <returns>切片数组</returns>
#define make(t,cap)
2、销毁切片
与go语言不同,c语言需要管理内存。用完后的切片需要销毁。
/// <summary>
/// 销毁切片
/// </summary>
/// <param name="a">切片数组</param>
#define unmake(a)
3、添加元素
可以添加元素也可以添加数组,数组长度会自动增长。
/// <summary>
/// 添加元素、数组
/// </summary>
/// <param name="a">切片数组</param>
/// <param name="e">元素、数组</param>
/// <param name="l">[可选]数组长度,e为数组时需要此项</param>
#define append(...)
4、切片长度
获取切片长度
/// <summary>
/// 切片长度
/// </summary>
/// <param name="a">切片数组</param>
/// <returns>切片长度</returns>
#define len(a)
5、切片容量
获取切片容量
/// <summary>
/// 切片容量
/// </summary>
/// <param name="a">切片数组</param>
/// <returns> 切片容量</returns>
#define cap(a)
二、完整代码
slice.h
#ifndef SLICE_H
#define SLICE_H
#include<stddef.h>
/************************************************************************
* @Project: Slice
* @Decription: 切片
* 相当于动态数组,用法与go语言的slice类似
* @Verision: v1.0.0
* @Author: Xin Nie
* @Create: 2024/03/25 01:02:00
* @LastUpdate: 2024/03/25 01:02:00
************************************************************************
* Copyright @ 2024. All rights reserved.
************************************************************************/
/// <summary>
/// 创建切片
/// </summary>
/// <param name="t">元素类型</param>
/// <param name="cap">切片容量</param>
/// <returns>切片数组</returns>
#define make(t,cap)_slice_make(sizeof(t),cap)
/// <summary>
/// 销毁切片
/// </summary>
/// <param name="a">切片数组</param>
#define unmake(a)_slice_umake(a);a=0
/// <summary>
/// 添加元素、数组
/// </summary>
/// <param name="a">切片数组</param>
/// <param name="e">元素、数组</param>
/// <param name="l">[可选]数组长度,e为数组时需要此项</param>
#define append(...)_ACF_COUNT_ARG(__VA_ARGS__)
/// <summary>
/// 切片长度
/// </summary>
/// <param name="a">切片数组</param>
/// <returns>切片长度</returns>
#define len(a) _slice_len( a)
/// <summary>
/// 切片容量
/// </summary>
/// <param name="a">切片数组</param>
/// <returns>切片容量</returns>
#define cap(a) _slice_cap( a)
///私有方法
#define _ACF_ARG_T(t) t
#define _ACF_ARG_N(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,N,...) N
#define _ARG_N_HELPER(...) _ACF_ARG_T(_ACF_ARG_N(__VA_ARGS__))
#define _ACF_COUNT_ARG(...) _ARG_N_HELPER(__VA_ARGS__,16,15,14,13,12,11,10,9,8,7,6,5,4,_APPEND_ARRAY(__VA_ARGS__),_APPEND(__VA_ARGS__),1 ,0)
#define _APPEND(a,e)a=_slice_append(a,0,sizeof(*a));a[len(a)-1] = e
#define _APPEND_ARRAY(a,e,l)_slice_appendArray(a,sizeof(*e),e,l)
void* _slice_make(size_t elementSize, size_t sliceCap);
void* _slice_append(void* array, void* element, size_t elementSize);
void* _slice_appendArray(void* array, size_t elementSize, void* array2, size_t array2Size);
size_t _slice_len(void* array);
size_t _slice_cap(void* array);
void _slice_umake(void* array);
#endif
slice.c
#include "slice.h"
#include<stdlib.h>
typedef struct Slice {
int length;
int capacity;
int elementSize;
}Slice;
void* _slice_make(size_t elementSize, size_t sliceCap) {
Slice* slice = malloc(elementSize * sliceCap + sizeof(Slice));
if (slice)
{
slice->capacity = sliceCap;
slice->elementSize = elementSize;
slice->length = 0;
return slice + 1;
}
return NULL;
}
void* _slice_append(void* array, void* element, size_t elementSize) {
Slice* slice = (array ? (Slice*)array : (Slice*)_slice_make(elementSize, 4)) - 1;
if (slice->capacity == slice->length) {
slice->capacity = slice->capacity == 0 ? 4 : slice->capacity * 2;
if ((slice = realloc(slice, slice->capacity * slice->elementSize + sizeof(Slice))) == NULL)return NULL;
}
if(element)
{
char* p = slice + 1;
memcpy(p + slice->elementSize * slice->length, element, slice->elementSize);
}
slice->length++;
return slice + 1;
}
void* _slice_appendArray(void* array, size_t elementSize, void* array2, size_t array2Size) {
Slice* slice = (array ? (Slice*)array : (Slice*)_slice_make(elementSize, array2Size)) - 1;
int newCap = slice->capacity;
while (newCap < slice->length+ array2Size) {
newCap << 1;
}
if (slice->capacity < newCap) {
slice->capacity = newCap;
if ((slice = realloc(slice, slice->capacity * slice->elementSize + sizeof(Slice))) == NULL)return NULL;
}
char* p = slice + 1;
memcpy(p + slice->elementSize * slice->length, array2, slice->elementSize * array2Size);
slice->length += array2Size;
return slice + 1;
}
size_t _slice_len(void* array) {
if (!array)return 0;
Slice* slice = (Slice*)array - 1;
return slice->length;
}
size_t _slice_cap(void* array) {
if (!array)return 0;
Slice* slice = (Slice*)array - 1;
return slice->capacity;
}
void _slice_umake(void* array) {
if (array)
{
Slice* slice = (Slice*)array - 1;
free(slice);
}
}
三、使用示例
1、一般使用流程
#include"slice.h"
#include<stdio.h>
void main() {
//创建切片,返回的是数组完全可以当成数组使用,通过len可以获取数组长度。
int* a = make(int, 0);
int b[] = { 1,2,3 };
//添加元素
a = append(a, 6510);
//添加数组
a = append(a, b, 3);
//循环添加元素
for (int i = 0; i < 1024; i++)
{
a = append(a, i);
}
//遍历切片
for (int i = 0; i < len(a); i++)
{
printf("%d ", a[i]);
}
//销毁切片
unmake(a);
}
效果预览
2、直接append
#include"slice.h"
#include<stdio.h>
void main() {
//数组为空时可以直接通过append产生切片
int* a = NULL;
int b[] = { 1,2,3 };
//添加元素
a = append(a, 6510);
//添加数组
a = append(a, b, 3);
//循环添加元素
for (int i = 0; i < 1024; i++)
{
a = append(a, i);
}
//遍历切片
for (int i = 0; i < len(a); i++)
{
printf("%d ", a[i]);
}
//销毁切片
unmake(a);
}
3、自定义类型
typedef struct VideoScale {
int align;
int width;
int height;
enum AVPixelFormat format;
struct SwsContext* ctx;
AVFrame* frame;
} VideoScale;
VideoScale* video_scales = NULL;
VideoScale t;
video_scales = append(video_scales, t);
for (int i = 0; i < len(video_scales);i++) {
int frame = video_scales[i].frame;
//其他操作略...
}
unmake(is->video_scales);
总结
以上就是今天要讲的内容,本文仅仅简单实现了切片,这种方式使用动态数组会很方便,这是一种新的思路,其他的集合类型也可以考虑用这种方式实现,尤其是能够统一一套接口,且简单易用,将能极大的提高c语言开发效率。