uthash的使用

 只要是读过MQTT broker开源实现的mosquitto代码,就需要了解其对uthash的使用。我也打算把这个只有头文件的hash库用到自己的demo里。于是就遇见了坑。
 我发现只能插入一个元素,后面的元素不能插入。弄了半天发现,uthash中的keyptr参数需要的是key的指针形式。
bug1.c

int add_ele(Context *context,SocketWrapper *ele){
    int ret=-1;
    SocketWrapper *found=NULL;
    HASH_FIND(hh_id,context->header,ele->socket,sizeof(Socket*),found);
    if(!found){
    HASH_ADD_KEYPTR(hh_id,context->header,ele->socket,sizeof(Socket*),ele);
    ret=0;
    }
    printf("%d\n",HASH_CNT(hh_id,context->header));
    return ret;
}
int delete_ele(Context *context,SocketWrapper *ele){
    SocketWrapper *found=NULL;
    int ret=-1;
    HASH_FIND(hh_id,context->header,ele->socket,sizeof(Socket*),found);
    if(found){
        HASH_DELETE(hh_id,context->header,ele);
        ret=0;
    }
    return ret;
}

 改完之后,我发现在windows中测试的时候似乎好使,在linux下就不行。百思不得其解。插入总是可以成功,但是在调用remove_ele的时候,就找不到相应的SocketWrapper。但是在insert_ele的插入操作之后,调用 HASH_FIND就可以查到插入的元素。百思不得其解。看了mosuittoto中的使用方式,才发现HASH_ADD_KEYPTR(hh_id,context->header,&ele,sizeof(ele),wrapper);我这样的使用方式,问题极大,这里传入的keyptr的地址是一个临时变量,离开这个函数后,这个地址(很大可能是个寄存器)很可能就指向了其他的内存了。插入的keyptr指向的key就发生变化,在remove_ele找不到对应的SocketWrapper也就正常了。
bug2.c

int insert_ele(Context *context,Socket *ele){
    int ret=-1;
    SocketWrapper *found=NULL;
    HASH_FIND(hh_id,context->header,&ele,sizeof(ele),found);
    if(!found){
    SocketWrapper *wrapper=socket_wrapper_new();
    wrapper->socket=ele;
    printf("insert %p\n",ele);
    HASH_ADD_KEYPTR(hh_id,context->header,&ele,sizeof(ele),wrapper);
    ret=0;
    }
    /*
     HASH_FIND(hh_id,context->header,&ele,sizeof(ele),found);
     if(found){
     printf(found %p\n",found->socket);
     }
    */
    return ret;
}
int remove_ele(Context *context,Socket *ele){
    SocketWrapper *found=NULL;
    int ret=-1;
    HASH_FIND(hh_id,context->header,&ele,sizeof(ele),found);
    if(found){
        printf("remove %p %d\n",found->socket,sizeof(ele));
        HASH_DELETE(hh_id,context->header,found);
        free(found);
        ret=0;
    }
    return ret;
}

 更改就是向HASH_ADD_KEYPTR传入的keyptr,必须指向一块已经分配的内存,不能是临时变量。这个使用方式等同于HASH_ADD(hh_id,context->header,socket,sizeof(ele),wrapper)。这里的socket是一个field。

int insert_ele(Context *context,Socket *ele){
    int ret=-1;
    SocketWrapper *found=NULL;
    HASH_FIND(hh_id,context->header,&ele,sizeof(ele),found);
    if(!found){
    SocketWrapper *wrapper=socket_wrapper_new();
    wrapper->socket=ele;
    printf("insert %p\n",ele);
    //the wright way to use  HASH_ADD_KEYPTR
    HASH_ADD_KEYPTR(hh_id,context->header,&(wrapper->socket),sizeof(ele),wrapper);
    ret=0;
    }
    return ret;
}

 normal.c

#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include "uthash.h"
typedef struct _Socket Socket;
typedef struct _SocketWrapper SocketWrapper;
typedef struct _Context Context;
struct _Socket{
    int fd;
    uint8_t *buf;
    int buf_len;
};
struct _SocketWrapper{
    Socket *socket;
    UT_hash_handle hh_id;
};
struct _Context{
   SocketWrapper *header;
};
Socket * socket_new();
SocketWrapper* socket_wrapper_new();
int insert_ele(Context *context,Socket *ele){
    int ret=-1;
    SocketWrapper *found=NULL;
    HASH_FIND(hh_id,context->header,&ele,sizeof(ele),found);
    if(!found){
    SocketWrapper *wrapper=socket_wrapper_new();
    wrapper->socket=ele;
    HASH_ADD(hh_id,context->header,socket,sizeof(ele),wrapper);
    ret=0;
    }
    return ret;
}
int remove_ele(Context *context,Socket *ele){
    SocketWrapper *found=NULL;
    int ret=-1;
    HASH_FIND(hh_id,context->header,&ele,sizeof(ele),found);
    if(found){
        printf("remove %p %d\n",found->socket,sizeof(ele));
        HASH_DELETE(hh_id,context->header,found);
        free(found);
        ret=0;
    }
    return ret;
}
Socket * socket_new(){
    Socket *ins=NULL;
    ins=(Socket*)malloc(sizeof(Socket));
    if(ins){
        memset(ins,0,sizeof(Socket));
    }
    return ins;
}
SocketWrapper* socket_wrapper_new(){
    SocketWrapper *ins=NULL;
    ins=(SocketWrapper*)malloc(sizeof(SocketWrapper));
    if(ins){
        memset(ins,0,sizeof(SocketWrapper));
    }
    return ins;
}

int main()
{
    Context context;
    memset(&context,0,sizeof(context));
    int ret=0;
    Socket *ele1;
    Socket *ele2;
    Socket *ele3;
    ele1=socket_new();
    ele2=socket_new();
    ele3=socket_new();
    insert_ele(&context,ele1);
    insert_ele(&context,ele2);
    insert_ele(&context,ele3);
    ele1->fd=2;
    printf("%d\n",HASH_CNT(hh_id,context.header));
    printf("ele1 %p\n",ele1);
    printf("ele2 %p\n",ele2);
    printf("ele3 %p\n",ele3);
    SocketWrapper *itor=NULL;
    SocketWrapper *itor_tmp=NULL;
    HASH_ITER(hh_id, context.header, itor, itor_tmp){
        printf("ele %p\n",itor->socket);
        //remove_ele(&context,itor);
        //HASH_DELETE(hh_id,context.header,itor);
    }
    remove_ele(&context,ele1);
    remove_ele(&context,ele2);
    printf("%d\n",HASH_CNT(hh_id,context.header));
    return 0;
}

 uthash使用起来很方便,但是使用教程较少,一不小心,可能就因为使用方式问题,掉进坑里。
 就这点问题,浪费一个上午,加上一个晚上。
[1]C开源hash代码uthash的用法总结 https://blog.csdn.net/houjixin/article/details/15809825
[2]uthash https://blog.csdn.net/shenwansangz/article/details/48729969
[3]c开源库uthash的使用 https://blog.csdn.net/qq_23091073/article/details/86485095

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值