首先,我们需要在栈中设置一个抽象的存储结构, void *elem, 其需要动态分配堆内存, 声明如下所示:
typedef struct {
void *elem;
int elem_size;
int length;
int position;
void (*freefn)(void *elem);
}Stack;
void stackNew(Stack *s, int elem_size, void (*freefn)(void *elem));
void push(Stack *s, void *elem);
void pop(Stack *s, void *result);
void stackDestroy(Stack *s);
void (*freefn)(void *elem) 为函数指针,其表示用于回收抽象指针(void *elem)中,需要回收的数据,比如动态分配的字符串和结构体中动态分配的内容,我们可以通过传递函数指针,使得在回收栈时,不必弹出所有栈中数据进行回收,而是通过stackDestroy调用我们定义的回收方法,进行数据回收。
具体实现如下所示:
#include <stdlib.h>
#include <string.h>
#include <iostream>
void stackNew(Stack *s, int elem_size, void (*freefn)(void *elem)) {
s->length = 4;
s->position = 0;
s->elem_size = elem_size;
s->freefn = freefn;
s->elem = malloc(s->length * s->elem_size);
}
static void stackGrow(Stack *s) {
s->length = s->length * 2;
s->elem = realloc(s->elem, s->length * s->elem_size);
}
void push(Stack *s, void *elem) {
if(s->position == s->length) {
stackGrow(s);
}
void *address;
address = (char *)s->elem + s->position * s->elem_size;
memcpy(address, elem, s->elem_size);
s->position++;
}
void pop(Stack *s, void *result) {
s->position--;
memcpy(result, (char *)s->elem + s->position * s->elem_size, s->elem_size);
}
void stackDestroy(Stack *s) {
int i = 0;
for(; i < s->position; i++) {
s->freefn((char *)s->elem + i * s->elem_size);
}
free(s->elem);
}
由于我们不知道数据类型是什么,所以在初始化栈时,我们需要传递数据类型的大小,在进行数据弹出和压入的时候,我们可以对内存直接进行拷贝工作,这样做的优点是显而易见的。不要忘记,elem_size*length才是需要分配堆内存的大小。
调用时,如下所示:
#include <iostream>
#include <string.h>
#include <stdlib.h>
#include "stack4.h"
using namespace std;
static char * Strdup(const char * source) {
char * temp = (char *)malloc(strlen(source) * sizeof(char) + 1);
strcpy(temp, source);
return temp;
}
void freefn(void *element) {
cout << "data free..." << endl;
char *temp = *((char **) element);
free(temp);
}
int main() {
char *str[] = {"AAAAAAAA", "BBBBBBBB", "CCCCCCCC", "DDDDDDDDD"};
Stack *s = (Stack *) malloc(sizeof(Stack));
stackNew(s, sizeof(char *), freefn);
int i=0;
for(;i < 4; i++) {
char *sc = Strdup(str[i]);
push(s, &sc);
}
char *result;
pop(s, &result);
cout << result << endl;
//回收pop的数据
free(result);
//有了固定的回收函数后,不必弹出所有的栈中数据,即可回收内存
//若没有,回收内存时,必须弹出所有栈中数据,手动进行回收,然后回收栈内存
stackDestroy(s);
free(s);
getchar();
return 0;
}
使用深度拷贝字符串,是考虑到了实际应用中,动态分配内容的生命周期。
还需注意的是,在压栈时,必须对指向动态分配字符串的指针地址进行操作,因为我们在栈的初始化时,传递的数据大小是sizeof(char *),另一方面,也有助于栈的高效操作。
本文介绍了一种自定义栈的实现方法,利用void指针和动态内存管理实现了通用的栈结构,并提供了栈的创建、压栈、弹栈及销毁等基本操作。通过传递函数指针的方式,支持了栈内元素的自定义释放机制。
236

被折叠的 条评论
为什么被折叠?



