首先,我们需要在栈中设置一个抽象的存储结构, 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 *),另一方面,也有助于栈的高效操作。