C泛型__数据结构

本文通过C语言实现了一个通用类型的栈结构,探讨了如何处理不同数据类型的入栈和出栈操作,以及如何在栈销毁时避免内存泄漏。通过在结构中添加用户自定义的释放函数,解决了动态内存管理的问题。
摘要由CSDN通过智能技术生成
这里通过用C实现一个通用类型的栈结构来加深对数据结构和C指针的一些理解  同时也为一些C通用类型库函数(如排序)的实现提供一些思路
先写一个int 版本的栈用于比较:
typedef struct
{
     int* elem;
     int logicalLen;
     int allocLen;
}stack;

void StackNew(stack* s)
{
     s->logicalLen = 0;
     s->allocLen   = 4;
     s->elem = (int*)malloc(s->allocLen*sizeof(int));
}

void StackDestroy(stack* s)
{
     free(s->elem);
     s->elem = NULL;
}

void StackPush(stack* s, int val)
{
     if (s->logicalLen == s->allocLen)
     {
          s->allocLen *= 2;
          s->elem = (int*)realloc(s->elem,s->allocLen*sizeof(int));
          assert(s->elem != NULL);
     }
     s->elem[s->logicalLen] = val;
     s->logicalLen++;
}

void StackPop(stack* s, int* pVal)
{
     assert(s->logicalLen > 0);
     s->logicalLen--;
     *pVal = s->elem[s->logicalLen];
}


这里通过三个变量很简便的表示了栈,当栈满时,通过扩大一倍的策略重新分配空间。为了维持操作的一致性,所有操作都无返回值。
思考一下这里为什么要传栈的地址而不直接传栈

那么接下来我们用操作内存的方式来实现通用性栈结构:
typedef struct
{
     int logicalLen;
     int allocLen;
     void* elems;
     int elemSize;
}stack;

void StackNew(stack* s, int size)
{
     s->logicalLen = 0;
     s->allocLen = 4;
     s->elemSize = size;
     s->elems = malloc(s->allocLen*s->elemSize);
}

void StackDestroy(stack* s)
{
     free (s->elems);
     s->elems = NULL;
}

void StackPush(stack* s, void* pElem)
{
     if(s->logicalLen == s->allocLen)
     {
          s->allocLen *= 2;
          s->elems = realloc(s->elems, s->allocLen*s->elemSize);
          assert(s->elems != NULL);
     }
     void* elemlocal = (char*)s->elems+s->elemSize*s->logicalLen;
     memcpy(elemlocal, pElem, s->elemSize);
     s->logicalLen++;
}

void StackPop(stack* s, void*pElem)
{
     assert(s->logicalLen != 0);
     void* elemlocal = (char*)s->elems+(s->logicalLen-1)*s->elemSize;
     memcpy(pElem, elemlocal, s->elemSize);
     s->logicalLen--;
}



以上代码能够很好的通过测试 并且运行效率相当快,在C中 我们通过对内存位模式的操作来屏蔽了所有类型及类型长度带来的差异。
现在考虑将以上数据结构变得通用的,更灵活。假如我们要入栈出栈的元素类型是char* 这样将遇到什么问题呢?
如果我们装入的是动态分配的内存,那么我们没有理由确信栈的销毁会在栈为空之后,也就是有可能栈里还有动态分配的内存的指针,栈就被销毁了。导致内存泄漏。
解决的方案是让栈在销毁时释放动态内存空间,而栈怎么知道哪些是栈内存指针哪些是堆内存指针呢?栈不会知道,但是用户知道。因此考虑给栈多加一个用户定义的释放函数用于释放堆空间。

对上一个版本代码做少许改动:
typedef struct
{
     int logicalLen;
     int allocLen;
     void* elems;
     int elemSize;
     void (*freefn)(void*);
}stack;

void StackNew(stack* s, int size, void (*freefunc)(void*))
{
     s->logicalLen = 0;
     s->allocLen = 4;
     s->elemSize = size;
     s->elems = malloc(s->allocLen*s->elemSize);
     s->freefn = freefunc;
}

void StackDestroy(stack* s)
{
     if(s->freefn != NULL)
          for(int i=0; i<s->logicalLen; i++)
          {
               s->freefn((char*)s->elems+i*s->elemSize);
          }
     free (s->elems);
     s->elems = NULL;
}


当传入普通内置类型时,传入freefn为NULL
当传入类型是字符串时,定义freefn如下:
void freefn(void* p)
//虽然只是一个void指针 但是只要有地址 堆结构自然能知道当初分配了多少字节空间
{
         free(*(char**) p);
}//考虑一下为什么不用free(char* p) 会有什么影响?
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值