这其实是一次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; }