C语言实现循环队列

本文介绍了一种通过宏定义实现的C语言通用队列,适用于多种数据类型,包括整型、指针类型和字符串。队列在满时会丢弃最早的数据,提供了初始化、去初始化、读写操作以及内存管理。代码示例展示了如何测试不同类型的队列,强调了使用后必须正确去初始化以避免内存泄漏。

背景

一般高级语言中都有直接可以使用的数据结构。c语言没有默认的实现。很多时候就根据不同是数据类型需要反复的造轮子。本文通过宏定义的方式实现了一个通用的队列实现,可以覆盖绝大部分的队列使用要求。

代码

queue.h

#ifndef __QUEUE_H_
#define __QUEUE_H_
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#ifndef logR
#define logR(fmt,...) printf(fmt, ##__VA_ARGS__)
#endif

#ifndef logD
#define logD(fmt,...) printf("[%s@%d]" fmt "\n", __FILE__, __LINE__, ##__VA_ARGS__)
#endif
#ifndef logI
#define logI(fmt,...) printf("[%s@%d]" fmt "\n", __FILE__, __LINE__, ##__VA_ARGS__)
#endif
#ifndef logE
#define logE(fmt,...) printf("[%s@%d]" fmt "\n", __FILE__, __LINE__, ##__VA_ARGS__)
#endif

typedef struct{
  void *queue;
  ssize_t l;
  ssize_t r;
  ssize_t w;
}QUEUE_HANDLE;

#define queueInit( type, handle, len ) \
  do{\
    (handle).queue = (type*)malloc(sizeof(type)*len);\
    if((handle).queue == NULL){\
      logE("malloc error");\
      break;\
    }\
    (handle).l = len;\
    (handle).r = 0;\
    (handle).w = 0;\
  }while(0)

#define queueDeinit( handle ) \
  do{\
    free((handle).queue);\
    (handle).queue = NULL;\
    (handle).r = 0;\
    (handle).w = 0;\
  }while(0)

#define CHECK_RANGE(num,max) ( (num)>0?( (num) % (max)):( ((num) + ((num)/(max)+1)*(max)) % (max) ))

#define FULL_CHECK(handle)\
  if( CHECK_RANGE((handle).w + 1,(handle).l) == CHECK_RANGE((handle).r,(handle).l))\
    (handle).r = CHECK_RANGE((handle).r+1, (handle).l) ///< 检查队列是否已经满了,满了之后则丢弃最早的一次数据

#define queueLen(handle)	CHECK_RANGE((handle).w+(handle).l - (handle).r, (handle).l) ///< 返回队列长度,w写指针,r读指针

#define queueWrite( type,handle,data)\
  ((type*)(handle).queue)[(handle).w] = data;\
  FULL_CHECK( handle );\
  (handle).w = CHECK_RANGE( (handle).w+1, (handle).l )                          ///< 向队列中写入一个数据

#define queueRead(type,handle,data) \
  data=((type*)(handle).queue)[(handle).r];\
  (handle).r = CHECK_RANGE( (handle).r+1, (handle).l )                          ///< 从队列中读出一个数据

/* 整数类型 */
#define queueInitInt(handle,len)    queueInit(int,handle,len)                ///< 初始化整形的队列
#define queueWriteInt(handle,data)  queueWrite(int,handle,data)              ///< 写入整形的数据
#define queueReadInt(handle,data)   queueRead(int,handle,data)               ///< 读取整形数据
#define queueDeinitInt(handle)      queueDeinit(handle)                  ///< 去初始化队列

/* 指针类型 */
#define FULL_CHECK_PTR(type,handle)\
  if( CHECK_RANGE((handle).w + 1,(handle).l) == CHECK_RANGE((handle).r,(handle).l)){\
    free(((type*)handle.queue)[(handle).r]);\
    ((type*)handle.queue)[(handle).r] = NULL;\
    (handle).r = CHECK_RANGE((handle).r+1, (handle).l);\
  } ///< 检查队列是否已经满了,满了之后则丢弃最早的一次数据

#define queueWritePtr( type,handle,ptr)\
  ((type*)(handle).queue)[(handle).w] = ptr;\
  FULL_CHECK_PTR( type,handle );\
  (handle).w = CHECK_RANGE( (handle).w+1, (handle).l )                          ///< 向队列中写入一个数据

