背景
一般高级语言中都有直接可以使用的数据结构。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
本文介绍了一种通过宏定义实现的C语言通用队列,适用于多种数据类型,包括整型、指针类型和字符串。队列在满时会丢弃最早的数据,提供了初始化、去初始化、读写操作以及内存管理。代码示例展示了如何测试不同类型的队列,强调了使用后必须正确去初始化以避免内存泄漏。
994

被折叠的 条评论
为什么被折叠?



