前言
之前做的项目需要用到多级菜单,我由于使用的是128*64的OLED,像是LVGL等UI软件不能使用。而且,由于项目的特殊性,市面上的UI不符合需求,我需要自己动手使用一些专业的绘图软件来构建自己的UI和自己的风格,因此需要一个多级菜单的功能。
网上的普遍采用的多级菜单的方案是基于索引或者树的结构,索引居多。网上构建索引这种方案经过我自己的使用经验和分析,不利于扩展,阅读性也是差的不要不要的,而且也容易造成内存越界访问的缺点。基于这些缺点,我就自己开发了一个简单的多级菜单的功能。这个多级菜单实际上是树的模式,但是我使用了一个比较巧妙地方法来把树结构的数据直接转换成哈希表的方式,使用哈希表的方式构建的一个多级菜单。
1、理解多级菜单的基础
其实,这个多级菜单也是基于索引的,但是它可阅读性好,拓展性也不错,查找的性能差不多是最优,就是有点占用内存空间。
1.1哈希表
这个多级菜单用到的就是一个很少单片机开发人员会学习的点,也就是数据结构。这里使用比较简单的数据结构的一种来构建多级菜单。这个数据结构就是哈希表或者说是散列表。
这里就用哈希表来说明,哈希表,也可以说是Python中的字典,也就是哈希表中有一个关键字,程序可以通过关键字来找到数据存在哪里。
我们用一个图就大概可以理解关键字(Key)和哈希表的关系。下图,程序可以通过哈希函数生成一个Key,这里的Key等于存储的地址,我们得到Key,也就是得到存储的地址,那就可以直接访问地址来获取数据。
注意,这里的Key是自己通过一个哈希函数生成的,我们可以通过这个Key来找到存放的数据。
1.11哈希函数
哈希函数也就是生成一个关键字的函数,该关键字是唯一的,这样我们才能通过地址找到想要的内容。哈希函数有很多,这里简单举个例子,看代码:
#define MAXARRY 50
int HASH_TABLE[MAXARRY];
int create_hashkey(int num)
{
return (num*2+3%MAXARRY);
}
int Find_data(int num)
{
int key=create_hashkey(num);
return HASH_TABLE[key];
}
从这里就可以看出,哈希表实际上建立的是一个地址与关键字之间的联系,通过关键字就可以找到相关的地址,从而找到相关的内容。
1.12哈希冲突
哈希冲突,就是生成的通过哈希函数生成的关键字一样,也就是映射地址是一样的,这样我们就没法通过地址来查找到需要的数据,我们不同的数据不可能都存放到同一个区域。
这就叫做哈希冲突,哈希冲突也是有办法解决的,比如简单的开放地址法,也就是如果冲突,就重新计算哈希值找到下一块空的地址,然后进行存放。
1.13适合场景
哈希表最适合求解问题是查找与给定值相等的记录。这里的重点是给定值很特殊,不重复,也就是计算哈希值的关键字要特殊。每一个都是挺特殊的,都挺唯一,那对于查找的问题就非常棒了。外插一句,查找的时间复杂度是O(1)。
2、哈希表与多级菜单
说了这么多,那哈希表与多级菜单有什么联系捏?我们认真观察,多级菜单需要高频率的查询,而且多级菜单的每一个选项的绝对位置都是唯一的。回顾哈希表的应用场景, 适用于查找与给定值相等数据,我们的每一个选项都是唯一的绝对位置,也就是非常符合哈希表的使用场景,完美。
2.1多级菜单的选项映射
如果我们能够将每一个选项的特殊性标识出来,那么是不是就可以进行映射,就可以查询到我们设置的菜单。那它的图示应该是这个样子的。
我们可以假设,子选项1.1是选项1的下一级。基于这个假设,我们访问的哈希表时候,是不是可以直接输入类似字符串“1.1”就可以知道它的实际内容了。
2.2参考代码
经过上面的简单分析,我们可以通过使用哈希表来构建多级菜单,只不过菜单的上一级和下一级的指向对象我们不关心,我们只需要输入相关选项的位置就一直通过哈希函数得到它指向的内容。如果我们需要改变选项的情况,那么改变输入的位置就可以知道。
这里提供一个关于哈希表的增删改查的基础函数,并基于这四个基础函数进行一个多级菜单的构建和使用的例子。
2.3实际位置定义
我将在下面使用字符串来获得哈希表的唯一Key,这个的字符串格式是个人自定义的,如果想使用这个和理解这个代码,可以先理解下图。
从图中可以看出这个多级目录实际是一个树的结构,如果想要遍历树,一般的思路是使用指针来指向其子节点和父节点,可以知道树是以指针的形式保存了其每一个成员的相对位置。而我使用了一个比较巧妙的方法,直接使用字符串的形式将每个位置的绝对位置表示出来,这其实有点类似文件存放的形式,只不过实际的位置存放在哈希表当中。
2.4 哈希表参考代码
实现基于面向对象的思想,代码略复杂,如果懂函数指针,static和const,结构体也不会特别难懂!
代码中将GUI更新和数据更新写成了两个函数指针,方便分开来使用。
代码使用纯C的方式书写,想要使用在单片机上面,直接复制叭!
2.4.1核心逻辑代码
#include "stdio.h"
#include "string.h"
#define MAX_EXPLAIN 10
#define HASH_SKEY_LEN 15
#define HASH_SIZE 50
#define HASH_NULLKEY '\0'
#define HASH_OK 1
#define HASH_ERROR (HASH_SIZE+2)
typedef unsigned long long uint64_t;
typedef unsigned int uint16_t;
typedef unsigned char uint8_t;
typedef unsigned char Status;
static void * my_memset(void *s,int c,int n)
{
char *c_s=(char *)s;
while(n--) *c_s++=c;
return s;
}
static void * my_memcpy(void *dest,void *src,unsigned int size)
{
char *b_dest=(char *)dest,*b_src=(char *)src;
unsigned int len;
for(len=size;len>0;len--)
{
*b_dest++=*b_src++;
}
return dest;
}
struct hashmenu_vtbl;
typedef struct
{
char pos[HASH_SKEY_LEN];
char explain[MAX_EXPLAIN];
void (*gui)(void *parameter);
void (*act)(void *parameter);
void (*default_act)(void *parameter);
}hashmenu_member;
typedef struct
{
hashmenu_member hashtable[HASH_SIZE];
struct hashmenu_vtbl *c_vptr;
int hash_table_size;
}hashmenu;
struct hashmenu_vtbl
{
Status (*insert)(hashmenu * const Me,hashmenu_member *const member);
Status (*remove)(hashmenu * const Me,hashmenu_member * const member);
Status (*modify)(hashmenu * const Me,hashmenu_member * const member);
Status (*search)(hashmenu * const Me,hashmenu_member * const member);
Status (*searchleft)(hashmenu * const Me,hashmenu_member * const member);
Status (*searchright)(hashmenu * const Me,hashmenu_member * const member);
Status (*searchup)(hashmenu * const Me,hashmenu_member * const member);
Status (*searchdown)(hashmenu * const Me,hashmenu_member * const member);
};
void HashMenuCreate(hashmenu * const Me);
void HashMenuDelete(hashmenu * const Me);
static Status HashOpenInser(hashmenu * const Me,hashmenu_member *const member);
static Status HashOpenRemove(hashmenu * const Me,hashmenu_member * const member);
static Status HashOpenSearch(hashmenu * const Me,hashmenu_member * const member);
static Status HashOpenModify(hashmenu * const Me,hashmenu_member * const member);
static Status HashMenuPeerLeft(hashmenu * const Me,hashmenu_member * const member);
static Status HashMenuPeerRight(hashmenu * const Me,hashmenu_member * const member);
static Status HashMenuDepthUp(hashmenu * const Me,hashmenu_member * const member);
static Status HashMenuDepthDown(hashmenu * const Me,hashmenu_member * const member);
static uint16_t HashOpenKey(const char* skey)
{
char *p = (char*)skey;
uint16_t hasy_key = 0;
if(*p)
{
for(;*p != '\0'; ++p)
hasy_key = (hasy_key << 5) - hasy_key + *p;
}
return hasy_key%(HASH_SIZE-1);
}
static uint16_t HashOpenFindUniqueKey(hashmenu * const Me,hashmenu_member *const member)
{
uint16_t unique_addr;
int cout=HASH_SIZE;
uint16_t clen;
if(Me->hash_table_size<=0) return HASH_ERROR;
unique_addr=HashOpenKey(member->pos);
clen=strlen(member->pos);
clen = (clen >HASH_SKEY_LEN) ?HASH_SKEY_LEN:clen;
while((strncmp(Me->hashtable[unique_addr].pos,member->pos,clen)!=0)&&cout--)
{
unique_addr=(unique_addr+1)%HASH_SIZE;
}
if(cout<0) return HASH_ERROR;
return unique_addr;
}
void HashMenuCreate(hashmenu * const Me)
{
int i;
static struct hashmenu_vtbl vtable;
Me->hash_table_size=0;
my_memset(Me, 0, sizeof(hashmenu));
vtable.insert=&HashOpenInser;
vtable.remove=&HashOpenRemove;
vtable.modify=&HashOpenModify;
vtable.search=&HashOpenSearch;
vtable.searchleft=&HashMenuPeerLeft;
vtable.searchright=&HashMenuPeerRight;
vtable.searchup=&HashMenuDepthUp;
vtable.searchdown=&HashMenuDepthDown;
Me->c_vptr=&vtable;
}
void HashMenuDelete(hashmenu * const Me)
{
int i;
my_memset(Me, 0, sizeof(hashmenu));
}
Status HashOpenInser(hashmenu * const Me,hashmenu_member *const member)
{
uint16_t addr;
if(Me->hash_table_size>=HASH_SIZE) return HASH_ERROR;
addr=HashOpenKey(member->pos);
while(Me->hashtable[addr].explain[0]!=HASH_NULLKEY)
addr=(addr+1)%HASH_SIZE;
my_memcpy(&(Me->hashtable[addr]),member,sizeof(hashmenu_member));
Me->hash_table_size++;
return HASH_OK;
}
Status HashOpenRemove(hashmenu * const Me,hashmenu_member * const member)
{
uint16_t addr;
addr=HashOpenFindUniqueKey(Me,member);
if(addr)
{
my_memset(&(Me->hashtable[addr]),0,sizeof(hashmenu_member));
Me->hash_table_size--;
return HASH_OK;
}
else return HASH_ERROR;
return HASH_OK;
}
Status HashOpenSearch(hashmenu * const Me,hashmenu_member * const member)
{
uint16_t addr;
addr=HashOpenFindUniqueKey(Me,member);
if(addr!=HASH_ERROR)
{
my_memcpy(member,&(Me->hashtable[addr]),sizeof(hashmenu_member));
return HASH_OK;
}
else return HASH_ERROR;
return HASH_OK;
}
Status HashOpenModify(hashmenu * const Me,hashmenu_member * const member)
{
uint16_t addr;
addr=HashOpenFindUniqueKey(Me,member);
if(addr)
{
my_memcpy(&(Me->hashtable[addr]),member,sizeof(hashmenu_member));
return HASH_OK;
}
else return HASH_ERROR;
return HASH_OK;
}
static Status HashMenuPeerLeft(hashmenu * const Me,hashmenu_member * const member)
{
uint8_t *c_pos=(uint8_t *)member->pos;
uint8_t t_chang;
Status flag;
if(Me->hash_table_size<=0) return HASH_ERROR;
while(*++c_pos!='\0');
t_chang=*(--c_pos)-1;
*c_pos=t_chang;
flag=Me->c_vptr->search(Me,member);
if(flag!=HASH_ERROR) return HASH_OK;
else
{
t_chang=(*c_pos)+1;
*c_pos=t_chang;
Me->c_vptr->search(Me,member);
}
member->default_act(NULL);
return HASH_OK;
}
static Status HashMenuPeerRight(hashmenu * const Me,hashmenu_member * const member)
{
uint8_t *c_pos=(uint8_t *)member->pos;
uint8_t t_chang;
Status flag;
if(Me->hash_table_size<=0) return HASH_ERROR;
while(*++c_pos!='\0');
t_chang=*(--c_pos)+1;
*c_pos=t_chang;
flag=Me->c_vptr->search(Me,member);
if(flag!=HASH_ERROR) return HASH_OK;
else
{
t_chang=(*c_pos)-1;
*c_pos=t_chang;
Me->c_vptr->search(Me,member);
}
member->default_act(NULL);
return HASH_OK;
}
static Status HashMenuDepthDown(hashmenu * const Me,hashmenu_member * const member)
{
uint8_t *c_pos=(uint8_t *)member->pos;
Status flag;
uint16_t clen;
if(Me->hash_table_size<=0) return HASH_ERROR;
clen=strlen(member->pos);
if(clen>=HASH_SKEY_LEN-3) return HASH_ERROR;
while(*++c_pos!='\0');
*c_pos='.';
*(c_pos+1)='1';
*(c_pos+2)='\0';
flag=Me->c_vptr->search(Me,member);
if(flag!=HASH_ERROR) return HASH_OK;
else
{
*c_pos='\0';
*(c_pos+1)='\0';
*(c_pos+2)='\0';
Me->c_vptr->search(Me,member);
}
member->default_act(NULL);
return HASH_OK;
}
static Status HashMenuDepthUp(hashmenu * const Me,hashmenu_member * const member)
{
uint8_t *c_pos=(uint8_t *)member->pos;
uint8_t last_pos;
Status flag;
uint16_t clen;
if(Me->hash_table_size<=0) return HASH_ERROR;
clen=strlen(member->pos);
if(clen<=1)
{
Me->c_vptr->search(Me,member);
return HASH_OK;
}
while(*++c_pos!='\0');
last_pos=*(c_pos-1);
*(c_pos-1)='\0';
*(c_pos-2)='\0';
flag=Me->c_vptr->search(Me,member);
if(flag!=HASH_ERROR) return HASH_OK;
else
{
*(c_pos-1)=last_pos;
*(c_pos-2)='.';
Me->c_vptr->search(Me,member);
}
member->default_act(NULL);
return HASH_OK;
}
void PrintMember(hashmenu_member member)
{
printf("pos=%s\r\n",member.pos);
}
void HashOpenPrint(hashmenu const * const Me)
{
for(int i=0;i<HASH_SIZE;i++)
{
if(Me->hashtable[i].explain[0]!=HASH_NULLKEY)
{
printf("key is %s explain = %s\n",Me->hashtable[i].pos,Me->hashtable[i].explain);
}
}
}
2.4.2测试参考代码
使用参考代码,主要可以看我创建数据的格式就行,逻辑都是一样的,就是略长。
void GUI_1(void *parameter)
{
printf("--------------- -GUI 123--------------\r\n");
printf("---------------->OPTION 1<-------------\r\n");
printf("-----------------OPTION 2-------------\r\n");
printf("-----------------OPTION 3-------------\r\n");
}
void GUI_2(void *parameter)
{
printf("--------------- -GUI 123--------------\r\n");
printf("-----------------OPTION 1-------------\r\n");
printf("---------------->OPTION 2<------------\r\n");
printf("-----------------OPTION 3-------------\r\n");
}
void GUI_3(void *parameter)
{
printf("--------------- -GUI 123--------------\r\n");
printf("-----------------OPTION 1-------------\r\n");
printf("-----------------OPTION 2-------------\r\n");
printf("---------------->OPTION 3<------------\r\n");
}
void GUI_1_1(void *parameter)
{
printf("------------------GUI 1_1-------------\r\n");
printf("---------------->OPTION 1.1<------------\r\n");
printf("-----------------OPTION 1.2-------------\r\n");
printf("-----------------OPTION 1.3------------\r\n");
}
void GUI_1_2(void *parameter)
{
printf("------------------GUI 1_1-------------\r\n");
printf("-----------------OPTION 1.1-------------\r\n");
printf("---------------->OPTION 1.2<------------\r\n");
printf("-----------------OPTION 1.3------------\r\n");
}
void GUI_1_3(void *parameter)
{
printf("------------------GUI 1_1-------------\r\n");
printf("-----------------OPTION 1.1-------------\r\n");
printf("-----------------OPTION 1.2-------------\r\n");
printf("---------------->OPTION 1.3<------------\r\n");
}
void func1(void *num)
{
hashmenu_member *mem=(hashmenu_member *)num;
printf("mem->explain =%s\r\n",mem->explain);
}
#define UP 'w'
#define DOWM 's'
#define LEFT 'a'
#define RIGHT 'd'
#define ENTER 'e'
#define QUIT 'q'
hashmenu tmenu;
void test()
{
hashmenu_member t_member;
hashmenu_member d_member;
int x=1;
char choice;
HashMenuCreate(&tmenu);
t_member.act=func1;
t_member.gui=GUI_1;
strcpy(t_member.pos,"1");
strcpy(t_member.explain,"1 EXPLAIN\r\n");
tmenu.c_vptr->insert(&tmenu,&t_member);
PrintMember(t_member);
t_member.act=func1;
t_member.gui=GUI_2;
strcpy(t_member.pos,"2");
strcpy(t_member.explain,"2 EXPLAIN\r\n");
tmenu.c_vptr->insert(&tmenu,&t_member);
PrintMember(t_member);
strcpy(t_member.pos,"3");
t_member.gui=GUI_3;
t_member.act=func1;
strcpy(t_member.explain,"3 EXPLAIN\r\n");
tmenu.c_vptr->insert(&tmenu,&t_member);
PrintMember(t_member);
t_member.act=func1;
t_member.gui=GUI_1_1;
strcpy(t_member.pos,"1.1");
strcpy(t_member.explain,"1.1 EXPLAIN\r\n");
tmenu.c_vptr->insert(&tmenu,&t_member);
PrintMember(t_member);
t_member.gui=GUI_1_2;
t_member.act=func1;
strcpy(t_member.pos,"1.2");
strcpy(t_member.explain,"1.2 EXPLAIN\r\n");
tmenu.c_vptr->insert(&tmenu,&t_member);
PrintMember(t_member);
t_member.gui=GUI_1_3;
t_member.act=func1;
strcpy(t_member.pos,"1.3");
strcpy(t_member.explain,"1.3 EXPLAIN\r\n");
tmenu.c_vptr->insert(&tmenu,&t_member);
PrintMember(t_member);
strcpy(t_member.pos,"2.1");
strcpy(t_member.explain,"2.1 EXPLAIN\r\n");
tmenu.c_vptr->insert(&tmenu,&t_member);
t_member.act=func1;
PrintMember(t_member);
strcpy(t_member.pos,"2.2");
strcpy(t_member.explain,"2.2 EXPLAIN\r\n");
tmenu.c_vptr->insert(&tmenu,&t_member);
t_member.act=func1;
PrintMember(t_member);
strcpy(t_member.pos,"3.1");
strcpy(t_member.explain,"2.1 EXPLAIN\r\n");
tmenu.c_vptr->insert(&tmenu,&t_member);
t_member.act=func1;
PrintMember(t_member);
strcpy(t_member.pos,"3.2");
strcpy(t_member.explain,"2.2 EXPLAIN\r\n");
tmenu.c_vptr->insert(&tmenu,&t_member);
t_member.act=func1;
PrintMember(t_member);
strcpy(t_member.pos,"1.1.1");
strcpy(t_member.explain,"1.1.1 EXPLAIN\r\n");
tmenu.c_vptr->insert(&tmenu,&t_member);
PrintMember(t_member);
t_member.act=func1;
strcpy(t_member.pos,"1.1.2");
strcpy(t_member.explain,"1.1.2 EXPLAIN\r\n");
tmenu.c_vptr->insert(&tmenu,&t_member);
PrintMember(t_member);
t_member.act=func1;
strcpy(t_member.pos,"1.1.3");
strcpy(t_member.explain,"1.1.3 EXPLAIN\r\n");
tmenu.c_vptr->insert(&tmenu,&t_member);
PrintMember(t_member);
strcpy(t_member.pos,"2.1.1");
strcpy(t_member.explain,"2.1.1 EXPLAIN\r\n");
tmenu.c_vptr->insert(&tmenu,&t_member);
t_member.act=func1;
PrintMember(t_member);
strcpy(t_member.pos,"2.2.1");
strcpy(t_member.explain,"2.2.1 EXPLAIN\r\n");
tmenu.c_vptr->insert(&tmenu,&t_member);
t_member.act=func1;
PrintMember(t_member);
strcpy(t_member.pos,"3.1.1");
strcpy(t_member.explain,"3.1.1 EXPLAIN\r\n");
tmenu.c_vptr->insert(&tmenu,&t_member);
PrintMember(t_member);
t_member.act=func1;
strcpy(t_member.pos,"3.1.2");
strcpy(t_member.explain,"3.1.2 EXPLAIN\r\n");
tmenu.c_vptr->insert(&tmenu,&t_member);
PrintMember(t_member);
t_member.act=func1;
strcpy(t_member.pos,"3.1.3");
strcpy(t_member.explain,"3.1.3 EXPLAIN\r\n");
tmenu.c_vptr->insert(&tmenu,&t_member);
PrintMember(t_member);
HashOpenPrint(&tmenu);
printf("--------------------------------------\r\n");
strcpy(d_member.pos,"1");
tmenu.c_vptr->search(&tmenu,&d_member);
PrintMember(d_member);
while(choice!=QUIT)
{
scanf("%c",&choice);
switch (choice)
{
case UP:
if(tmenu.c_vptr->searchup(&tmenu,&d_member)!=HASH_ERROR) d_member.act(&d_member);;
break;
case DOWM:
if(tmenu.c_vptr->searchdown(&tmenu,&d_member)!=HASH_ERROR) d_member.act(&d_member);
break;
case LEFT:
if(tmenu.c_vptr->searchleft(&tmenu,&d_member)!=HASH_ERROR) d_member.act(&d_member);
break;
case RIGHT:
if(tmenu.c_vptr->searchright(&tmenu,&d_member)!=HASH_ERROR) d_member.act(&d_member);
break;
case ENTER:
d_member.act(&d_member);
break;
default:
break;
}
if(choice!='\n') d_member.gui(NULL);
}
}
int main()
{
test();
return 0;
}
2.4.3执行结果
pos=1
context=1
pos=2
context=2
pos=3
context=3
pos=1.1
context=11
pos=1.2
context=12
pos=1.3
context=13
-------------all content-------------
key is 1 explain = 1 EXPLAIN
key is 2 explain = 2 EXPLAIN
key is 3 explain = 3 EXPLAIN
key is 1.1 explain = 1.1 EXPLAIN
key is 1.2 explain = 1.2 EXPLAIN
key is 1.3 explain = 1.3 EXPLAIN
--------------------------------------
pos=1
context=1
d
mem->explain =2 EXPLAIN
,content=2
--------------- -GUI 123--------------
-----------------OPTION 1-------------
---------------->OPTION 2<------------
-----------------OPTION 3-------------
d
mem->explain =3 EXPLAIN
,content=3
--------------- -GUI 123--------------
-----------------OPTION 1-------------
-----------------OPTION 2-------------
---------------->OPTION 3<------------
a
mem->explain =2 EXPLAIN
,content=2
--------------- -GUI 123--------------
-----------------OPTION 1-------------
---------------->OPTION 2<------------
-----------------OPTION 3-------------
a
mem->explain =1 EXPLAIN
,content=1
--------------- -GUI 123--------------
---------------->OPTION 1<-------------
-----------------OPTION 2-------------
-----------------OPTION 3-------------
s
mem->explain =1.1 EXPLAIN
,content=11
------------------GUI 1_1-------------
---------------->OPTION 1.1<------------
-----------------OPTION 1.2-------------
-----------------OPTION 1.3------------
d
mem->explain =1.2 EXPLAIN
,content=12
------------------GUI 1_1-------------
-----------------OPTION 1.1-------------
---------------->OPTION 1.2<------------
-----------------OPTION 1.3------------
d
mem->explain =1.3 EXPLAIN
,content=13
------------------GUI 1_1-------------
-----------------OPTION 1.1-------------
-----------------OPTION 1.2-------------
---------------->OPTION 1.3<------------
a
mem->explain =1.2 EXPLAIN
,content=12
------------------GUI 1_1-------------
-----------------OPTION 1.1-------------
---------------->OPTION 1.2<------------
-----------------OPTION 1.3------------
a
mem->explain =1.1 EXPLAIN
,content=11
------------------GUI 1_1-------------
---------------->OPTION 1.1<------------
-----------------OPTION 1.2-------------
-----------------OPTION 1.3------------
3.碎碎念
其实这个菜单是挺适合调试的时候或者硬件实在非常简陋的环境下使用。比较简陋的环境下比如,只有按键和很简陋的显示屏(例如128*64的OLED这种),或者连一个显示屏都没有。实际上还是建议大家学一些常用的嵌入式 GUI 图形库,比如LVGL,QT等。如果不是像我这样,需要自主绘制合适的GUI,还是不建议在非调试的时候或者硬件实在常简陋的环境下使用。
分享就到这里啦,这个思路也是思考挺久的,从有一个合适的想法到边写文章边写代码,其中推翻之前的思路两次,也整了一整天了,希望对大家有帮助!!!
4.完整参考代码
完整参考代码包括测试与核心的代码就贴在这里啦。
#include "stdio.h"
#include "string.h"
#define MAX_EXPLAIN 10
#define HASH_SKEY_LEN 15
#define HASH_SIZE 50
#define HASH_NULLKEY '\0'
#define HASH_OK 1
#define HASH_ERROR (HASH_SIZE+2)
typedef unsigned long long uint64_t;
typedef unsigned int uint16_t;
typedef unsigned char uint8_t;
typedef unsigned char Status;
static void * my_memset(void *s,int c,int n)
{
char *c_s=(char *)s;
while(n--) *c_s++=c;
return s;
}
static void * my_memcpy(void *dest,void *src,unsigned int size)
{
char *b_dest=(char *)dest,*b_src=(char *)src;
unsigned int len;
for(len=size;len>0;len--)
{
*b_dest++=*b_src++;
}
return dest;
}
struct hashmenu_vtbl;
typedef struct
{
char pos[HASH_SKEY_LEN];
char explain[MAX_EXPLAIN];
void (*gui)(void *parameter);
void (*act)(void *parameter);
void (*default_act)(void *parameter);
}hashmenu_member;
typedef struct
{
hashmenu_member hashtable[HASH_SIZE];
struct hashmenu_vtbl *c_vptr;
int hash_table_size;
}hashmenu;
struct hashmenu_vtbl
{
Status (*insert)(hashmenu * const Me,hashmenu_member *const member);
Status (*remove)(hashmenu * const Me,hashmenu_member * const member);
Status (*modify)(hashmenu * const Me,hashmenu_member * const member);
Status (*search)(hashmenu * const Me,hashmenu_member * const member);
Status (*searchleft)(hashmenu * const Me,hashmenu_member * const member);
Status (*searchright)(hashmenu * const Me,hashmenu_member * const member);
Status (*searchup)(hashmenu * const Me,hashmenu_member * const member);
Status (*searchdown)(hashmenu * const Me,hashmenu_member * const member);
};
void HashMenuCreate(hashmenu * const Me);
void HashMenuDelete(hashmenu * const Me);
static Status HashOpenInser(hashmenu * const Me,hashmenu_member *const member);
static Status HashOpenRemove(hashmenu * const Me,hashmenu_member * const member);
static Status HashOpenSearch(hashmenu * const Me,hashmenu_member * const member);
static Status HashOpenModify(hashmenu * const Me,hashmenu_member * const member);
static Status HashMenuPeerLeft(hashmenu * const Me,hashmenu_member * const member);
static Status HashMenuPeerRight(hashmenu * const Me,hashmenu_member * const member);
static Status HashMenuDepthUp(hashmenu * const Me,hashmenu_member * const member);
static Status HashMenuDepthDown(hashmenu * const Me,hashmenu_member * const member);
static uint16_t HashOpenKey(const char* skey)
{
char *p = (char*)skey;
uint16_t hasy_key = 0;
if(*p)
{
for(;*p != '\0'; ++p)
hasy_key = (hasy_key << 5) - hasy_key + *p;
}
return hasy_key%(HASH_SIZE-1);
}
static uint16_t HashOpenFindUniqueKey(hashmenu * const Me,hashmenu_member *const member)
{
uint16_t unique_addr;
int cout=HASH_SIZE;
uint16_t clen;
if(Me->hash_table_size<=0) return HASH_ERROR;
unique_addr=HashOpenKey(member->pos);
clen=strlen(member->pos);
clen = (clen >HASH_SKEY_LEN) ?HASH_SKEY_LEN:clen;
while((strncmp(Me->hashtable[unique_addr].pos,member->pos,clen)!=0)&&cout--)
{
unique_addr=(unique_addr+1)%HASH_SIZE;
}
if(cout<0) return HASH_ERROR;
return unique_addr;
}
void HashMenuCreate(hashmenu * const Me)
{
int i;
static struct hashmenu_vtbl vtable;
Me->hash_table_size=0;
my_memset(Me, 0, sizeof(hashmenu));
vtable.insert=&HashOpenInser;
vtable.remove=&HashOpenRemove;
vtable.modify=&HashOpenModify;
vtable.search=&HashOpenSearch;
vtable.searchleft=&HashMenuPeerLeft;
vtable.searchright=&HashMenuPeerRight;
vtable.searchup=&HashMenuDepthUp;
vtable.searchdown=&HashMenuDepthDown;
Me->c_vptr=&vtable;
}
void HashMenuDelete(hashmenu * const Me)
{
int i;
my_memset(Me, 0, sizeof(hashmenu));
}
Status HashOpenInser(hashmenu * const Me,hashmenu_member *const member)
{
uint16_t addr;
if(Me->hash_table_size>=HASH_SIZE) return HASH_ERROR;
addr=HashOpenKey(member->pos);
while(Me->hashtable[addr].explain[0]!=HASH_NULLKEY)
addr=(addr+1)%HASH_SIZE;
my_memcpy(&(Me->hashtable[addr]),member,sizeof(hashmenu_member));
Me->hash_table_size++;
return HASH_OK;
}
Status HashOpenRemove(hashmenu * const Me,hashmenu_member * const member)
{
uint16_t addr;
addr=HashOpenFindUniqueKey(Me,member);
if(addr)
{
my_memset(&(Me->hashtable[addr]),0,sizeof(hashmenu_member));
Me->hash_table_size--;
return HASH_OK;
}
else return HASH_ERROR;
return HASH_OK;
}
Status HashOpenSearch(hashmenu * const Me,hashmenu_member * const member)
{
uint16_t addr;
addr=HashOpenFindUniqueKey(Me,member);
if(addr!=HASH_ERROR)
{
my_memcpy(member,&(Me->hashtable[addr]),sizeof(hashmenu_member));
return HASH_OK;
}
else return HASH_ERROR;
return HASH_OK;
}
Status HashOpenModify(hashmenu * const Me,hashmenu_member * const member)
{
uint16_t addr;
addr=HashOpenFindUniqueKey(Me,member);
if(addr)
{
my_memcpy(&(Me->hashtable[addr]),member,sizeof(hashmenu_member));
return HASH_OK;
}
else return HASH_ERROR;
return HASH_OK;
}
static Status HashMenuPeerLeft(hashmenu * const Me,hashmenu_member * const member)
{
uint8_t *c_pos=(uint8_t *)member->pos;
uint8_t t_chang;
Status flag;
if(Me->hash_table_size<=0) return HASH_ERROR;
while(*++c_pos!='\0');
t_chang=*(--c_pos)-1;
*c_pos=t_chang;
flag=Me->c_vptr->search(Me,member);
if(flag!=HASH_ERROR) return HASH_OK;
else
{
t_chang=(*c_pos)+1;
*c_pos=t_chang;
Me->c_vptr->search(Me,member);
}
member->default_act(NULL);
return HASH_OK;
}
static Status HashMenuPeerRight(hashmenu * const Me,hashmenu_member * const member)
{
uint8_t *c_pos=(uint8_t *)member->pos;
uint8_t t_chang;
Status flag;
if(Me->hash_table_size<=0) return HASH_ERROR;
while(*++c_pos!='\0');
t_chang=*(--c_pos)+1;
*c_pos=t_chang;
flag=Me->c_vptr->search(Me,member);
if(flag!=HASH_ERROR) return HASH_OK;
else
{
t_chang=(*c_pos)-1;
*c_pos=t_chang;
Me->c_vptr->search(Me,member);
}
member->default_act(NULL);
return HASH_OK;
}
static Status HashMenuDepthDown(hashmenu * const Me,hashmenu_member * const member)
{
uint8_t *c_pos=(uint8_t *)member->pos;
Status flag;
uint16_t clen;
if(Me->hash_table_size<=0) return HASH_ERROR;
clen=strlen(member->pos);
if(clen>=HASH_SKEY_LEN-3) return HASH_ERROR;
while(*++c_pos!='\0');
*c_pos='.';
*(c_pos+1)='1';
*(c_pos+2)='\0';
flag=Me->c_vptr->search(Me,member);
if(flag!=HASH_ERROR) return HASH_OK;
else
{
*c_pos='\0';
*(c_pos+1)='\0';
*(c_pos+2)='\0';
Me->c_vptr->search(Me,member);
}
member->default_act(NULL);
return HASH_OK;
}
static Status HashMenuDepthUp(hashmenu * const Me,hashmenu_member * const member)
{
uint8_t *c_pos=(uint8_t *)member->pos;
uint8_t last_pos;
Status flag;
uint16_t clen;
if(Me->hash_table_size<=0) return HASH_ERROR;
clen=strlen(member->pos);
if(clen<=1)
{
Me->c_vptr->search(Me,member);
return HASH_OK;
}
while(*++c_pos!='\0');
last_pos=*(c_pos-1);
*(c_pos-1)='\0';
*(c_pos-2)='\0';
flag=Me->c_vptr->search(Me,member);
if(flag!=HASH_ERROR) return HASH_OK;
else
{
*(c_pos-1)=last_pos;
*(c_pos-2)='.';
Me->c_vptr->search(Me,member);
}
member->default_act(NULL);
return HASH_OK;
}
void PrintMember(hashmenu_member member)
{
printf("pos=%s\r\n",member.pos);
}
void HashOpenPrint(hashmenu const * const Me)
{
for(int i=0;i<HASH_SIZE;i++)
{
if(Me->hashtable[i].explain[0]!=HASH_NULLKEY)
{
printf("key is %s explain = %s\n",Me->hashtable[i].pos,Me->hashtable[i].explain);
}
}
}
void GUI_1(void *parameter)
{
printf("--------------- -GUI 123--------------\r\n");
printf("---------------->OPTION 1<-------------\r\n");
printf("-----------------OPTION 2-------------\r\n");
printf("-----------------OPTION 3-------------\r\n");
}
void GUI_2(void *parameter)
{
printf("--------------- -GUI 123--------------\r\n");
printf("-----------------OPTION 1-------------\r\n");
printf("---------------->OPTION 2<------------\r\n");
printf("-----------------OPTION 3-------------\r\n");
}
void GUI_3(void *parameter)
{
printf("--------------- -GUI 123--------------\r\n");
printf("-----------------OPTION 1-------------\r\n");
printf("-----------------OPTION 2-------------\r\n");
printf("---------------->OPTION 3<------------\r\n");
}
void GUI_1_1(void *parameter)
{
printf("------------------GUI 1_1-------------\r\n");
printf("---------------->OPTION 1.1<------------\r\n");
printf("-----------------OPTION 1.2-------------\r\n");
printf("-----------------OPTION 1.3------------\r\n");
}
void GUI_1_2(void *parameter)
{
printf("------------------GUI 1_1-------------\r\n");
printf("-----------------OPTION 1.1-------------\r\n");
printf("---------------->OPTION 1.2<------------\r\n");
printf("-----------------OPTION 1.3------------\r\n");
}
void GUI_1_3(void *parameter)
{
printf("------------------GUI 1_1-------------\r\n");
printf("-----------------OPTION 1.1-------------\r\n");
printf("-----------------OPTION 1.2-------------\r\n");
printf("---------------->OPTION 1.3<------------\r\n");
}
void func1(void *num)
{
hashmenu_member *mem=(hashmenu_member *)num;
printf("mem->explain =%s\r\n",mem->explain);
}
#define UP 'w'
#define DOWM 's'
#define LEFT 'a'
#define RIGHT 'd'
#define ENTER 'e'
#define QUIT 'q'
hashmenu tmenu;
void test()
{
hashmenu_member t_member;
hashmenu_member d_member;
int x=1;
char choice;
HashMenuCreate(&tmenu);
t_member.act=func1;
t_member.gui=GUI_1;
strcpy(t_member.pos,"1");
strcpy(t_member.explain,"1 EXPLAIN\r\n");
tmenu.c_vptr->insert(&tmenu,&t_member);
PrintMember(t_member);
t_member.act=func1;
t_member.gui=GUI_2;
strcpy(t_member.pos,"2");
strcpy(t_member.explain,"2 EXPLAIN\r\n");
tmenu.c_vptr->insert(&tmenu,&t_member);
PrintMember(t_member);
strcpy(t_member.pos,"3");
t_member.gui=GUI_3;
t_member.act=func1;
strcpy(t_member.explain,"3 EXPLAIN\r\n");
tmenu.c_vptr->insert(&tmenu,&t_member);
PrintMember(t_member);
t_member.act=func1;
t_member.gui=GUI_1_1;
strcpy(t_member.pos,"1.1");
strcpy(t_member.explain,"1.1 EXPLAIN\r\n");
tmenu.c_vptr->insert(&tmenu,&t_member);
PrintMember(t_member);
t_member.gui=GUI_1_2;
t_member.act=func1;
strcpy(t_member.pos,"1.2");
strcpy(t_member.explain,"1.2 EXPLAIN\r\n");
tmenu.c_vptr->insert(&tmenu,&t_member);
PrintMember(t_member);
t_member.gui=GUI_1_3;
t_member.act=func1;
strcpy(t_member.pos,"1.3");
strcpy(t_member.explain,"1.3 EXPLAIN\r\n");
tmenu.c_vptr->insert(&tmenu,&t_member);
PrintMember(t_member);
strcpy(t_member.pos,"2.1");
strcpy(t_member.explain,"2.1 EXPLAIN\r\n");
tmenu.c_vptr->insert(&tmenu,&t_member);
t_member.act=func1;
PrintMember(t_member);
strcpy(t_member.pos,"2.2");
strcpy(t_member.explain,"2.2 EXPLAIN\r\n");
tmenu.c_vptr->insert(&tmenu,&t_member);
t_member.act=func1;
PrintMember(t_member);
strcpy(t_member.pos,"3.1");
strcpy(t_member.explain,"2.1 EXPLAIN\r\n");
tmenu.c_vptr->insert(&tmenu,&t_member);
t_member.act=func1;
PrintMember(t_member);
strcpy(t_member.pos,"3.2");
strcpy(t_member.explain,"2.2 EXPLAIN\r\n");
tmenu.c_vptr->insert(&tmenu,&t_member);
t_member.act=func1;
PrintMember(t_member);
strcpy(t_member.pos,"1.1.1");
strcpy(t_member.explain,"1.1.1 EXPLAIN\r\n");
tmenu.c_vptr->insert(&tmenu,&t_member);
PrintMember(t_member);
t_member.act=func1;
strcpy(t_member.pos,"1.1.2");
strcpy(t_member.explain,"1.1.2 EXPLAIN\r\n");
tmenu.c_vptr->insert(&tmenu,&t_member);
PrintMember(t_member);
t_member.act=func1;
strcpy(t_member.pos,"1.1.3");
strcpy(t_member.explain,"1.1.3 EXPLAIN\r\n");
tmenu.c_vptr->insert(&tmenu,&t_member);
PrintMember(t_member);
strcpy(t_member.pos,"2.1.1");
strcpy(t_member.explain,"2.1.1 EXPLAIN\r\n");
tmenu.c_vptr->insert(&tmenu,&t_member);
t_member.act=func1;
PrintMember(t_member);
strcpy(t_member.pos,"2.2.1");
strcpy(t_member.explain,"2.2.1 EXPLAIN\r\n");
tmenu.c_vptr->insert(&tmenu,&t_member);
t_member.act=func1;
PrintMember(t_member);
strcpy(t_member.pos,"3.1.1");
strcpy(t_member.explain,"3.1.1 EXPLAIN\r\n");
tmenu.c_vptr->insert(&tmenu,&t_member);
PrintMember(t_member);
t_member.act=func1;
strcpy(t_member.pos,"3.1.2");
strcpy(t_member.explain,"3.1.2 EXPLAIN\r\n");
tmenu.c_vptr->insert(&tmenu,&t_member);
PrintMember(t_member);
t_member.act=func1;
strcpy(t_member.pos,"3.1.3");
strcpy(t_member.explain,"3.1.3 EXPLAIN\r\n");
tmenu.c_vptr->insert(&tmenu,&t_member);
PrintMember(t_member);
HashOpenPrint(&tmenu);
printf("--------------------------------------\r\n");
strcpy(d_member.pos,"1");
tmenu.c_vptr->search(&tmenu,&d_member);
PrintMember(d_member);
while(choice!=QUIT)
{
scanf("%c",&choice);
switch (choice)
{
case UP:
if(tmenu.c_vptr->searchup(&tmenu,&d_member)!=HASH_ERROR) d_member.act(&d_member);;
break;
case DOWM:
if(tmenu.c_vptr->searchdown(&tmenu,&d_member)!=HASH_ERROR) d_member.act(&d_member);
break;
case LEFT:
if(tmenu.c_vptr->searchleft(&tmenu,&d_member)!=HASH_ERROR) d_member.act(&d_member);
break;
case RIGHT:
if(tmenu.c_vptr->searchright(&tmenu,&d_member)!=HASH_ERROR) d_member.act(&d_member);
break;
case ENTER:
d_member.act(&d_member);
break;
default:
break;
}
if(choice!='\n') d_member.gui(NULL);
}
}
int main()
{
test();
return 0;
}
5.PS
其实文章的结构和我写的代码定义的位置是差不多一样的,可以翻上去看看。
|