#define queueDeinitPtr(type,handle)\
  for(int i=0;i<handle.l;i++){\
    if( ((type*)handle.queue)[i] != NULL ){\
    free(((type*)handle.queue)[i]);\
    ((type*)handle.queue)[i] = NULL;\
    }\
  }\
  queueDeinit(handle)                                                          ///< 去初始化字符串队列,字符串队列由于写入是使用的是strdup,所以需要将未读出的数据全部读出释放,防止内存泄露

#define FULL_CHECK_PTR_EX(type,handle,fun)\
  if( CHECK_RANGE((handle).w + 1,(handle).l) == CHECK_RANGE((handle).r,(handle).l)){\
    fun(((type*)handle.queue)[(handle).r]);\
    free(((type*)handle.queue)[(handle).r]);\
    ((type*)handle.queue)[(handle).r] = NULL;\
    (handle).r = CHECK_RANGE((handle).r+1, (handle).l);\
  } ///< 检查队列是否已经满了,满了之后则丢弃最早的一次数据

#define queueWritePtrEx( type,handle,ptr, fun)\
  ((type*)(handle).queue)[(handle).w] = ptr;\
  FULL_CHECK_PTR_EX( type,handle, fun );\
  (handle).w = CHECK_RANGE( (handle).w+1, (handle).l )                          ///< 向队列中写入一个数据

#define queueDeinitPtrEx(type,handle, fun)\
  for(int i=0;i<handle.l;i++){\
    if( ((type*)handle.queue)[i] != NULL ){\
      fun( ((type*)handle.queue)[i] );\
      free(((type*)handle.queue)[i]);\
      ((type*)handle.queue)[i] = NULL;\
    }\
  }\
  queueDeinit(handle)                                                          ///< 去初始化字符串队列,字符串队列由于写入是使用的是strdup,所以需要将未读出的数据全部读出释放,防止内存泄露

/* 字符串类型,队列不使用时一定要调用deinit释放内存,防止泄露 */
#define queueInitStr(handle,len)    queueInit(char*,handle,len)               ///< 初始化一个字符串类型的队列
#define queueDeinitStr(handle)      queueDeinitPtr(char*,handle)              ///< 去初始化字符串队列,字符串队列由于写入是使用的是strdup,所以需要将未读出的数据全部读出释放,防止内存泄露

#define queueWriteStr(handle,str)   queueWritePtr(char*, handle, strdup(str)) ///< 向队列中写入一个字符串,使用strdup拷贝字符串到队列中
#define queueReadStr(handle,strPtr) queueRead(char*,handle,strPtr)            ///< 从队列中读出一个字符串,使用完毕之后需要释放内存

#endif

queue2.c

#include "queue2.h"
#include <malloc.h>

#if 0   // 内存分配调试
extern void *__libc_malloc(size_t size);
extern void *__libc_free(void *ptr);

int malloc_hook_active = 1;
int free_hook_active = 1;

void* my_malloc_hook (size_t size, void *caller)
{
  void *result;

  // deactivate hooks for logging
  malloc_hook_active = 0;

  result = malloc(size);

  // do logging
  printf("+malloc(%ld) %p\n", size, result);

  // reactivate hooks
  malloc_hook_active = 1;

  return result;
}

void my_free_hook(void *ptr, void *caller)
{

  // deactivate hooks for logging
  free_hook_active = 0;

  free(ptr);

  // do logging
  printf("-free() %p\n", ptr);

  // reactivate hooks
  free_hook_active = 1;

}

void* malloc (size_t size)
{
  void *caller = __builtin_return_address(0);
  if (malloc_hook_active)
    return my_malloc_hook(size, caller);
  return __libc_malloc(size);
}

void free(void *ptr)
{
  void *caller = __builtin_return_address(0);
  if (free_hook_active)
    my_free_hook(ptr, caller);
  else
    __libc_free(ptr);
}
#endif

typedef struct{
  char buff[8];
  int  len;
}USER_STRUCT;

typedef struct{
  char *buff;
  int  len;
}USER_STRUCT_EX;

/*
 * @brief 打印整形队列数组中的所有的数据,队列的中数据以绿色打印
 * */
