4_08_GLib库入门与实践_双端队列

简介

在Glib中,GQueue双端队列是由GList实现的一个队列结构,由于两端都可以push和pop,当从队列尾push从头pop时可看作队列,当从头push头pop时可看做栈。

数据结构

GQueue的数据结构

struct GQueue {
  GList *head;
  GList *tail;
  guint  length;
};

函数列表

GQueue *	g_queue_new ()
void	g_queue_free ()
void	g_queue_free_full ()
void	g_queue_init ()
void	g_queue_clear ()
gboolean	g_queue_is_empty ()
guint	g_queue_get_length ()
void	g_queue_reverse ()
GQueue *	g_queue_copy ()
void	g_queue_foreach ()
GList *	g_queue_find ()
GList *	g_queue_find_custom ()
void	g_queue_sort ()
void	g_queue_push_head ()
void	g_queue_push_tail ()
void	g_queue_push_nth ()
gpointer	g_queue_pop_head ()
gpointer	g_queue_pop_tail ()
gpointer	g_queue_pop_nth ()
gpointer	g_queue_peek_head ()
gpointer	g_queue_peek_tail ()
gpointer	g_queue_peek_nth ()
gint	g_queue_index ()
gboolean	g_queue_remove ()
guint	g_queue_remove_all ()
void	g_queue_insert_before ()
void	g_queue_insert_after ()
void	g_queue_insert_sorted ()
void	g_queue_push_head_link ()
void	g_queue_push_tail_link ()
void	g_queue_push_nth_link ()
GList *	g_queue_pop_head_link ()
GList *	g_queue_pop_tail_link ()
GList *	g_queue_pop_nth_link ()
GList *	g_queue_peek_head_link ()
GList *	g_queue_peek_tail_link ()
GList *	g_queue_peek_nth_link ()
gint	g_queue_link_index ()
void	g_queue_unlink ()
void	g_queue_delete_link ()

函数功能分类

创建

GQueue *	g_queue_new ()

静态初始化

g_queue_init ();
G_QUEUE_INIT;

测长

gboolean	g_queue_is_empty ()
guint	g_queue_get_length ()

遍历

void	g_queue_foreach ()

释放

void	g_queue_free ()
void	g_queue_free_full ()

入队列

void	g_queue_push_head ()
void	g_queue_push_tail ()
void	g_queue_push_nth ()
void	g_queue_push_head_link ()
void	g_queue_push_tail_link ()
void	g_queue_push_nth_link ()

查看

gpointer	g_queue_peek_head ()
gpointer	g_queue_peek_tail ()
gpointer	g_queue_peek_nth ()
GList *	g_queue_peek_head_link ()
GList *	g_queue_peek_tail_link ()
GList *	g_queue_peek_nth_link ()

出队列

gpointer	g_queue_pop_head ()
gpointer	g_queue_pop_tail ()
gpointer	g_queue_pop_nth ()
GList *	g_queue_pop_head_link ()
GList *	g_queue_pop_tail_link ()
GList *	g_queue_pop_nth_link ()

查找

gint	g_queue_index ()
gint	g_queue_link_index ()
GList *	g_queue_find ()
GList *	g_queue_find_custom ()

插入

void	g_queue_insert_before ()
void	g_queue_insert_after ()

删除

gboolean	g_queue_remove ()
guint	g_queue_remove_all ()
void	g_queue_unlink ()
void	g_queue_delete_link ()

清空

void	g_queue_clear ()

逆序

void	g_queue_reverse ()

排序

void	g_queue_sort ()
void	g_queue_insert_sorted ()

拷贝

GQueue *	g_queue_copy ()

函数功能说明及综合演示

创建一个队列

使用g_queue_new可创建一个队列,队列创建完成后,得到一个GQueue类型的指针变量。

GQueue *	g_queue_new ()
静态初始化

队列的静态初始化有两种方式,g_queue_initG_QUEUE_INIT

GQueue q1;
g_queue_init (&q1);

或者

GQueue q2 = G_QUEUE_INIT;
测长

g_queue_is_empty返回队列是否为空,g_queue_get_length返回队列中节点个数。

gboolean	g_queue_is_empty ()
guint	g_queue_get_length ()
遍历

遍历队列中的所有节点
func参数:回调函数,调用者自己实现,对节点如何遍历,由此回调函数实现。对每个节点的遍历都会调用此函数。
user_data为回调函数的传入参数。

void
g_queue_foreach (GQueue *queue,
                 GFunc func,
                 gpointer user_data);
释放

若不需要自己释放队列中的节点,可以调用g_queue_free,如果需要单独释放的节点,需要调用g_queue_free_full,自行实现释放节点。

void	g_queue_free ()
void	g_queue_free_full ()

针对以上介绍的函数,示例如下:
源码见glib_examples\glib_queue\glib_queue_basic

#include <glib.h>

static void _queue_foreach_func(gpointer data, gpointer user_data)
{
    g_print("user_data:%s, data:%d \n", (gchar *)user_data, GPOINTER_TO_INT(data));
}

gint main(gint argc, gchar **argv)
{
    GQueue *q = NULL;
    gboolean empty = TRUE;
    guint len = 0;

    q = g_queue_new();

    empty = g_queue_is_empty(q);
    len = g_queue_get_length(q);
    g_print("empty=%s, len=%d \n", empty ? "TRUE":"FALSE", len);

    g_queue_push_head(q, GINT_TO_POINTER(1));
    g_queue_push_head(q, GINT_TO_POINTER(3));

    empty = g_queue_is_empty(q);
    len = g_queue_get_length(q);
    g_print("empty=%s, len=%d \n", empty ? "TRUE":"FALSE", len);

    g_queue_foreach(q, _queue_foreach_func, "Queue");

    g_queue_free(q);

    return 0;
}

运行结果:

empty=TRUE, len=0 
empty=FALSE, len=2 
user_data:Queue, data:3 
user_data:Queue, data:1

这里使用了一个g_queue_push_head的函数,这是入队列函数中的一个,下面具体介绍。

入队列

入队列相关的操作函数如下:

void	g_queue_push_head ()
void	g_queue_push_tail ()
void	g_queue_push_nth ()
void	g_queue_push_head_link ()
void	g_queue_push_tail_link ()
void	g_queue_push_nth_link ()

函数功能介绍:

//将节点入队列。
void
g_queue_push_head (GQueue *queue,
                   gpointer data);

//将节点入队列,与g_queue_push_head相似。g_queue_push_head是将数据入队列,g_queue_push_head_link是将节点入队列。
//第二个参数是只有一个元素的链表,也可以称作是个链表节点。
//g_queue_push_tail和g_queue_push_head相似,将数据插入队尾。
void
g_queue_push_head_link (GQueue *queue,
                        GList *link_);

//g_queue_push_nth,将数据插入指定位置。
void
g_queue_push_nth (GQueue *queue,
                  gpointer data,
                  gint n);

入队列函数操作示例:
源码见glib_examples\glib_queue\glib_queue_push_err

#include <glib.h>

static void _queue_foreach_func(gpointer data, gpointer       user_data)
{
    g_print("user_data:%s, data:%d \n", (gchar *)user_data, GPOINTER_TO_INT(data));
}

