这其实是一次c语言课的作业。自己拓展了一下题目。
核心就在于如何实现容器对所有数据类型的支持。我的解决方案是在初始化的时候获得数据类型的长度(sizeof),以后在添加数据的时候一个字节一个字节地拷贝。
数据结构如下图:
采用的是链表+数组的做法。我把ELEMENT_NUM设为了40。
声明如下:
struct cInfo;
struct cBox;
struct cInfo
{
cBox *head,*tail;
int blong,size;
};
struct cBox
{
cBox *pre,*next;
cInfo *info;
int move; //使用数 = move
void *pt;
};
void _finit( int bl,cBox &b ); // _finit( sizeof(int),head )
void _fpush( void *data,cBox &b );
void fprint( cBox &b,void (*fct)( void *) ); //传入一个输出函数的指针 输出一个块
void _fprintAll( cBox &b,void (*fct)(void *));
void* ffind( int pos,cBox &b ); //内部调用函数 找到某个位置的地址返回
void* _fget( int pos,cBox &b ); //直接返回的是相应地址的指针,这就要求用户小心使用
void _fchange( void *data,int pos,cBox &b );
void _fclean( cBox &b );
void fcopy( void *data,void *plc,int bl ); //内部调用函数
void _finsert( int pos,void *data,cBox &b ); //在特定位置插入一个元素,如果pos>size,会被压到最后
其中_finsert的实现方式比较特殊,它的功能是在pos位置以后加入一个元素。运行时会找到pos位置所在的相应块,如果块没满( move < ELEMENT_NUM ),就将pos对应位置一下的当前块的元素下移一个单位,然后写入新元素。如果满了,就在那个块后面加入一个0个元素的新块,把当前块的一半元素移动到新块,然后执行假设一没满时候的操作。
PS:虽说是c的代码,但是我偷懒用了c++的引用。
代码:
box.h#ifndef BOX_H_INCLUDED
#define BOX_H_INCLUDED
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#define ELEMENT_NUM 40
struct cInfo;
struct cBox;
struct cInfo
{
cBox *head,*tail;
int blong,size;
};
struct cBox
{
cBox *pre,*next;
cInfo *info;
int move; //使用数 = move
void *pt;
};
void _finit( int bl,cBox &b ); // _finit( sizeof(int),head )
void _fpush( void *data,cBox &b );
void fprint( cBox &b,void (*fct)( void *) ); //传入一个输出函数的指针 输出一个块
void _fprintAll( cBox &b,void (*fct)(void *));
void* ffind( int pos,cBox &b ); //内部调用函数 找到某个位置的地址返回
void* _fget( int pos,cBox &b ); //直接返回的是相应地址的指针,这就要求用户小心使用
void _fchange( void *data,int pos,cBox &b );
void _fclean( cBox &b );
void fcopy( void *data,void *plc,int bl ); //内部调用函数
void _finsert( int pos,void *data,cBox &b ); //在特定位置插入一个元素,如果pos>size,会被压到最后
void _finit( int bl,cBox &b ) //提供给用户初始化
{
b.info = (cInfo*)malloc(sizeof(cInfo));
cInfo *temp = b.info;
temp->head = temp->tail = &b;
temp->blong = bl;
temp->size = 0;
b.pre = b.next = NULL;
b.move = 0;
b.pt = malloc( ELEMENT_NUM * (temp->blong) ); //依然以void*管理
}
void fcopy( void *data,void *plc,int bl ) //调用者确保plc是正确的地址
{
char *wm = (char*)plc;
char *d = (char*)data;
for( int i = 0;i < bl;++i )
{
*wm = *d; //每次拷入一个字节
++wm,++d;
}
}
void _fpush( void *data,cBox &tc ) //压入一个数据在最尾部 ;;;;size
{
cInfo *temp = tc.info;
cBox& b = *temp->tail;
if( b.move >= ELEMENT_NUM ) //用光了
{
b.next = (cBox*)malloc(sizeof(cBox));
//init
cBox *ptn = b.next;
ptn->pre = &b;
ptn->next = NULL;
ptn->info = temp;
ptn->move = 0;
ptn->pt = malloc(ELEMENT_NUM * (temp->blong));
temp->tail = ptn;
//压入
_fpush( data,*ptn );
}
else
{
void *beg = (void*)((char*)b.pt + b.move*(temp->blong)); //写入的首地址
fcopy( data,beg,temp->blong );
++b.move;
++temp->size;
}
}
void fprint( cBox &b,void (*fct)( void *) ) //传入一个输出函数的指针,打印一个块,内部调用
{
cInfo *temp = b.info;
void *pm = b.pt; //起始地址
for( int i = 0;i < b.move;++i )
{
fct( pm ); //打印一个元素
pm = (void*)((char*)pm + temp->blong ); //移动到下一个位置,char为一个字节
}
}
void _fprintAll( cBox &b,void (*fct)(void *) )
{
cInfo *temp = b.info;
cBox *head = temp->head;
while( head != NULL )
{
fprint( *head,fct ); //打印一个块
head = head->next; //移动到下一个块
}
}
void* ffind( int pos,cBox &b ) //内部调用函数 找到某个位置的地址返回
{
cInfo *temp = b.info;
cBox *head = temp->head; //准备遍历
int count = 0;
if( pos >= temp->size ) //size从0开始
return NULL;
while( count + head->move < pos )
{
count += head->move;
head = head->next; //下移
}
//已经找到了
int plc = pos - count;
return (void*)( (char*)head->pt + plc*(temp->blong) ); //相应元素的首地址
}
void* _fget( int pos,cBox &b )
{
void *beg = ffind( pos,b );
return beg;
}
void _fchange( void *data,int pos,cBox &b ) //改变某个位置的元素
{
cInfo *temp = b.info;
if( pos >= temp->size )
return ;
void *plc = ffind( pos,b ); //找到
fcopy( data,plc,temp->blong ); //覆盖
}
void _finsert( int pos,void *data,cBox &b ) //在特定位置之后插入一个元素,如果pos>size,会被压到最后
{
cInfo *temp = b.info;
if( pos >= temp->size )
{
_fpush( data,b );
return ;
}
int count = 0;
cBox *head = temp->head;
while( count + head->move < pos ) //找到应该插入的那个块
{
count += head->move;
head = head->next;
}
////////////////////////bug in here maybe
if( head->move < ELEMENT_NUM ) //本块还有剩余空间
{
int mbeg = pos - count;
void *pa = (void*)((char*)head->pt + (head->move)*(temp->blong));
void *pb = (void*)((char*)head->pt + (head->move+1)*(temp->blong)); //pb = pa
for( int i = head->move;i > mbeg;--i ) //后移
{
fcopy( pa,pb,temp->blong );
pa = (void*)( (char*)pa - temp->blong);
pb = (void*)( (char*)pb - temp->blong);
}
pa = (void*)( (char*)pa + temp->blong);
fcopy( data,pa,temp->blong );
++head->move;
++temp->size;
}
else //本块没有空间了,在后面开辟一个节点,并将一半的数据拷贝过去
{
cBox *pct = head->next;
head->next = (cBox*)malloc( sizeof(cBox) );
cBox *pnew = head->next;
pnew->pre = head,pnew->next = pct;
pnew->info = temp;
pnew->move = 0;
pnew->pt = malloc( ELEMENT_NUM * (temp->blong) );
if( pct == NULL )
temp->tail = pnew;
//移动数据
int bgnum = ELEMENT_NUM / 2; //开始移动的编号
void *pa = (void*)((char*)head->pt + bgnum*(temp->blong));
void *pb = (void*)((char*)pnew->pt);
while( bgnum < ELEMENT_NUM )
{
fcopy(pa,pb,temp->blong);
--head->move;
++pnew->move;
pa = (void*)( (char*)pa + temp->blong);
pb = (void*)( (char*)pb + temp->blong);
++bgnum;
}
//移动完毕 偷懒的做法
_finsert( pos,data,b );
}
}
void clean( cBox &b )
{
cInfo *temp = b.info;
cBox *head = temp->head,*tkeep;
while( head != NULL )
{
tkeep = head;
head = head->next;
free(tkeep);
}
free(temp);
}
#endif // BOX_H_INCLUDED
一个使用例子。使用了三种类型,int,double和自定义类型,只用到了压入和输出全部的功能
box.cpp#include <stdio.h>
#include <stdlib.h>
#include "box.h"
struct mytype
{
int a;
char b;
};
void int_out( void *data )
{
int *p = (int*)data;
printf("%d\n",*p);
}
void double_out( void *data )
{
double *p = (double*)data;
printf("%.2lf\n",*p);
}
void mytype_out( void *data )
{
mytype *p = (mytype*)data;
printf("%c - %d\n",p->b,p->a);
}
int main()
{
cBox tInt,tDb,tMy; //三种数据类型测试
_finit( sizeof(int),tInt ); //初始化
_finit( sizeof(double),tDb );
_finit( sizeof(mytype),tMy );
int ti = 0;
double tdb = 0.0;
mytype tm = { 0,0 };
for( int i = 0;i < 100;++i )
{
_fpush( (void*)&ti,tInt );
_fpush( (void*)&tdb,tDb );
_fpush( (void*)&tm,tMy );
++ti,++tdb;
++tm.a,++tm.b;
}
_fprintAll(tInt,int_out);
system("pause");
system("cls");
_fprintAll(tDb,double_out);
system("pause");
system("cls");
_fprintAll(tMy,mytype_out);
system("pause");
system("cls");
return 0;
}