void printQueueInt(QUEUE_HANDLE handle)
{
#define DEFAULT   "\033[0m"
#define GREEN     "\033[0;32m\033[1m"
#define TEXT_GREEN(s)   GREEN s "\033[0m"

  for(int i=0; i< handle.l; i++){
    if(
       ( handle.w>handle.r && handle.r <= i && i< handle.w && handle.r != handle.w ) ||
       ( handle.w<handle.r && ( i<handle.w || handle.r <= i ) && handle.r != handle.w )
      ){
      logR(TEXT_GREEN("%2d "),((int*)handle.queue)[i]);
    }else{
      logR("%2d ",((int*)handle.queue)[i]);
    }
  }
  logR("\tr:%2ld, w:%2ld\n", handle.r, handle.w);
}

/*
 * @brief 打印字符队列数组中的所有的数据,队列的中数据以绿色打印
 * */
void printQueueStr(QUEUE_HANDLE handle)
{
#define DEFAULT   "\033[0m"
#define GREEN     "\033[0;32m\033[1m"
#define TEXT_GREEN(s)   GREEN s "\033[0m"

  for(int i=0; i< handle.l; i++){
    if(
       ( handle.w>handle.r && handle.r <= i && i< handle.w && handle.r != handle.w ) ||
       ( handle.w<handle.r && ( i<handle.w || handle.r <= i ) && handle.r != handle.w )
      ){
      logR(TEXT_GREEN("'%s' "),((char**)handle.queue)[i]);
    }else{
      logR("'%s' ",((char**)handle.queue)[i]);
    }
  }
  logR("\tr:%2ld, w:%2ld\n", handle.r, handle.w);
}

/*
 * @brief 打印字符队列数组中的所有的数据,队列的中数据以绿色打印
 * */
void printQueuePtr(QUEUE_HANDLE handle)
{
#define DEFAULT   "\033[0m"
#define GREEN     "\033[0;32m\033[1m"
#define TEXT_GREEN(s)   GREEN s "\033[0m"

  for(int i=0; i< handle.l; i++){
    if(
       ( handle.w>handle.r && handle.r <= i && i< handle.w && handle.r != handle.w ) ||
       ( handle.w<handle.r && ( i<handle.w || handle.r <= i ) && handle.r != handle.w )
      ){
      logR(TEXT_GREEN("'%s' "), ((USER_STRUCT**)handle.queue)[i]->buff );
    }else{
      logR("'%s' ", ((USER_STRUCT**)handle.queue)[i]->buff );
    }
  }
  logR("\tr:%2ld, w:%2ld\n", handle.r, handle.w);
}

/*
 * @brief 打印字符队列数组中的所有的数据,队列的中数据以绿色打印
 * */
void printQueuePtrEx(QUEUE_HANDLE handle)
{
#define DEFAULT   "\033[0m"
#define GREEN     "\033[0;32m\033[1m"
#define TEXT_GREEN(s)   GREEN s "\033[0m"

  for(int i=0; i< handle.l; i++){
    if(
       ( handle.w>handle.r && handle.r <= i && i< handle.w && handle.r != handle.w ) ||
       ( handle.w<handle.r && ( i<handle.w || handle.r <= i ) && handle.r != handle.w )
      ){
      logR(TEXT_GREEN("'%s' "), ((USER_STRUCT_EX**)handle.queue)[i]->buff );
    }else{
      if( ((USER_STRUCT_EX**)handle.queue)[i] != NULL )
        logR("'%s' ", ((USER_STRUCT_EX**)handle.queue)[i]->buff );
      else
        logR("(null) ");
    }
  }
  logR("\tr:%2ld, w:%2ld\n", handle.r, handle.w);
}

int main_int()
{
  int data=0;
  logD("空队列:");
  QUEUE_HANDLE test_int;
  queueInitInt(test_int,8);

  logD("\n");

  for(int i=0; i<12; i++){
    data = rand()%100;
    logD("插入数据:%d", data);
    queueWriteInt(test_int, data);
    printQueueInt(test_int);
  }

  logD("\n");

  while(queueLen(test_int)>0){
    queueReadInt(test_int, data);
    logD("读出数据:%d", data);
    printQueueInt(test_int);
  }
  queueDeinitInt(test_int);
  return 0;
}