gint main(gint argc, gchar **argv)
{
    GQueue *q = NULL;
    gboolean empty = TRUE;
    guint len = 0;
    GList *node[3] = {NULL, NULL, NULL};

    q = g_queue_new();

    empty = g_queue_is_empty(q);
    len = g_queue_get_length(q);
    g_print("empty=%s, len=%d \n", empty ? "TRUE":"FALSE", len);

    g_queue_push_head(q, GINT_TO_POINTER(1));
    g_queue_push_tail(q, GINT_TO_POINTER(3));
    g_queue_push_nth(q, GINT_TO_POINTER(2), 1);
    // result: 1-2-3

    node[0] = g_new0(GList, 1);
    node[0]->data = GINT_TO_POINTER(7);
    node[1] = g_new0(GList, 1);
    node[1]->data = GINT_TO_POINTER(8);
    node[2] = g_new0(GList, 1);
    node[2]->data = GINT_TO_POINTER(9);

    g_queue_push_head_link(q, node[0]);
    g_queue_push_tail_link(q, node[1]);
    g_queue_push_nth_link(q, 1, node[2]);
    // result: 7-9-1-2-3-8

    g_queue_foreach(q, _queue_foreach_func, "Queue");
    
    empty = g_queue_is_empty(q);
    len = g_queue_get_length(q);
    g_print("empty=%s, len=%d \n", empty ? "TRUE":"FALSE", len);

    g_list_free(node[0]);
    g_list_free(node[1]);
    g_list_free(node[2]);
    g_queue_free(q);

    return 0;
}

运行结果:

empty=TRUE, len=0 
user_data:Queue, data:7 
user_data:Queue, data:9 
user_data:Queue, data:1 
user_data:Queue, data:2 
user_data:Queue, data:3 
user_data:Queue, data:8 
empty=FALSE, len=6

运行结果看上去没有什么问题,使用valgrind检查一下,就会发现有内存释放异常问题。

