在C中实现队列

时间:2012-12-31 15:21:46

标签: c queue gstreamer tail head

我正在尝试为Gstreamer缓冲区实现自定义队列。问题是,当我试图出列时,似乎我正在失去队列的头部。每当我尝试两次出队时,我都会遇到分段错误。我也注意到头部总是等于头部 - >接下来。现在我不确定入队或出队是否有问题。请帮帮我。谢谢。

typedef struct _GstBUFFERQUEUE GstBufferQueue;

struct _GstBUFFERQUEUE {
  GstBuffer *buf;
  guint buf_size;
  struct _GstBUFFERQUEUE *next;
};

void enqueue_gstbuffer(GstBufferQueue **head, GstBufferQueue **tail, guint *queue_size, GstBuffer *buf)
{
  if (*queue_size == 0)
  {
    *head = malloc(sizeof(GstBufferQueue));
    (*head)->buf = gst_buffer_try_new_and_alloc (GST_BUFFER_SIZE(buf));
    (*head)->buf = gst_buffer_copy(buf); 
    *tail = *head;
  }
  else
  {
    if ((*tail)->next = malloc(sizeof(GstBufferQueue))) {
        (*tail)->next->buf = gst_buffer_try_new_and_alloc (GST_BUFFER_SIZE(buf));
        (*tail)->next->buf = gst_buffer_copy(buf);
        (*tail) = (*tail)->next;
    }
    else {
        GST_WARNING("Error allocating memory for new buffer in queue");
    } 
  } 
  (*tail)->next = NULL; 
  (*queue_size)++;

}

void dequeue_gstbuffer(GstBufferQueue **head, GstBufferQueue **tail, guint *queue_size, GstBuffer **buf)
{
  GstBufferQueue **tmpPtr = head;
  GstBufferQueue **nextPtr;
  *nextPtr = (*head)->next; 
  *buf = gst_buffer_try_new_and_alloc (GST_BUFFER_SIZE((*tmpPtr)->buf));
  *buf = gst_buffer_copy((*tmpPtr)->buf);
  gst_buffer_unref((*tmpPtr)->buf);
  free((*tmpPtr));
  *head = *nextPtr;

  if ((*head) == NULL)
     (*tail) = NULL;

   (*queue_size)--;   
}

3 个答案:

答案 0 :(得分:5)

当通过添加足够的伪基础设施来模拟GST系统转换为可编译代码时,GCC会发出警告,这几乎肯定是您遇到麻烦的根源:

gstq.c: In function ‘dequeue_gstbuffer’:
gstq.c:73:12: warning: ‘nextPtr’ is used uninitialized in this function [-Wuninitialized]

这些行是:

72  GstBufferQueue **nextPtr;
73  *nextPtr = (*head)->next;

在这些方面,你需要:

GstBufferQueue *nextPtr = (*head)->next;

您还需要使用:

(*head)->next = nextPtr;

注意编译器警告。如果编译器没有发出警告,请执行此操作。如果您无法发出警告,请获得更好的编译器。


SSCCE

#include <stdlib.h>
#include <stdio.h>
#include <assert.h>

#define GST_BUFFER_SIZE(x)  sizeof(x)
#define GST_WARNING(x)      fprintf(stderr, "%s\n", x)

typedef struct GstBuffer { int value; } GstBuffer;
typedef unsigned int guint;

static GstBuffer *gst_buffer_try_new_and_alloc(int size)
{
    GstBuffer *buf = malloc(sizeof(GstBuffer));
    assert(buf != 0);
    buf->value = size;
    return buf;
}

static GstBuffer *gst_buffer_copy(const GstBuffer *buf)
{
    GstBuffer *new_buf = malloc(sizeof(GstBuffer));
    assert(new_buf != 0);
    new_buf->value = buf->value;
    return new_buf;
}

static void gst_buffer_unref(GstBuffer *buf)
{
    buf->value = -1;
}

typedef struct _GstBUFFERQUEUE GstBufferQueue;

struct _GstBUFFERQUEUE {
  GstBuffer *buf;
  guint buf_size;
  struct _GstBUFFERQUEUE *next;
};

extern void enqueue_gstbuffer(GstBufferQueue **head, GstBufferQueue **tail, guint *queue_size, GstBuffer *buf);
extern void dequeue_gstbuffer(GstBufferQueue **head, GstBufferQueue **tail, guint *queue_size, GstBuffer **buf);

void enqueue_gstbuffer(GstBufferQueue **head, GstBufferQueue **tail, guint *queue_size, GstBuffer *buf)
{
  if (*queue_size == 0)
  {
    *head = malloc(sizeof(GstBufferQueue));
    (*head)->buf = gst_buffer_try_new_and_alloc(GST_BUFFER_SIZE(buf));
    (*head)->buf = gst_buffer_copy(buf); 
    *tail = *head;
  }
  else
  {
    if (((*tail)->next = malloc(sizeof(GstBufferQueue))) != 0)
    {
        (*tail)->next->buf = gst_buffer_try_new_and_alloc(GST_BUFFER_SIZE(buf));
        (*tail)->next->buf = gst_buffer_copy(buf);
        (*tail) = (*tail)->next;
    }
    else
    {
        GST_WARNING("Error allocating memory for new buffer in queue");
    } 
  } 
  (*tail)->next = NULL; 
  (*queue_size)++;
}

