C语言伪泛型的实现--栈

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 <stdarg.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, ... ){
	
	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 <stdio.h>
# include <stdlib.h>
# include <string.h>
# 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;
}

5.图示



Writer:  Anden       Email:  andensemail@163.com      Time:  2016.04.8










 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: jmu-java-05集合(泛型)-10-generalstack是关于Java泛型中的通用的学习内容。通用是一种可以存储任意类型数据的结构,通过泛型的方式实现。在学习中,我们可以了解到通用实现原理、使用方法以及注意事项等内容,帮助我们更好地理解和应用Java泛型。 ### 回答2: JMU-Java-05集合(泛型)-10-GeneralStack是一个Stack()的实现类,使用Java中的泛型进行定义,可以存储任何类型的数据。 在该类中,使用一个Object类型的数组进行存储元素,并通过一个整型变量top来表示当前顶元素的下标。在push(入)方法中,先将top加一,然后将元素存储在数组中,实现了入的功能。在pop(出)方法中,先判断是否为空,若为空则抛出空异常,否则将top减一,并返回数组中相应的元素,实现了出的功能。其他方法如isEmpty(判断是否为空)、isFull(判断是否已满)和size(获取中元素个数)也在该类中实现。 该类的泛型定义使得我们可以使用该类存储任何类型的数据,而不需要在定义类时指定数据类型,提高了其灵活性和可复用性。在使用时,我们只需要在创建对象时传入相应的数据类型,如:GeneralStack<Integer> stack = new GeneralStack<Integer>();即可。 该类还实现了Iterable接口,使得该类可以使用foreach循环进行遍历操作,方便了我们对中元素的操作。同时,该类还通过对数组的动态扩容,解决了数组固定大小的限制问题,从而提高了该类的通用性和易用性。 总之,JMU-Java-05集合(泛型)-10-GeneralStack是一个使用泛型实现的通用类,可以存储各种类型的数据,并提供了常用的操作方法,具有较高的可复用性与适用性。 ### 回答3: jmu-java-05集合(泛型)-10-generalstack,意为基于泛型实现。 首先我们需要了解什么是泛型泛型是Java SE 5引入的一个新特性,它可以让我们在编写代码时定义一些未知的类型参数,以达到代码的复用和类型安全的目的。对于集合类或者其他容器类而言,适用于任何类的容器的需求是普遍存在的,这时就可以应用泛型。 在Java中泛型使用<>标识,其语法格式如下: ```java public class 类名<类型参数列表> { //成员变量、方法等 } ``` 类型参数列表是由逗号隔开的参数列表,可以理解为未知类型的占位符。这样的好处是可以在编写集合类时,避免出现类型转换等一系列问题,提高程序的可读性。 而在jmu-java-05集合(泛型)-10-generalstack中,则是基于泛型实现的数据结构——是一种后进先出(LIFO)的数据结构,它只允许在顶添加或删除元素,因此操作非常简单、快速并且高效。 在该实现中,的元素可以是任意类型,数据元素入则是通过 push() 方法实现的,出则是通过 pop() 方法实现的。其中,push() 方法用于向中添加新的元素,pop() 方法用于弹出并返回顶的元素。同时,还提供了 isEmpty() 方法、size() 方法等基本的操作方法。 总之,jmu-java-05集合(泛型)-10-generalstack是一种基于泛型实现,提供了复用和类型安全的目的。其实现使用简便、高效、易于扩展等优点,广泛应用在Java开发中。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值