[root@centos7_6 build]# valgrind --tool=memcheck --leak-check=full ./glib_queue_push_err
==7921== Memcheck, a memory error detector
==7921== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
==7921== Using Valgrind-3.10.1 and LibVEX; rerun with -h for copyright info
==7921== Command: ./glib_queue_push_err
==7921== 
empty=TRUE, len=0 
user_data:Queue, data:7 
user_data:Queue, data:9 
user_data:Queue, data:1 
user_data:Queue, data:2 
user_data:Queue, data:3 
user_data:Queue, data:8 
empty=FALSE, len=6 
==7921== Invalid read of size 8
==7921==    at 0x4E9BD9D: g_slice_free_chain_with_offset (in /lib/x86_64-linux-gnu/libglib-2.0.so.0.4002.0)
==7921==    by 0x400DD5: main (glib_queue_push_err.c:45)
==7921==  Address 0x596a338 is 8 bytes inside a block of size 24 free'd
==7921==    at 0x4C2BDEC: free (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==7921==    by 0x4E9BD8A: g_slice_free_chain_with_offset (in /lib/x86_64-linux-gnu/libglib-2.0.so.0.4002.0)
==7921==    by 0x400DC9: main (glib_queue_push_err.c:44)
==7921== 
==7921== Invalid free() / delete / delete[] / realloc()
==7921==    at 0x4C2BDEC: free (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==7921==    by 0x4E9BD8A: g_slice_free_chain_with_offset (in /lib/x86_64-linux-gnu/libglib-2.0.so.0.4002.0)
==7921==    by 0x400DD5: main (glib_queue_push_err.c:45)
==7921==  Address 0x596a330 is 0 bytes inside a block of size 24 free'd
==7921==    at 0x4C2BDEC: free (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==7921==    by 0x4E9BD8A: g_slice_free_chain_with_offset (in /lib/x86_64-linux-gnu/libglib-2.0.so.0.4002.0)
==7921==    by 0x400DC9: main (glib_queue_push_err.c:44)
==7921== 
==7921== Invalid read of size 8
==7921==    at 0x4E9BD9D: g_slice_free_chain_with_offset (in /lib/x86_64-linux-gnu/libglib-2.0.so.0.4002.0)
==7921==    by 0x400DE1: main (glib_queue_push_err.c:46)
==7921==  Address 0x596a398 is 8 bytes inside a block of size 24 free'd
==7921==    at 0x4C2BDEC: free (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==7921==    by 0x4E9BD8A: g_slice_free_chain_with_offset (in /lib/x86_64-linux-gnu/libglib-2.0.so.0.4002.0)
==7921==    by 0x400DC9: main (glib_queue_push_err.c:44)
==7921== 
==7921== Invalid free() / delete / delete[] / realloc()
==7921==    at 0x4C2BDEC: free (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==7921==    by 0x4E9BD8A: g_slice_free_chain_with_offset (in /lib/x86_64-linux-gnu/libglib-2.0.so.0.4002.0)
==7921==    by 0x400DE1: main (glib_queue_push_err.c:46)
==7921==  Address 0x596a390 is 0 bytes inside a block of size 24 free'd
==7921==    at 0x4C2BDEC: free (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==7921==    by 0x4E9BD8A: g_slice_free_chain_with_offset (in /lib/x86_64-linux-gnu/libglib-2.0.so.0.4002.0)
==7921==    by 0x400DC9: main (glib_queue_push_err.c:44)
==7921== 
==7921== Invalid read of size 8
==7921==    at 0x4E9BD9D: g_slice_free_chain_with_offset (in /lib/x86_64-linux-gnu/libglib-2.0.so.0.4002.0)
==7921==    by 0x4E8FA20: g_queue_free (in /lib/x86_64-linux-gnu/libglib-2.0.so.0.4002.0)
==7921==    by 0x400DED: main (glib_queue_push_err.c:47)
==7921==  Address 0x596a2d8 is 8 bytes inside a block of size 24 free'd
==7921==    at 0x4C2BDEC: free (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==7921==    by 0x4E9BD8A: g_slice_free_chain_with_offset (in /lib/x86_64-linux-gnu/libglib-2.0.so.0.4002.0)
==7921==    by 0x400DC9: main (glib_queue_push_err.c:44)
==7921== 
==7921== Invalid free() / delete / delete[] / realloc()
==7921==    at 0x4C2BDEC: free (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==7921==    by 0x4E9BD8A: g_slice_free_chain_with_offset (in /lib/x86_64-linux-gnu/libglib-2.0.so.0.4002.0)
==7921==    by 0x4E8FA20: g_queue_free (in /lib/x86_64-linux-gnu/libglib-2.0.so.0.4002.0)
==7921==    by 0x400DED: main (glib_queue_push_err.c:47)
==7921==  Address 0x596a2d0 is 0 bytes inside a block of size 24 free'd
==7921==    at 0x4C2BDEC: free (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==7921==    by 0x4E9BD8A: g_slice_free_chain_with_offset (in /lib/x86_64-linux-gnu/libglib-2.0.so.0.4002.0)
==7921==    by 0x400DC9: main (glib_queue_push_err.c:44)
==7921== 
==7921== 
==7921== HEAP SUMMARY:
==7921==     in use at exit: 2,214 bytes in 9 blocks
==7921==   total heap usage: 66 allocs, 69 frees, 268,016 bytes allocated
==7921== 
==7921== LEAK SUMMARY:
==7921==    definitely lost: 0 bytes in 0 blocks
==7921==    indirectly lost: 0 bytes in 0 blocks
==7921==      possibly lost: 0 bytes in 0 blocks
==7921==    still reachable: 2,214 bytes in 9 blocks
==7921==         suppressed: 0 bytes in 0 blocks
==7921== Reachable blocks (those to which a pointer was found) are not shown.
==7921== To see them, rerun with: --leak-check=full --show-leak-kinds=all
==7921== 
==7921== For counts of detected and suppressed errors, rerun with: -v
==7921== ERROR SUMMARY: 24 errors from 6 contexts (suppressed: 0 from 0)

疑问:队列释放时,是否需要手动释放g_queue_push_xxx_link入队列的几个节点?
g_queue_push_head_link入队列的节点,通过g_new0申请,申请完后得到只包含一个节点的链表。这个链表中仅有的节点,prev和next均为空,data被赋予了一个整数,整数不需要单独释放内存,因此也无法对此链表进行插入删除等操作,此链表在入队列时视为一个节点,查看GQueue的g_queue_push_head_link源码实现,可以发现其将链表节点插入队列时,节点的next和prev指针指向前后链表节点(如果有的话),并更新GQueue的head值,此后,链表节点被队列管理。查看GQueue的g_queue_free源码实现,发现其会释放GQueue内的所有节点。
可见,本例中,通过g_new0申请的链表节点不需要手动释放,上述代码修改为如下形式可解决内存泄露问题:
源码见glib_examples\glib_queue\glib_queue_push

#include <glib.h>

static void _queue_foreach_func(gpointer data, gpointer       user_data)
{
    g_print("user_data:%s, data:%d \n", (gchar *)user_data, GPOINTER_TO_INT(data));
}

gint main(gint argc, gchar **argv)
{
    GQueue *q = NULL;
    gboolean empty = TRUE;
    guint len = 0;
    GList *node[3] = {NULL, NULL, NULL};

    q = g_queue_new();

    empty = g_queue_is_empty(q);
    len = g_queue_get_length(q);
    g_print("empty=%s, len=%d \n", empty ? "TRUE":"FALSE", len);

    g_queue_push_head(q, GINT_TO_POINTER(1));
    g_queue_push_tail(q, GINT_TO_POINTER(3));
    g_queue_push_nth(q, GINT_TO_POINTER(2), 1);
    // result: 1-2-3

    node[0] = g_new0(GList, 1);
    node[0]->data = GINT_TO_POINTER(7);
    node[1] = g_new0(GList, 1);
    node[1]->data = GINT_TO_POINTER(8);
    node[2] = g_new0(GList, 1);
    node[2]->data = GINT_TO_POINTER(9);

    g_queue_push_head_link(q, node[0]);
    g_queue_push_tail_link(q, node[1]);
    g_queue_push_nth_link(q, 1, node[2]);
    // result: 7-9-1-2-3-8

    g_queue_foreach(q, _queue_foreach_func, "Queue");
    
    empty = g_queue_is_empty(q);
    len = g_queue_get_length(q);
    g_print("empty=%s, len=%d \n", empty ? "TRUE":"FALSE", len);

    //g_list_free(node[0]);
    //g_list_free(node[1]);
    //g_list_free(node[2]);
    g_queue_free(q);

    return 0;
}

此时再使用valgrind工具,内存异常释放问题得以解决。

查看

队列查看的函数有以下几个。

gpointer	g_queue_peek_head ()
gpointer	g_queue_peek_tail ()
gpointer	g_queue_peek_nth ()
GList *	g_queue_peek_head_link ()
GList *	g_queue_peek_tail_link ()
GList *	g_queue_peek_nth_link ()

g_queue_peek_xxx这几个函数,查看队列中的元素,返回的是链表节点的数值。
g_queue_peek_xxx_link这几个函数,查看队列中的元素,返回的是链表节点。
peek函数不会影响队列本身,也不需要释放peek返回的数值或节点,因为这些返回值仅仅是指针,实际的内存仍然在队列中。
队列查看函数示例:
源码见glib_examples\glib_queue\glib_queue_peek

#include <glib.h>

static void _queue_foreach_func(gpointer data, gpointer       user_data)
{
    g_print("user_data:%s, data:%d \n", (gchar *)user_data, GPOINTER_TO_INT(data));
}

gint main(gint argc, gchar **argv)
{
    GQueue *q = NULL;
    gint ret1 = 0, ret2 = 0, ret3 = 0;
    GList *l1 = NULL, *l2 = NULL, *l3 = NULL;

    q = g_queue_new();

    g_queue_push_tail(q, GINT_TO_POINTER(1));
    g_queue_push_tail(q, GINT_TO_POINTER(3));
    g_queue_push_tail(q, GINT_TO_POINTER(5));
    g_queue_push_tail(q, GINT_TO_POINTER(7));
    g_queue_push_tail(q, GINT_TO_POINTER(9));
    // result: 1-3-5-7-9

    g_queue_foreach(q, _queue_foreach_func, "Queue-push");

    // peek
    ret1 = GPOINTER_TO_INT(g_queue_peek_head(q));
    ret2 = GPOINTER_TO_INT(g_queue_peek_tail(q));
    ret3 = GPOINTER_TO_INT(g_queue_peek_nth(q, 2));
    l1 = g_queue_peek_head_link(q);
    l2 = g_queue_peek_tail_link(q);
    l3 = g_queue_peek_nth_link(q, 2);

    g_print("ret1:%d, ret2:%d, ret3:%d. l1->data:%d, l2->data:%d, l3->data:%d \n",
        ret1, ret2, ret3, GPOINTER_TO_INT(l1->data), GPOINTER_TO_INT(l2->data), 
        GPOINTER_TO_INT(l3->data));

    g_queue_foreach(q, _queue_foreach_func, "Queue-after-peek");
    
    g_queue_free(q);

    return 0;
}

运行结果:

user_data:Queue-push, data:1 
user_data:Queue-push, data:3 
user_data:Queue-push, data:5 
user_data:Queue-push, data:7 
user_data:Queue-push, data:9 
ret1:1, ret2:9, ret3:5. l1->data:1, l2->data:9, l3->data:5 
user_data:Queue-after-peek, data:1 
user_data:Queue-after-peek, data:3 
user_data:Queue-after-peek, data:5 
user_data:Queue-after-peek, data:7 
user_data:Queue-after-peek, data:9
出队列

出队列函数有如下几个。

gpointer	g_queue_pop_head ()
gpointer	g_queue_pop_tail ()
gpointer	g_queue_pop_nth ()
GList *	g_queue_pop_head_link ()
GList *	g_queue_pop_tail_link ()
GList *	g_queue_pop_nth_link ()

出队列对队列本身有影响,每次出队列队列的节点都会减少一个。
下面是出队列的函数功能演示:
源码见glib_examples\glib_queue\glib_queue_pop_err

#include <glib.h>

static void _queue_foreach_func(gpointer data, gpointer       user_data)
{
    g_print("user_data:%s, data:%d \n", (gchar *)user_data, GPOINTER_TO_INT(data));
}

gint main(gint argc, gchar **argv)
{
    GQueue *q = NULL;
    gint ret1 = 0, ret2 = 0, ret3 = 0;
    GList *l1 = NULL, *l2 = NULL, *l3 = NULL;

    q = g_queue_new();

    g_queue_push_tail(q, GINT_TO_POINTER(1));
    g_queue_push_tail(q, GINT_TO_POINTER(2));
    g_queue_push_tail(q, GINT_TO_POINTER(3));
    g_queue_push_tail(q, GINT_TO_POINTER(4));
    g_queue_push_tail(q, GINT_TO_POINTER(5));
    g_queue_push_tail(q, GINT_TO_POINTER(6));
    g_queue_push_tail(q, GINT_TO_POINTER(7));
    // result: 1-2-3-4-5-6-7

    g_queue_foreach(q, _queue_foreach_func, "Queue-push");

    // pop
    ret1 = GPOINTER_TO_INT(g_queue_pop_head(q));
    ret2 = GPOINTER_TO_INT(g_queue_pop_tail(q));
    ret3 = GPOINTER_TO_INT(g_queue_pop_nth(q, 2));
    // result: 2-3-5-6
    l1 = g_queue_pop_head_link(q);
    l2 = g_queue_pop_tail_link(q);
    l3 = g_queue_pop_nth_link(q, 1);
    // result: 3

    g_print("ret1:%d, ret2:%d, ret3:%d. l1->data:%d, l2->data:%d, l3->data:%d \n",
        ret1, ret2, ret3, GPOINTER_TO_INT(l1->data), GPOINTER_TO_INT(l2->data), 
        GPOINTER_TO_INT(l3->data));

    g_queue_foreach(q, _queue_foreach_func, "Queue-after-pop");

    g_queue_free(q);

    return 0;
}

运行结果:

user_data:Queue-push, data:1 
user_data:Queue-push, data:2 
user_data:Queue-push, data:3 
user_data:Queue-push, data:4 
user_data:Queue-push, data:5 
user_data:Queue-push, data:6 
user_data:Queue-push, data:7 
ret1:1, ret2:7, ret3:4. l1->data:2, l2->data:6, l3->data:5 
user_data:Queue-after-pop, data:3

使用valgrind工具可以看到有内存泄露。

[root@centos7_6 build]# valgrind --tool=memcheck --leak-check=full ./glib_queue_pop_err
==7880== Memcheck, a memory error detector
==7880== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
==7880== Using Valgrind-3.10.1 and LibVEX; rerun with -h for copyright info
==7880== Command: ./glib_queue_pop_err
==7880== 
user_data:Queue-push, data:1 
user_data:Queue-push, data:2 
user_data:Queue-push, data:3 
user_data:Queue-push, data:4 
user_data:Queue-push, data:5 
user_data:Queue-push, data:6 
user_data:Queue-push, data:7 
ret1:1, ret2:7, ret3:4. l1->data:2, l2->data:6, l3->data:5 
user_data:Queue-after-pop, data:3 
==7880== 
==7880== HEAP SUMMARY:
==7880==     in use at exit: 2,286 bytes in 12 blocks
==7880==   total heap usage: 73 allocs, 61 frees, 301,353 bytes allocated
==7880== 
==7880== 24 bytes in 1 blocks are definitely lost in loss record 6 of 12
==7880==    at 0x4C2AB80: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==7880==    by 0x4E856D0: g_malloc (in /lib/x86_64-linux-gnu/libglib-2.0.so.0.4002.0)
==7880==    by 0x4E9B2ED: g_slice_alloc (in /lib/x86_64-linux-gnu/libglib-2.0.so.0.4002.0)
==7880==    by 0x4E7C503: g_list_append (in /lib/x86_64-linux-gnu/libglib-2.0.so.0.4002.0)
==7880==    by 0x4E8FE81: g_queue_push_tail (in /lib/x86_64-linux-gnu/libglib-2.0.so.0.4002.0)
==7880==    by 0x400B89: main (glib_queue_pop_err.c:17)
==7880== 
==7880== 24 bytes in 1 blocks are definitely lost in loss record 7 of 12
==7880==    at 0x4C2AB80: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==7880==    by 0x4E856D0: g_malloc (in /lib/x86_64-linux-gnu/libglib-2.0.so.0.4002.0)
==7880==    by 0x4E9B2ED: g_slice_alloc (in /lib/x86_64-linux-gnu/libglib-2.0.so.0.4002.0)
==7880==    by 0x4E7C503: g_list_append (in /lib/x86_64-linux-gnu/libglib-2.0.so.0.4002.0)
==7880==    by 0x4E8FE81: g_queue_push_tail (in /lib/x86_64-linux-gnu/libglib-2.0.so.0.4002.0)
==7880==    by 0x400BBC: main (glib_queue_pop_err.c:20)
==7880== 
==7880== 24 bytes in 1 blocks are definitely lost in loss record 8 of 12
==7880==    at 0x4C2AB80: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==7880==    by 0x4E856D0: g_malloc (in /lib/x86_64-linux-gnu/libglib-2.0.so.0.4002.0)
==7880==    by 0x4E9B2ED: g_slice_alloc (in /lib/x86_64-linux-gnu/libglib-2.0.so.0.4002.0)
==7880==    by 0x4E7C503: g_list_append (in /lib/x86_64-linux-gnu/libglib-2.0.so.0.4002.0)
==7880==    by 0x4E8FE81: g_queue_push_tail (in /lib/x86_64-linux-gnu/libglib-2.0.so.0.4002.0)
==7880==    by 0x400BCD: main (glib_queue_pop_err.c:21)
==7880== 
==7880== LEAK SUMMARY:
==7880==    definitely lost: 72 bytes in 3 blocks
==7880==    indirectly lost: 0 bytes in 0 blocks
==7880==      possibly lost: 0 bytes in 0 blocks
==7880==    still reachable: 2,214 bytes in 9 blocks
==7880==         suppressed: 0 bytes in 0 blocks
==7880== Reachable blocks (those to which a pointer was found) are not shown.
==7880== To see them, rerun with: --leak-check=full --show-leak-kinds=all
==7880== 
==7880== For counts of detected and suppressed errors, rerun with: -v
==7880== ERROR SUMMARY: 3 errors from 3 contexts (suppressed: 0 from 0)

原因是g_queue_pop_xxx和g_queue_pop_xxx_link函数会将节点从队列中移除,如果不释放被移除的节点,就会造成内存泄露。
上述内存泄露的修改方式为:
源码见glib_examples\glib_queue\glib_queue_pop

#include <glib.h>

static void _queue_foreach_func(gpointer data, gpointer       user_data)
{
    g_print("user_data:%s, data:%d \n", (gchar *)user_data, GPOINTER_TO_INT(data));
}

gint main(gint argc, gchar **argv)
{
    GQueue *q = NULL;
    gint ret1 = 0, ret2 = 0, ret3 = 0;
    GList *l1 = NULL, *l2 = NULL, *l3 = NULL;

    q = g_queue_new();

    g_queue_push_tail(q, GINT_TO_POINTER(1));
    g_queue_push_tail(q, GINT_TO_POINTER(2));
    g_queue_push_tail(q, GINT_TO_POINTER(3));
    g_queue_push_tail(q, GINT_TO_POINTER(4));
    g_queue_push_tail(q, GINT_TO_POINTER(5));
    g_queue_push_tail(q, GINT_TO_POINTER(6));
    g_queue_push_tail(q, GINT_TO_POINTER(7));
    // result: 1-2-3-4-5-6-7

    g_queue_foreach(q, _queue_foreach_func, "Queue-push");

    // pop
    ret1 = GPOINTER_TO_INT(g_queue_pop_head(q));
    ret2 = GPOINTER_TO_INT(g_queue_pop_tail(q));
    ret3 = GPOINTER_TO_INT(g_queue_pop_nth(q, 2));
    // result: 2-3-5-6
    l1 = g_queue_pop_head_link(q);
    l2 = g_queue_pop_tail_link(q);
    l3 = g_queue_pop_nth_link(q, 1);
    // result: 3

    g_print("ret1:%d, ret2:%d, ret3:%d. l1->data:%d, l2->data:%d, l3->data:%d \n",
        ret1, ret2, ret3, GPOINTER_TO_INT(l1->data), GPOINTER_TO_INT(l2->data), 
        GPOINTER_TO_INT(l3->data));

    g_queue_foreach(q, _queue_foreach_func, "Queue-after-pop");

    g_list_free(l1);
    g_list_free(l2);
    g_list_free(l3);
   
    g_queue_free(q);

    return 0;
}

再次编译,使用valgrind检查,发现已经无内存泄露。

查找

队列的查找与链表的查找方式非常相似,前两个函数是通过节点的值查找节点在队列中的位置并返回节点的下标,后两个函数是通过节点的值查找节点在队列中的位置并返回节点的指针。

gint	g_queue_index ()
gint	g_queue_link_index ()
GList *	g_queue_find ()
GList *	g_queue_find_custom ()

函数功能说明:

//找到data数据在队列中的节点的下标。
gint
g_queue_index (GQueue *queue,
               gconstpointer data);

//通过一个链表节点找到与此节点相同的节点在队列中的下标。
gint
g_queue_link_index (GQueue *queue,
                    GList *link_);

//找到data数据在队列中的节点,返回其指针地址,如果有多个相同的值,仅返回第一个。
GList *
g_queue_find (GQueue *queue,
              gconstpointer data);

//找到data数据在队列中的节点,查找函数用户自己实现,如果func返回值为0,视为找到,返回节点的指针地址。如果有多个相同的数值,仅能找到第一个。
GList *
g_queue_find_custom (GQueue *queue,
                     gconstpointer data,
                     GCompareFunc func);

下面例子展示上述查找函数的用法。
源码见glib_examples\glib_queue\glib_queue_find

#include <glib.h>

static void _queue_foreach_func(gpointer data, gpointer       user_data)
{
    g_print("user_data:%s, data:%d \n", (gchar *)user_data, GPOINTER_TO_INT(data));
}

static gint _queue_find_cmp_func(gconstpointer a, gconstpointer b)
{
    gint x, y;

    x = GPOINTER_TO_INT(a);
    y = GPOINTER_TO_INT(b);

    return (x > y ? +1 : x == y ? 0 : -1);
}

gint main(gint argc, gchar **argv)
{
    GQueue *q = NULL;
    gint index1 = 0, index2 = 0;
    GList *l = NULL;
    GList *l1 = NULL, *l2 = NULL;

    q = g_queue_new();

    g_queue_push_tail(q, GINT_TO_POINTER(10));
    g_queue_push_tail(q, GINT_TO_POINTER(20));
    g_queue_push_tail(q, GINT_TO_POINTER(30));
    g_queue_push_tail(q, GINT_TO_POINTER(40));
    // result: 10-20-30-40

    g_queue_foreach(q, _queue_foreach_func, "Queue-push");

    // find pos
    index1 = g_queue_index(q, GINT_TO_POINTER(40));

    l = g_new0(GList, 1);
    l->data = GINT_TO_POINTER(30);
    index2 = g_queue_link_index(q, l);
    g_print("index1:%d, index2:%d \n", index1, index2);

    // find GList
    l1 = g_queue_find(q, GINT_TO_POINTER(20));
    if(NULL != l1) {
        g_print("l1->data:%d \n", GPOINTER_TO_INT(l1->data));
    } else {
        g_print("g_queue_find not found \n");
    }
    l2 = g_queue_find_custom(q, GINT_TO_POINTER(10), _queue_find_cmp_func);
    if(NULL != l2) {
        g_print("l2->data:%d \n", GPOINTER_TO_INT(l2->data));
    } else {
        g_print("g_queue_find_custom not found \n");
    }

    g_queue_foreach(q, _queue_foreach_func, "Queue-after-find");

    g_list_free(l);
    g_queue_free(q);

    return 0;
}

运行结果:

user_data:Queue-push, data:10 
user_data:Queue-push, data:20 
user_data:Queue-push, data:30 
user_data:Queue-push, data:40 
index1:3, index2:-1 
l1->data:20 
l2->data:10 
user_data:Queue-after-find, data:10 
user_data:Queue-after-find, data:20 
user_data:Queue-after-find, data:30 
user_data:Queue-after-find, data:40

首先可以看到的是查找不会影响队列本身。
构造一个链表节点,通过g_queue_link_index并不能查找到节点在队列中的位置,这是为什么呢?我们通过g_queue_link_index函数的说明可以得知,其第二个参数link_必须为队列中的一个节点。通过查看g_queue_link_index也可以发现,link_与GQueue中各链表节点是通过==运算符直接进行的地址比较,构造相同的值,无法找到。

插入

这两个插入函数很好理解,要注意的是,其第二个参数sibling,需要是queue中的一个节点,否则插入就会失败,如果此参数为空,则插入到队列头。

void	g_queue_insert_before ()
void	g_queue_insert_after ()

函数原型:

void
g_queue_insert_before (GQueue *queue,
                       GList *sibling,
                       gpointer data);
void
g_queue_insert_after (GQueue *queue,
                      GList *sibling,
                      gpointer data);

插入函数示例代码如下。
源码见glib_examples\glib_queue\glib_queue_insert

#include <glib.h>

static void _queue_foreach_func(gpointer data, gpointer       user_data)
{
    g_print("user_data:%s, data:%d \n", (gchar *)user_data, GPOINTER_TO_INT(data));
}

gint main(gint argc, gchar **argv)
{
    GQueue *q = NULL;
    GList *l = NULL;

    q = g_queue_new();

    g_queue_push_tail(q, GINT_TO_POINTER(10));
    g_queue_push_tail(q, GINT_TO_POINTER(20));
    g_queue_push_tail(q, GINT_TO_POINTER(30));
    // result: 10-20-30

    g_queue_foreach(q, _queue_foreach_func, "Queue-push");

    // find GList
    l = g_queue_find(q, GINT_TO_POINTER(20));

    g_queue_insert_before(q, l, GINT_TO_POINTER(15));
    g_queue_insert_after(q, l, GINT_TO_POINTER(25));
    // result: 10-15-20-25-30

    g_queue_foreach(q, _queue_foreach_func, "Queue-after-insert");

    g_queue_free(q);

    return 0;
}

运行结果:

user_data:Queue-push, data:10 
user_data:Queue-push, data:20 
user_data:Queue-push, data:30 
user_data:Queue-after-insert, data:10 
user_data:Queue-after-insert, data:15 
user_data:Queue-after-insert, data:20 
user_data:Queue-after-insert, data:25 
user_data:Queue-after-insert, data:30
删除

删除函数如下。

gboolean	g_queue_remove ()
guint	g_queue_remove_all ()
void	g_queue_unlink ()
void	g_queue_delete_link ()

函数功能说明:

//根据值移除队列中与此值相同的第一个节点,返回值如果为TRUE,则表示找到了与data相同值的节点并已经删除
gboolean
g_queue_remove (GQueue *queue,
                gconstpointer data);

//与g_queue_remove相似,不过是移除队列中与给定值相同的所有节点,返回值表示删除的总节点个数
guint
g_queue_remove_all (GQueue *queue,
                    gconstpointer data);

//移除队列中的一个节点,但不释放其内存
void
g_queue_unlink (GQueue *queue,
                GList *link_);

//移除队列中的一个节点,但释放其内存
void
g_queue_delete_link (GQueue *queue,
                     GList *link_);

删除功能示例代码如下:
源码见glib_examples\glib_queue\glib_queue_remove

#include <glib.h>

static void _queue_foreach_func(gpointer data, gpointer       user_data)
{
    g_print("user_data:%s, data:%d \n", (gchar *)user_data, GPOINTER_TO_INT(data));
}

gint main(gint argc, gchar **argv)
{
    GQueue *q = NULL;
    GList *l1 = NULL, *l2 = NULL;
    gboolean removed = FALSE;
    guint num = 0;

    q = g_queue_new();

    g_queue_push_tail(q, GINT_TO_POINTER(10));
    g_queue_push_tail(q, GINT_TO_POINTER(15));
    g_queue_push_tail(q, GINT_TO_POINTER(20));
    g_queue_push_tail(q, GINT_TO_POINTER(25));
    g_queue_push_tail(q, GINT_TO_POINTER(30));
    g_queue_push_tail(q, GINT_TO_POINTER(20));
    // result: 10-15-20-25-30-20

    g_queue_foreach(q, _queue_foreach_func, "Queue-push");

    // remove
    removed = g_queue_remove(q, GINT_TO_POINTER(30));
    num = g_queue_remove_all(q, GINT_TO_POINTER(20));
    g_print("removed-30:%s, num-20:%d \n", removed? "TRUE":"FALSE", num);
    // result: 10-15-25

    g_queue_foreach(q, _queue_foreach_func, "Queue-after-remove");

    // find GList and unlink
    l1 = g_queue_find(q, GINT_TO_POINTER(15));
    g_queue_unlink(q, l1);
    g_queue_foreach(q, _queue_foreach_func, "Queue-after-unlink");
    // result: 10-25

    // find GList and delete
    l2 = g_queue_find(q, GINT_TO_POINTER(25));
    g_queue_delete_link(q, l2);
    g_queue_foreach(q, _queue_foreach_func, "Queue-after-delete");
    // result: 10

    g_list_free(l1);
    g_queue_free(q);

    return 0;
}

运行结果:

user_data:Queue-push, data:10 
user_data:Queue-push, data:15 
user_data:Queue-push, data:20 
user_data:Queue-push, data:25 
user_data:Queue-push, data:30 
user_data:Queue-push, data:20 
removed-30:TRUE, num-20:2 
user_data:Queue-after-remove, data:10 
user_data:Queue-after-remove, data:15 
user_data:Queue-after-remove, data:25 
user_data:Queue-after-unlink, data:10 
user_data:Queue-after-unlink, data:25 
user_data:Queue-after-delete, data:10
清空

本函数只负责清除队列中的所有节点,如果节点有额外申请的内存空间,需要用户先手动释放。
本函数的一种应用场景是多个线程共享一个队列,要求队列不能被销毁,但其中的数据可能被清空的情况。
额外的内存空间如果要释放,有以下几种方法:
peek每一个节点,先将其额外的内存释放掉,再调用clear函数。
foreach遍历每个节点,释放额外的内存,再调用clear函数。

void	g_queue_clear ()

举例如下:
举例1:无需释放额外的内存空间
源码见glib_examples\glib_queue\glib_queue_free1

#include <glib.h>

static void _queue_foreach_func(gpointer data, gpointer       user_data)
{
    g_print("user_data:%s, data:%d \n", (gchar *)user_data, GPOINTER_TO_INT(data));
}

gint main(gint argc, gchar **argv)
{
    GQueue *q = NULL;
    gboolean empty = FALSE;

    q = g_queue_new();

    g_queue_push_tail(q, GINT_TO_POINTER(10));
    g_queue_push_tail(q, GINT_TO_POINTER(15));
    g_queue_push_tail(q, GINT_TO_POINTER(20));
    // result: 10-15-20

    g_queue_foreach(q, _queue_foreach_func, "Queue");

    // clear
    g_queue_clear(q);

    empty = g_queue_is_empty(q);
    g_print( "empty:%s \n", empty ? "TRUE" : "FALSE" );

    g_queue_free(q);

    return 0;
}

运行结果:

user_data:Queue, data:10 
user_data:Queue, data:15 
user_data:Queue, data:20 
empty:TRUE

举例2:peek_head先释放额外内存,再clear
源码见glib_examples\glib_queue\glib_queue_free2

#include <glib.h>

static void _queue_foreach_print_func(gpointer data, gpointer user_data)
{
    g_print("user_data:%s, data:%s \n", (gchar *)user_data, (gchar *)data);
}

gint main(gint argc, gchar **argv)
{
    GQueue *q = NULL;
    gboolean empty = FALSE;
    gchar *str[3] = {NULL, NULL, NULL};
    GList *list[3] = {NULL, NULL, NULL};
    guint i=0, len = 0;
    GList *l = NULL;

    str[0] = g_strdup("hello");
    str[1] = g_strdup("foo");
    str[2] = g_strdup("bar");

    q = g_queue_new();
    g_queue_push_tail(q, (gpointer)str[0]);
    g_queue_push_tail(q, (gpointer)str[1]);
    g_queue_push_tail(q, (gpointer)str[2]);

    g_queue_foreach(q, _queue_foreach_print_func, "before free");

    len = g_queue_get_length(q);
    for(i=0;i<len;i++) {
        list[i] = g_queue_peek_nth_link(q, i);
        g_print("peek %d: %s \n", i, (gchar *)list[i]->data);
    }

    for(i=0;i<len;i++) {
        g_queue_unlink(q, list[i]);
        if(NULL != list[i]->data) {
            g_print("free %s \n", (gchar *)list[i]->data);
            g_free((gchar *)list[i]->data);
        }
        g_list_free(list[i]);
    }
    g_list_free(l);

    empty = g_queue_is_empty(q);

    g_print( "empty:%s \n", empty ? "TRUE" : "FALSE" );
    
    g_queue_free(q);

    return 0;
}

举例3:foreach先释放额外内存,再clear
源码见glib_examples\glib_queue\glib_queue_free3

#include <glib.h>

static void _queue_foreach_print_func(gpointer data, gpointer user_data)
{
    GList *l = NULL;

    l = (GList *)data;
    if(NULL == l) {
        g_print("input param error! \n");
        return;
    }

    g_print("user_data:%s, data:%s \n", (gchar *)user_data, (gchar *)l->data);
}

static void _queue_foreach_free_func(gpointer data, gpointer user_data)
{
    GList *l = NULL;

    l = (GList *)data;
    if(NULL == l) {
        g_print("input param error! \n");
        return;
    }

    g_free((gchar *)l->data);
    g_list_free(l);
}

gint main(gint argc, gchar **argv)
{
    GQueue *q = NULL;
    gboolean empty = FALSE;
    GList *list[3] = {NULL, NULL, NULL};

    q = g_queue_new();

    list[0] = g_new0(GList, 1);
    list[0]->data = (gpointer)g_strdup("hello");
    list[1] = g_new0(GList, 1);
    list[1]->data = (gpointer)g_strdup("foo");
    list[2] = g_new0(GList, 1);
    list[2]->data = (gpointer)g_strdup("bar");

    g_queue_push_tail(q, (gpointer)list[0]);
    g_queue_push_tail(q, (gpointer)list[1]);
    g_queue_push_tail(q, (gpointer)list[2]);

    g_queue_foreach(q, _queue_foreach_print_func, "Queue-print");

    g_queue_foreach(q, _queue_foreach_free_func, "Queue-free");

    g_queue_clear(q);

    empty = g_queue_is_empty(q);

    g_print( "empty:%s \n", empty ? "TRUE" : "FALSE" );
    
    g_queue_free(q);

    return 0;
}

运行结果:

user_data:Queue-print, data:hello 
user_data:Queue-print, data:foo 
user_data:Queue-print, data:bar 
empty:TRUE
逆序

将一个队列逆序的函数如下:

void	g_queue_reverse ()

示例代码:
源码见glib_examples\glib_queue\glib_queue_reverse

#include <glib.h>
static void _queue_foreach_func(gpointer data, gpointer user_data)
{
    g_print("user_data:%s, data:%d \n", (gchar *)user_data, GPOINTER_TO_INT(data));
}

gint main(gint argc, gchar **argv)
{
    GQueue *q = NULL;

    q = g_queue_new();

    g_queue_push_tail(q, GINT_TO_POINTER(1));
    g_queue_push_tail(q, GINT_TO_POINTER(3));
    g_queue_push_tail(q, GINT_TO_POINTER(5));

    g_queue_foreach(q, _queue_foreach_func, "Queue-ori");

    //reverse
    g_queue_reverse(q);

    g_queue_foreach(q, _queue_foreach_func, "Queue-reverse");

    g_queue_free(q);

    return 0;
}

运行结果:

user_data:Queue-ori, data:1 
user_data:Queue-ori, data:3 
user_data:Queue-ori, data:5 
user_data:Queue-reverse, data:5 
user_data:Queue-reverse, data:3 
user_data:Queue-reverse, data:1
排序

排序函数下面两个:

void	g_queue_sort ()
void	g_queue_insert_sorted ()

详细说明:

//排序,排序函数用户自己指定,user_data为传给compare_func的参数。
void
g_queue_sort (GQueue *queue,
              GCompareDataFunc compare_func,
              gpointer user_data);

//在排好序的队列中插入一个数据data。如果queue本身是无序的,调用本函数并不能使新队列达到有序状态,新的数据data只会插入到比其节点值大的节点前面。
void
g_queue_insert_sorted (GQueue *queue,
                       gpointer data,
                       GCompareDataFunc func,
                       gpointer user_data);

排序示例1:
源码见glib_examples\glib_queue\glib_queue_sort1

#include <glib.h>
static void _queue_foreach_func(gpointer data, gpointer user_data)
{
    g_print("user_data:%s, data:%d \n", (gchar *)user_data, GPOINTER_TO_INT(data));
}

static gint _queue_sort_cmp_func(gconstpointer a, gconstpointer b, gpointer user_data)
{
    gint x, y;

    x = GPOINTER_TO_INT(a);
    y = GPOINTER_TO_INT(b);

    return (x > y ? +1 : x == y ? 0 : -1);
}

gint main(gint argc, gchar **argv)
{
    GQueue *q = NULL;

    q = g_queue_new();

    g_queue_push_tail(q, GINT_TO_POINTER(1));
    g_queue_push_tail(q, GINT_TO_POINTER(5));
    g_queue_push_tail(q, GINT_TO_POINTER(3));
    g_queue_push_tail(q, GINT_TO_POINTER(7));

    g_queue_foreach(q, _queue_foreach_func, "Queue-ori");

    // sort
    g_queue_sort(q, _queue_sort_cmp_func, NULL);

    g_queue_foreach(q, _queue_foreach_func, "Queue-sort");

    g_queue_free(q);

    return 0;
}

运行结果:

user_data:Queue-ori, data:1 
user_data:Queue-ori, data:5 
user_data:Queue-ori, data:3 
user_data:Queue-ori, data:7 
user_data:Queue-sort, data:1 
user_data:Queue-sort, data:3 
user_data:Queue-sort, data:5 
user_data:Queue-sort, data:7

排序示例2:
源码见glib_examples\glib_queue\glib_queue_sort2

#include <glib.h>
static void _queue_foreach_func(gpointer data, gpointer user_data)
{
    g_print("user_data:%s, data:%d \n", (gchar *)user_data, GPOINTER_TO_INT(data));
}

static gint _queue_sort_cmp_func(gconstpointer a, gconstpointer b, gpointer user_data)
{
    gint x, y;

    x = GPOINTER_TO_INT(a);
    y = GPOINTER_TO_INT(b);

    return (x > y ? +1 : x == y ? 0 : -1);
}

gint main(gint argc, gchar **argv)
{
    GQueue *q = NULL;

    q = g_queue_new();

    g_queue_push_tail(q, GINT_TO_POINTER(1));
    g_queue_push_tail(q, GINT_TO_POINTER(5));
    g_queue_push_tail(q, GINT_TO_POINTER(3));
    g_queue_push_tail(q, GINT_TO_POINTER(7));

    g_queue_foreach(q, _queue_foreach_func, "Queue-ori");

    // insert sorted
    g_queue_insert_sorted(q, GINT_TO_POINTER(6), _queue_sort_cmp_func, NULL);

    g_queue_foreach(q, _queue_foreach_func, "Queue-sort");

    g_queue_free(q);

    return 0;
}

运行结果:

user_data:Queue-ori, data:1 
user_data:Queue-ori, data:5 
user_data:Queue-ori, data:3 
user_data:Queue-ori, data:7 
user_data:Queue-sort, data:1 
user_data:Queue-sort, data:5 
user_data:Queue-sort, data:3 
user_data:Queue-sort, data:6 
user_data:Queue-sort, data:7

并没有得到想要的1-3-5-6-7,原因是原队列中的节点是无序的,插入时,只会找到比原队列节点值小的前面。

拷贝

GQueue * g_queue_copy ()
队列的浅拷贝。
可参考链表:浅拷贝,深拷贝。

专题

浅谈队列的内存模型

模型一:队列中的节点是一个整数
源码见glib_examples\glib_queue\glib_queue_mem_int

#include <glib.h>

static void _queue_foreach_func(gpointer data, gpointer       user_data)
{
    g_print("user_data:%s, data:%d \n", (gchar *)user_data, GPOINTER_TO_INT(data));
}

gint main(gint argc, gchar **argv)
{
    GQueue *q = NULL;

    q = g_queue_new();

    g_queue_push_tail(q, GINT_TO_POINTER(1));
    g_queue_push_tail(q, GINT_TO_POINTER(3));
    g_queue_push_tail(q, GINT_TO_POINTER(5));

    g_queue_foreach(q, _queue_foreach_func, "Queue");

    g_queue_free(q);

    return 0;
}

运行结果:

user_data:Queue, data:1 
user_data:Queue, data:3 
user_data:Queue, data:5

模型二:队列中的节点是一个链表节点,每一个链表节点的数据都是一个字符串
源码见glib_examples\glib_queue\glib_queue_mem_str

#include <glib.h>

static void _queue_foreach_print_func(gpointer data, gpointer       user_data)
{
    GList *l = NULL;

    l = (GList *)data;
    if(NULL == l) {
        g_print("input param error! \n");
        return;
    }

    g_print("user_data:%s, data:%s \n", (gchar *)user_data, (gchar *)l->data);
}

static void _queue_free_func(gpointer data)
{
    GList *l;

    l = (GList *)data;
    if(NULL == l) {
        g_print("input param error! \n");
        return;
    }

    g_free((gchar *)l->data);
    g_list_free(l);
}

gint main(gint argc, gchar **argv)
{
    GQueue *q = NULL;
    GList *list[3] = {NULL, NULL, NULL};

    q = g_queue_new();

    list[0] = g_new0(GList, 1);
    list[0]->data = (gpointer)g_strdup("hello");
    list[1] = g_new0(GList, 1);
    list[1]->data = (gpointer)g_strdup("foo");
    list[2] = g_new0(GList, 1);
    list[2]->data = (gpointer)g_strdup("bar");

    g_queue_push_tail(q, (gpointer)list[0]);
    g_queue_push_tail(q, (gpointer)list[1]);
    g_queue_push_tail(q, (gpointer)list[2]);

    g_queue_foreach(q, _queue_foreach_print_func, "Queue");

    g_queue_free_full(q, _queue_free_func);

    return 0;
}

运行结果:

user_data:Queue, data:hello 
user_data:Queue, data:foo 
user_data:Queue, data:bar

模型三:队列中的节点是一个链表节点,每一个链表节点的数据都是用户自定义数据
源码见glib_examples\glib_queue\glib_queue_mem_custom

#include <glib.h>

typedef struct employee_info_tag {
    gint id;
    gchar *name;
}employee_info_t;

static void _queue_foreach_print_func(gpointer data, gpointer       user_data)
{
    GList *l = NULL;

    l = (GList *)data;
    if(NULL == l) {
        g_print("input param error! \n");
        return;
    }

    g_print("user_data:%s, id:%d, name:%s \n", (gchar *)user_data, 
        ((employee_info_t *)l->data)->id, ((employee_info_t *)l->data)->name);
}

static void _queue_free_func(gpointer data)
{
    GList *l;
    employee_info_t *einfo = NULL;

    l = (GList *)data;
    if(NULL == l) {
        g_print("input param error! \n");
        return;
    }

    einfo = (employee_info_t *)l->data;
    if ( NULL != einfo ) {
        if(NULL != einfo->name) {
            g_free(einfo->name);
        }
        g_free(einfo);
    }

    g_list_free(l);
}

gint main(gint argc, gchar **argv)
{
    GQueue *q = NULL;
    GList *list[3] = {NULL, NULL, NULL};
    employee_info_t *einfo[3] = {NULL, NULL, NULL};

    einfo[0] = g_new0(employee_info_t, 1);
    einfo[1] = g_new0(employee_info_t, 1);
    einfo[2] = g_new0(employee_info_t, 1);

    einfo[0]->id = 1000;
    einfo[0]->name = g_strdup("Jack");
    einfo[1]->id = 1001;
    einfo[1]->name = g_strdup("Pony");
    einfo[2]->id = 1002;
    einfo[2]->name = g_strdup("Robin");

    list[0] = g_list_append(list[0], (gpointer)einfo[0]); // Jack
    list[1] = g_list_append(list[1], (gpointer)einfo[1]); // Pony
    list[2] = g_list_append(list[2], (gpointer)einfo[2]); // Robin

    q = g_queue_new();

    g_queue_push_tail(q, (gpointer)list[0]);
    g_queue_push_tail(q, (gpointer)list[1]);
    g_queue_push_tail(q, (gpointer)list[2]);

    g_queue_foreach(q, _queue_foreach_print_func, "Queue");

    g_queue_free_full(q, _queue_free_func);

    return 0;
}

运行结果:

user_data:Queue, id:1000, name:Jack 
user_data:Queue, id:1001, name:Pony 
user_data:Queue, id:1002, name:Robin
队列和栈

队列:排成一队
栈:一摞盘子

使用GQueue模拟栈。
如果使用g_queue_push_head/g_queue_pop_tail或g_queue_push_tail/g_queue_pop_head,就可以实现一个队列,这也是为什么GLib的队列叫做双端队列,两端都可以操作。
如果使用g_queue_push_head/g_queue_pop_head或g_queue_push_tail/g_queue_pop_tail,那是否可以实现栈的效果呢?
下面用两个例子给出答案。

队列
源码见glib_examples\glib_queue\glib_queue_queue

#include <glib.h>

static void _queue_foreach_func(gpointer data, gpointer       user_data)
{
    g_print("user_data:%s, data:%d \n", (gchar *)user_data, GPOINTER_TO_INT(data));
}

gint main(gint argc, gchar **argv)
{
    GQueue *q = NULL;
    gint ret1 = 0, ret2 = 0, ret3 = 0;

    q = g_queue_new();

    g_queue_push_tail(q, GINT_TO_POINTER(1));
    g_queue_push_tail(q, GINT_TO_POINTER(2));
    g_queue_push_tail(q, GINT_TO_POINTER(3));
    // result: 1-2-3

    g_queue_foreach(q, _queue_foreach_func, "Queue-push");

    // pop
    ret1 = GPOINTER_TO_INT(g_queue_pop_head(q));
    ret2 = GPOINTER_TO_INT(g_queue_pop_head(q));
    ret3 = GPOINTER_TO_INT(g_queue_pop_head(q));

    g_print("ret1:%d, ret2:%d, ret3:%d \n", ret1, ret2, ret3);
    g_queue_free(q);

    return 0;
}

运行结果:

user_data:Queue-push, data:1 
user_data:Queue-push, data:2 
user_data:Queue-push, data:3 
ret1:1, ret2:2, ret3:3

尾插头取,第一个被插入的数据是1,第一个被取出的数据也是1,符合队列先进先出特点。


源码见glib_examples\glib_queue\glib_queue_stack

#include <glib.h>

static void _queue_foreach_func(gpointer data, gpointer       user_data)
{
    g_print("user_data:%s, data:%d \n", (gchar *)user_data, GPOINTER_TO_INT(data));
}

gint main(gint argc, gchar **argv)
{
    GQueue *q = NULL;
    gint ret1 = 0, ret2 = 0, ret3 = 0;

    q = g_queue_new();

    g_queue_push_tail(q, GINT_TO_POINTER(1));
    g_queue_push_tail(q, GINT_TO_POINTER(2));
    g_queue_push_tail(q, GINT_TO_POINTER(3));
    // result: 1-2-3

    g_queue_foreach(q, _queue_foreach_func, "Queue-push");

    // pop
    ret1 = GPOINTER_TO_INT(g_queue_pop_tail(q));
    ret2 = GPOINTER_TO_INT(g_queue_pop_tail(q));
    ret3 = GPOINTER_TO_INT(g_queue_pop_tail(q));

    g_print("ret1:%d, ret2:%d, ret3:%d \n", ret1, ret2, ret3);
    g_queue_free(q);

    return 0;
}

运行结果

user_data:Queue-push, data:1 
user_data:Queue-push, data:2 
user_data:Queue-push, data:3 
ret1:3, ret2:2, ret3:1

尾插尾取,第一个插入的数据1最后被取出,符合栈的特点。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值