void dequeue_gstbuffer(GstBufferQueue **head, GstBufferQueue **tail, guint *queue_size, GstBuffer **buf)
{
  GstBufferQueue **tmpPtr = head;
  GstBufferQueue  *nextPtr;
  nextPtr = (*head)->next; 
  *buf = gst_buffer_try_new_and_alloc (GST_BUFFER_SIZE((*tmpPtr)->buf));
  *buf = gst_buffer_copy((*tmpPtr)->buf);
  gst_buffer_unref((*tmpPtr)->buf);
  free((*tmpPtr));
  *head = nextPtr;

  if ((*head) == NULL)
     (*tail) = NULL;

   (*queue_size)--;   
}

int main(void)
{
    GstBufferQueue *q_head = 0;
    GstBufferQueue *q_tail = 0;
    guint           q_size = 0;

    for (int i = 0; i < 10; i++)
    {
        GstBuffer *buf = gst_buffer_try_new_and_alloc(i + 100);
        enqueue_gstbuffer(&q_head, &q_tail, &q_size, buf);
        printf("EQ: %d\n", buf->value);
        free(buf);
        if (i % 2 == 1)
        {
            GstBuffer *buf;
            dequeue_gstbuffer(&q_head, &q_tail, &q_size, &buf);
            printf("DQ: %d\n", buf->value);
            free(buf);
        }
    }

    while (q_size > 0)
    {
        GstBuffer *buf;
        dequeue_gstbuffer(&q_head, &q_tail, &q_size, &buf);
        printf("DQ: %d\n", buf->value);
        free(buf);
    }

    printf("All done\n");
    return(0);
}

输出

EQ: 100
EQ: 101
DQ: 100
EQ: 102
EQ: 103
DQ: 101
EQ: 104
EQ: 105
DQ: 102
EQ: 106
EQ: 107
DQ: 103
EQ: 108
EQ: 109
DQ: 104
DQ: 105
DQ: 106
DQ: 107
DQ: 108
DQ: 109
All done

请注意,上面的SSCCE代码泄漏比筛子差。我没有计划修复泄漏,因为它们在模拟GST缓冲区管理的代码中。请检查您的代码是否没有内存泄漏。


我认为你应该以不同方式打包'队列'。你所谓的GstBufferQueue应该是GstBufferQueueItem,你的实际GstBufferQueue应该包含头尾指针和大小。您将指向(修订的)GstBufferQueue的指针传递给enqueue_gstbuffer()dequeue_gstbuffer()函数,而不是传递3个单独的参数。

typedef struct GstBufferQueueItem GstBufferQueueItem;

struct GstBufferQueueItem
{
  GstBuffer *buf;
  guint buf_size;
  GstBufferQueueItem *next;
};

typedef struct GstBufferQueue GstBufferQueue;

struct GstBufferQueue
{
    GstBufferQueueItem *head;
    GstBufferQueueItem *tail;
    guint               size;
};

// Uncompiled - but to give you an idea
void dequeue_gstbuffer(GstBufferQueue *q, GstBuffer **buf)
{
    GstBufferQueueItem *item = q->head;
    GstBufferQueueItem *next = item->next; 
    *buf = gst_buffer_try_new_and_alloc(GST_BUFFER_SIZE(item->buf));
    *buf = gst_buffer_copy(item->buf);
    gst_buffer_unref(item->buf);
    free(item);
    q->head = next;

    if (q->head == NULL)
        q->tail = NULL;

    q->size--;   
}

请注意,这些名称会避免使用前导下划线。这样的名字很危险。带有下划线和大写字母的名称保留用于任何目的的实现。带有下划线和小写字母的名字保留不同的单词,但使用其中任何一个都是狡猾的(尽管标准对下划线和数字很少说,不要用它们玩游戏 - 将前导下划线视为'保留用于系统'除非你写'系统'。)

ISO / IEC 9899:2011§7.1.3保留标识符

  
      
  • 所有以下划线和大写字母或其他字母开头的标识符   下划线总是保留用于任何用途。
  •   
  • 所有以下划线开头的标识符始终保留用作标识符   在普通名称和标签名称空间中都有文件范围。
  •   

答案 1 :(得分:3)

替换

GstBufferQueue **nextPtr;
*nextPtr = (*head)->next;
...
*head = *nextPtr;

通过

GstBufferQueue *nextPtr;
nextPtr = (*head)->next;
...
*head = nextPtr;

答案 2 :(得分:0)

有一点让我感到高兴的是,当您第一次分配队列时(当* queue_size == 0时),您没有将新创建的节点的“下一个”指针设置为NULL。

在分配和分配到(* head)之后无法保证它是NULL,所以当你执行出队时你的(* head) - &gt; next可能指向一个垃圾地址。