C语言伪泛型的实现--栈
1、前言
C语言的发眀远早于泛型的提出,正因为泛型的提出,计算机硬件的蓬勃发展,人类创造出了很多优秀的编程语言如 c++ java c# vb等,它们增强了软件的安全性,缩短了软件的开发周期,降底了软件开发成本,提高了程序的可移植性,降底了编码事业的门槛,所以对于计算机软件事业,泛型的实现非常伟大。
我曾经用LAMP架构写了一个网站(自己学习用的),学习了PHP编程语言,也就是这一次接触了面向对象的思想,面向对向的最小数据类型是类(class),里面实现了数据与函数(成员与方法)的封装、继承与多态,而C语言(面向过程)的最小数据类型是结构体与数组,代码的编写非常自由,所以说实现面向对象的思想完全可行,只是看你想实现多少面向对象的功能而以。
2、简介
前天晚上,我思考了很久,什么是数据结构?最终得到一个答案(还不知道是不是对的~~,不对的话email我):数据结构是数据的载体和操作这种载体方法的集合,它与数据是无关的,所以它是可以被抽象出来的。为了实践这个结论,我打算用--栈--这个最简单的数据结构来实践它。
我分别用静态数组栈,动态数组栈,链表栈实现的我的这一结论,由于C语言的本质,静态数组栈的初始化得在主函数中实现或在代码中定义静态数组栈,这样有被于我这篇博文想实现的代码功能,所以这篇博文我不会再讲到它。
3、栈的数据结构
1>动态数组栈的数据结构
typedef struct stack{
size_t topElement; //栈顶元素序号
size_t length; //栈中元素数量
size_t dataLen; //元素长度,单位为字节
char *data; //动态数组指针
} *STACK;
size_t 数据类型是一个 unsigned long的另一写法,因为 sizof()函数返回值和 void *malloc( size_t size )的参数都是size_t类型的,所以我将栈顶元素位置,栈的长度,数据的长度全都
定义成size_t数据类型。
char *data 这个语句不能狭义的理解为定义了一个字符型数据指针,或者定义了一个字符串指针。应该理解为data是数据内存块中第一个字节的地址,这个字节地址及其后面的地址,以二进制的形存放着栈中的各种数据,它本来可以用void *data来定义,但是看了些文章说vc中不能对void*data执行加法运算,需要将data强制转化成字符指针才可以-(char*)data,我用的是linux系统GUN的编译器,无法测试这句话,所以我就所性将data定义为char *data,。
核心算法:
已知 : a. 栈顶元素序号 b.元素的长度(单位字节) c. 栈内存的首字节地址
求当前栈顶元素首字节地址: data + topElement * dataLen
求当前本顶元素: (STACK_TYPE * )( data + topElement * dataLen ) //指针类型强制转化成栈数据类型指针
压栈: size_t pos = (++topElement) * dataLen; //寻找内存中的写入点
memcpy( data + pos, value, dataLen ); //把数据复制进栈内存中的写入点
出栈: topElement--; //这样就OK
2>链表的数据结构
//承载数据的结构
typedef struct node{
char *data; // 数据内存的首字节地址
struct node *pNext; // 下一个节点地址
} NODE, *PNODE;
//承载栈的结构
typedef struct stack{
PNODE top; //栈顶节点地址
PNODE bottom; //栈底节点地址
size_t dataLen; //数据长度(单位字节)
} *STACK;
定义一个NODE有头结点栈链表,再定义了一个STACK结构,结构中的top指向NODE链表的尾节点,bottom指向NODE链表的头节点, dataLen是数据的长度。
核心算法:
已知 : a. 栈链表的栈顶节点 b. 栈链表的栈底节点 c. 栈数据的长度(单位字节)
求栈顶元素地址: top->data;
求栈顶元素: (STACK_TYPE * )( top->data ) //指针类型强制转化成栈数据类型指针
压栈: PNODE new = PNODE malloc( sizeof( NODE ) * 1 );//分配节点空间
new->data = char *malloc( sizeof( char ) * dataLen ); //分配数据空间
memecpy( new->data, value, dataLen ); //复制数据
new->pNext = top;//追加节点
top = new;
出栈: PNODE tmp; //定义一个临时数据节点指针
tmp = top; //指针指向栈定节点
top = top->pNext; //移动栈顶指针
free( tmp->data );//释放数据内存
free( tmp );//释放节点内存
注 void free( void * )这个函数不需要知道待释放内存的长度,因为长度就写在待释放内存中。
4. 代码实现
说明: 一共有4 个文件
stack.h 是栈的接口文件,动态数组与链表栈共用,里面定义了两种栈的数据类型。
d_stack.c 动态数组栈的实现
l_stack.c 链表栈的实现
main.c 测试主函数
由于C语言不具备函数重载的功能,而我用了相同的数据及函数名,所以不能同时测试两种栈,需要屏蔽一个,测试另一个。
具体屏蔽方法看代码。
stack.h
** stack.h
**动态数组堆栈接口文件
*/
/*
**动态数组的数据结构
*/
# if 0
typedef struct stack{
size_t topElement;//栈顶位置
size_t length; //栈中元素数量
size_t dataLen; //数据长度,单位为字节
char *data; //动态数组指针
} *STACK;
# endif
/*
** 链表堆栈的数据结构
*/
//承载数据的节点
typedef struct node{
char *data;
struct node *pNext;
} NODE, *PNODE;
//承载栈的节点
typedef struct stack{
PNODE top;
PNODE bottom;
size_t dataLen;
} *STACK;
/*
** 创建一个动态数组堆栈
*/
STACK CreateStack( size_t dataLen,... );
/*
** 压栈
*/
int Push( STACK stack, void *value );
/*
** 出栈
*/
int Pop( STACK stack );
/*
** 访问栈顶
*/
void *Top( STACK stack );
/*
** 销毁一个栈
*/
void DestroyStack( STACK stack );
d_stack.c
** d_stack.c
** 一个类似于泛型的动态数组堆栈的实现
*/
# include "stack.h"
# include
# define MALLOC( type, len ) (type*)malloc( sizeof( type ) * len )
# define TRUE 1
# define FALSE 0
/**************内部函数声明*******************/
static int IsEmpty( STACK stack );
static int IsFull( STACK stack );
/******************END************************/
/****************接口实现*********************/
/*
** Function: 创建一个动态数组堆栈
** len: 动态数组中元素的个数
** dataLen: 数据的长度单位为字节
** Return: 堆栈指针
*/
STACK CreateStack( size_t dataLen, ... ){
va_list var;
size_t len ;
va_start( var, dataLen );
len = (size_t)va_arg( var, unsigned );
va_end(var);
//栈数组中下标为0的位置保留, 所以len必须大于1
if( len <= 1 )
return NULL;
//定义一个堆栈指针,为其分配动态内存
STACK st;
st = MALLOC( struct stack, 1 );
if( st != NULL ){//分配成功
//分配动态数组空间
st->data = MALLOC( char, (len * dataLen ) );
if( st->data != NULL ){
//初始化参数
st->topElement = 0; //0保留,用于定义空栈
st->length = len; //栈的长度
st->dataLen = dataLen; //数据的长度单位是字节
memset( st->data, 0, dataLen );//初始化st->data[0]的数据,虽然这个数据不会被访问到
return st;//返回堆栈指针
}
free( st );
st = NULL;
}
return NULL;
}
/*
** Function: 压栈
** stack: 堆栈指针
** value: 一个不知数据类型的数据地址
** Return: TRUE 压栈成功, FALSE 压栈失败
*/
int Push( STACK stack , void *value ){
//栈不满时才能压栈
if( IsFull( stack ) )
return FALSE;
//计算数据写入点,并将数据复制到压中
size_t pos;
pos = ++stack->topElement * stack->dataLen;
memcpy( stack->data+pos, value, stack->dataLen );
return TRUE;
}
/*
** Function: 出栈
** stack: 堆栈指针
** Return: TRUE出栈成功, FALSE出栈失败
*/
int Pop( STACK stack ){
//堆栈不为空
if( IsEmpty( stack ) )
return FALSE;
//头节点回移
stack->topElement -= 1;
return TRUE;
}
/*
** Function: 访问栈顶
** stack: 堆栈指针
** Return: 数据的首地址,但不知数据类型
*/
void *Top( STACK stack ){
if( IsEmpty(stack) )
return NULL;
//数据定位
size_t pos;
pos = stack->topElement * stack->dataLen;
//返回栈顶元素的首字节地址
return stack->data+pos;
}
/*
** Function: 销毁一个栈
** stack: 堆栈指针
*/
void DestroyStack( STACK stack ){
//释放堆栈节点
if( stack != NULL ){
//释放动态数组
if( stack->data != NULL )
free( stack->data );
free( stack );
stack = NULL;
}
return;
}
/******************END************************/
/**************内部函数实现*******************/
/*
** Function: 堆栈是否为空
** stack: 堆栈指针
** Return: TRUE 堆栈为空,FALSE 堆栈不为空
*/
static
int IsEmpty( STACK stack ){
if( stack->topElement == 0 )
return TRUE;
return FALSE;
}
/*
** Function: 堆栈是否为满
** stack: 堆栈指针
** Return: TRUE 堆栈已满,FALSE 堆栈不满
*/
static
int IsFull( STACK stack ){
if( stack->topElement == stack->length - 1 )
return TRUE;
return FALSE;
}
/******************END************************/
# undef MALLOC
# undef TRUE
# undef FALSE
l_stack.c
** l_stack.c
** 一个类似于泛型的动态数组堆栈的实现
*/
# include "stack.h"
# define MALLOC( type, len ) (type*)malloc( sizeof( type ) * len )
# define TRUE 1
# define FALSE 0
/**************内部函数声明*******************/
static int IsEmpty( STACK stack );
static int IsFull( STACK stack );
/******************END************************/
/****************接口实现*********************/
/*
** Function: 创建一个动态数组堆栈
** len: 动态数组中元素的个数
** dataLen: 数据的长度单位为字节
** Return: 堆栈指针
*/
STACK CreateStack( size_t dataLen, ... ){
STACK st;
st = MALLOC( struct stack, 1 );
if( st != NULL ){
st->top = MALLOC( NODE, 1 );
if( st->top != NULL ){
st->bottom = st->top;
st->dataLen = dataLen;
st->top->data = NULL;
return st;
}
free( st );
st = NULL;
}
return NULL;
}
/*
** Function: 压栈
** stack: 堆栈指针
** value: 一个不知数据类型的数据地址
** Return: TRUE 压栈成功, FALSE 压栈失败
*/
int Push( STACK stack , void *value ){
//动态分配一个数据节点
PNODE new = MALLOC( NODE, 1 );
if( new != NULL ){
//动态分配数据节点中的数据内存
new->data = MALLOC( char, stack->dataLen );
if( new->data != NULL ){
//数据复制到新的内存中
memcpy( new->data, value, stack->dataLen );
//将新数据节点置为栈顶
new->pNext = stack->top;
stack->top = new;
//压栈成功
return TRUE;
}
//压栈失败
free( new );
new = NULL;
}
return FALSE;
}
/*
** Function: 出栈
** stack: 堆栈指针
** Return: TRUE出栈成功, FALSE出栈失败
*/
int Pop( STACK stack ){
//栈不为空
if( IsEmpty(stack) )
return FALSE;
//临时数据节点指针
PNODE tmp;
//移动栈顶指针
tmp = stack->top;
stack->top = stack->top->pNext;
//释放数据内存及数据节点内存
free( tmp->data );
free( tmp );
return TRUE;
}
/*
** Function: 访问栈顶
** stack: 堆栈指针
** Return: 数据的首地址,但不知数据类型
*/
void *Top( STACK stack ){
return stack->top->data;
}
/*
** Function: 销毁一个栈
** stack: 堆栈指针
*/
void DestroyStack( STACK stack ){
//循环出栈
while( Pop( stack ) )
;
//释放堆栈结构节点
free( stack );
}
/******************END************************/
/**************内部函数实现*******************/
/*
** Function: 堆栈是否为空
** stack: 堆栈指针
** Return: TRUE 堆栈为空,FALSE 堆栈不为空
*/
static
int IsEmpty( STACK stack ){
if( stack->top == stack->bottom )
return TRUE;
return FALSE;
}
/*
** Function: 堆栈是否为满
** stack: 堆栈指针
** Return: TRUE 堆栈已满,FALSE 堆栈不满
** 链表堆栈不用此函数,留个存根
*/
static
int IsFull( STACK stack ){
}
/******************END************************/
# undef MALLOC
# undef TRUE
# undef FALSE
main.c
** main.c
** 用于测试 d_stack.c 与 l_stack.c两个文件的主函数
** 测试方法
** 测试动态数组堆栈时启用语句 测试链表堆栈时启用语句
** FILE statement statement
** stack.h 动态数组堆栈 stack数据类型 数据node 链表堆栈stack数据类型
** main.c #include "d_stack.c" #include "l_stack.c"
** st1 = CreateStack( sizeof(input), 3 ); st1 = CreateStack( sizeof(input) );
** st2 = CreateStack( sizeof(input), 3 ); st2 = CreateStack( sizeof(input) );
** printf("***无长度链表栈实现***\n"); printf("***有长度动态数组栈实现***");
**
**/
# include
# include
# include
# include "l_stack.c"
//# include "d_stack.c"
int main( void ){
/*用一个整数测试*/
STACK st1;
int input, *output;
st1 = CreateStack( sizeof(input) );
//st1 = CreateStack( sizeof(input), 3 );
if( st1 != NULL ){
printf( "*******************无长度链表栈的实现**********************\n" );
//printf( "*****************有长度动态数组栈的实现*********************\n" );
printf( "=====测试整数型的栈=====\n");
input = 100;
printf( "压入一个数据 ");
if( !Push( st1,&input ) )
printf( "压入数据不成功\n");
output = (int*)Top( st1 );
if( output != NULL )
printf( "top = %d\n", *output );
input = 200;
printf( "压入一个数据 ");
if( !Push( st1,&input ) )
printf( "压入数据不成功\n");
output = (int*)Top( st1 );
if( output != NULL )
printf( "top = %d\n", *output );
input = 300;
printf( "压入一个数据 ");
if( !Push( st1,&input ) )
printf( "压入数据不成功 ");
output = (int*)Top( st1 );
if( output != NULL )
printf( "top = %d\n", *output );
printf( "弹出个一个数据 ");
if( !Pop( st1 ) )
printf( "弹出不成功\n" );
output = (int*)Top( st1 );
if( output!= NULL )
printf( "top = %d\n", *output );
printf( "弹出个一个数据 ");
if( !Pop( st1 ) )
printf( "弹出不成功\n" );
output = (int*)Top( st1 );
if( output!= NULL )
printf( "top = %d\n", *output );
else
printf( "栈为空了\n");
DestroyStack( st1 );
}
/*用一个结构测试*/
struct student{
unsigned short num;
char name[20];
char sex[5];
float score;
} stuin, *stuout;
STACK st2;
//st2 = CreateStack( sizeof(stuin), 3 );
st2 = CreateStack( sizeof(stuin) );
if( st2 != NULL ){
printf( "=====测试结构型的栈=====\n");
stuin.num = 2008;
stuin.score = 99.5;
strcpy( stuin.name, "Anden" );
strcpy( stuin.sex, "男");
printf( "压入一个数据 ");
if( !Push( st2, &stuin ) )
printf( "压入数据不成功 ");
stuout = (struct student*)Top( st2 );
if( stuout != NULL )
printf( "top = 学号: %hu 姓名: %s 性别: %s 成绩 %3.2f \n",
stuout->num, stuout->name, stuout->sex, stuout->score);
stuin.num = 2009;
stuin.score = 98.6;
strcpy( stuin.name, "刘倩" );
strcpy( stuin.sex, "女");
printf( "压入一个数据 ");
if( !Push( st2, &stuin ) )
printf( "压入数据不成功 ");
stuout = (struct student*)Top( st2 );
if( stuout != NULL )
printf( "top = 学号: %hu 姓名: %s 性别: %s 成绩 %3.2f \n",
stuout->num, stuout->name, stuout->sex, stuout->score);
stuin.num = 2010;
stuin.score = 90.6;
strcpy( stuin.name, "王小娟" );
strcpy( stuin.sex, "女");
printf( "压入一个数据 ");
if( !Push( st2, &stuin ) )
printf( "压入数据不成功 ");
stuout = (struct student*)Top( st2 );
if( stuout != NULL )
printf( "top = 学号: %hu 姓名: %s 性别: %s 成绩 %3.2f \n",
stuout->num, stuout->name, stuout->sex, stuout->score);
printf( "弹出个一个数据 ");
Pop( st2 );
stuout = (struct student*)Top( st2 );
if( stuout != NULL )
printf( "top = 学号: %hu 姓名: %s 性别: %s 成绩 %3.2f \n",
stuout->num, stuout->name, stuout->sex, stuout->score);
printf( "弹出个一个数据 ");
Pop( st2 );
stuout = (struct student*)Top( st2 );
if( stuout != NULL )
printf( "top = 学号: %hu 姓名: %s 性别: %s 成绩 %3.2f \n",
stuout->num, stuout->name, stuout->sex, stuout->score);
else
printf( "栈为空了\n" );
DestroyStack( st2 );
}
return EXIT_SUCCESS;
}
Writer: Anden Email: [email protected] Time: 2016.04.8