int main_str()
{
  logD("空队列:");
  QUEUE_HANDLE test_str;
  queueInitStr(test_str,8);

  logD("\n");

  for(int i=0; i<12; i++){
    int data=0;
    char buff[5] = {0};
    data = rand()%100;
    sprintf(buff,"%2d",data);
    logD("插入数据:'%s'", buff);
    queueWriteStr(test_str, buff);
    printQueueStr(test_str);
  }

  logR("\n");

  while(queueLen(test_str)>0){
    char *str = NULL;
    queueReadStr(test_str, str);
    logD("读出数据:'%s'", str);
    printQueueStr(test_str);
  }
  logD("deinit queue");
  queueDeinitStr(test_str);
  return 0;
}

int main_user()
{
  logD("空队列:");
  QUEUE_HANDLE test_user;
  queueInit( USER_STRUCT*,test_user, 8 );

  logD("\n");

  for(int i=0; i<12; i++){
    int data=0;
    USER_STRUCT *user = (USER_STRUCT*)malloc(sizeof(USER_STRUCT));
    data = rand()%100;
    sprintf(user->buff,"%2d",data);
    user->len = strlen(user->buff);
    logD("插入数据:'%s'", user->buff);
    queueWritePtr( USER_STRUCT*,test_user, user);
    printQueuePtr(test_user);
  }

  logR("\n");

  while(queueLen(test_user)>0){
    USER_STRUCT *user = NULL;
    queueRead( USER_STRUCT*, test_user, user);
    logD("读出数据:%d:'%s'", user->len, user->buff);
    printQueuePtr(test_user);
  }
  logD("deinit queue");
  queueDeinitPtr( USER_STRUCT*, test_user);
  return 0;
}

void del_user_struct_ex(USER_STRUCT_EX *user)
{
  if( user->buff != NULL ){
    free( user->buff );
    user->buff = NULL;
  }
}

int main_user_ex()
{
  logD("空队列:");
  QUEUE_HANDLE  test_user_ex;
  queueInit( USER_STRUCT_EX*,test_user_ex, 8 );

  logD("\n");

  for(int i=0; i<12; i++){
    int data=0;
    USER_STRUCT_EX *user = (USER_STRUCT_EX*)malloc(sizeof(USER_STRUCT_EX));
    data = rand()%100;
    char buff[8];
    sprintf(buff,"%2d",data);
    user->buff = strdup(buff);
    user->len = strlen(user->buff);
    logD("插入数据:'%s'", user->buff);
    queueWritePtrEx( USER_STRUCT_EX*,test_user_ex, user, del_user_struct_ex);
    logD();
    printQueuePtrEx(test_user_ex);
  }

  logR("\n");

  while(queueLen(test_user_ex)>0){
    USER_STRUCT_EX *user = NULL;
    queueRead( USER_STRUCT_EX*, test_user_ex, user);
    logD("读出数据:%d:'%s'", user->len, user->buff);
    printQueuePtrEx(test_user_ex);
  }
  logD("deinit queue");
  queueDeinitPtrEx( USER_STRUCT_EX*, test_user_ex, del_user_struct_ex);
  return 0;
}

void user_help(char *app)
{
  logE("%s [0/1/2/3]", app);
  logE("%s 0 test int queue", app);
  logE("%s 1 test string queue", app);
  logE("%s 2 test custom type queue", app);
  logE("%s 3 test custom extension type queue", app);
}

int main(int argc, char *argv[])
{
  if(argc != 2){
    user_help( argv[0] );
    return 0;
  }
  switch( atoi(argv[1]) ){
  case 0:
    return main_int();
  case 1:
    return main_str();
  case 2:
    return main_user();
  case 3:
    return main_user_ex();
  default:
    user_help( argv[0] );
    return 0;
  }
}

说明

通过输入不同的参数可以执行不同类型的队列测试

注意

当队列不使用时一定要执行与初始化想匹配的去初始化,否则会导致内存溢出。文中注释部分有一种内存溢出查找的代码。
具体使用方法或者其他调试方法可以参见https://blog.youkuaiyun.com/cooper1024/article/details/124177568?spm=1001.2014.3001.5501

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值