【问题标题】:queue in C array with pointers使用指针在 C 数组中排队
【发布时间】:2017-07-04 07:02:03
【问题描述】:

我尝试用 C 语言通过数组和指针来实现队列。队列 采用C结构建模

// Circular Buffer
typedef struct{
    main_controller_req_t     buffer[BUFFER_SIZE];   // buffer
    uint16_t                  size;                  // length of the queue
    uint16_t                  count;                 // number of elements present in the queue
    main_controller_req_t     *p_head;               // pointer to head of the queue (read end)
    main_controller_req_t     *p_tail;               // pointer to tail of the queue (write end)
}circular_buffer_t;

我已经实现了队列初始化函数

void init_cb(circular_buffer_t *p_cb){

    p_cb->p_head = p_cb->buffer;
    p_cb->p_tail = p_cb->buffer;
    p_cb->count = 0;
    p_cb->size = BUFFER_SIZE;

}

插入队列的函数

BOOL enqueue_cb(circular_buffer_t *p_cb, main_controller_req_t *p_enq_elem){

    if(p_cb->count < p_cb->size){

        // queue contains at least one free element

        taskENTER_CRITICAL();

            // insert the element at the tail of queue
            *(p_cb->p_tail) = *p_enq_elem;
            // incrementing modulo size
            p_cb->p_tail = (((p_cb->p_tail++) == (p_cb->buffer + p_cb->size)) ? (p_cb->buffer) : (p_cb->p_tail));
            // one element added
            p_cb->count++;

        taskEXIT_CRITICAL();

        return TRUE;

    }else{

        // queue is full
        return FALSE;

    }

}

enqueue_cb 函数运行良好,直到尾部达到 BUFFER_SIZE。然后 程序崩溃和 uC 重置。问题出在 p_tail 指针更新中,但我没有 明白为什么。请问有人可以帮我吗?提前致谢。

【问题讨论】:

  • 我建议您在您的 PC(Visual Studio 或类似软件)上执行所有不依赖于 CPU 的库。它将简化您的调试并产生更少的问题。
  • 请提供您的主要功能的一小部分,您的使用会导致崩溃。
  • @Steve> 您不能在同一语句中指定 p_cb->p_tail 在其上使用 ++。 (公平地说,你可以让你的代码难以理解,即使是你自己)

标签: c arrays pointers struct queue


【解决方案1】:

问题出在您的测试中...您检查是否溢出,但增加指针之前,这会导致p_cb-&gt;p_tail越过循环缓冲区的末尾。

 p_cb->p_tail = ((p_cb->p_tail++) == (p_cb->buffer + p_cb->size)) 
                    ? (p_cb->buffer) 
                    : (p_cb->p_tail);

你必须在 递增之后测试 tail 的值。

 p_cb->p_tail = (++(p_cb->p_tail) >= (p_cb->buffer + p_cb->size)) 
                    ? p_cb->buffer 
                    : p_cb->p_tail;

顺便说一句,链表并不是实现循环缓冲区的一种非常有效的方式。您是否考虑过使用简单的 fifo 缓冲区实现?

fifo 结构非常常见,因此为 C 提供 通用 宏实现很有用。如果您只有一个生产者和一个消费者,它是线程安全的(不需要锁)。

这些宏很容易被编译器优化。

这是一个很好的工具。

// save these macros in a header file.
#define fifo_reset(fifo) \
     { (fifo).head = (fifo).tail = (fifo).buffer; }

#define fifo_elements(fifo) \
    (sizeof((fifo).buffer) / sizeof((fifo).buffer[0])

#define fifo_count(fifo) \
    (((fifo).tail >= (fifo).head)) ? ((fifo).tail - (fifo).head) : (((fifo).tail - (fifo).head) + fifo_elements(fifo))

#define fifo_free_space(fifo) \
    (fifo_elements(fifo) - (fifo_count(fifo) + 1))

#define __fifo_advance(fifo, ptr) /* this is "private" */\
    (( ((ptr) + 1) < (fifo).buffer + fifo_elements(fifo))) ? ((ptr) + 1) : (fifo).buffer )

#define fifo_full(fifo) \
    (__fifo_advance(fifo, (fifo).head) == (fifo).tail)

#define fifo_empty(fifo) \
    ((fifo).head == (fifo).tail)

#define fifo_pop(fifo, el)  \
    ( (fifo).head = ((el) = *(fifo).head, __fifo_advance(fifo, (fifo).head)) )

#define fifo_push(fifo, el)  \
    ( (fifo).tail = (*((fifo).tail) = (el), __fifo_advance(fifo, (fifo).tail)) )

// Now, any struct with head, tail, and fixed-length buffer is a fifo.
struct char_fifo_128
{
  char buffer[128];  
  char* head;
  char* tail;
};

struct main_controller_req_t
{
    // all data types are _movable_ using '='
    char any[128];
    int data;
    float more_data;
    char* dupstr;
};

#define BUFFER_SIZE 256

struct controller_fifo
{
  main_controller_req_t     buffer[BUFFER_SIZE];   // buffer
  main_controller_req_t*    head;
  main_controller_req_t*    tail;
};

int main()
{
  char c = 0;
  int elements_stored;
  controller_fifo queue;
  main_controller_req_t req;

  fifo_reset(queue);  // MUST call before using fifo

  // always check if space/data is available.
  if (!fifo_full(queue))
  {
      fifo_push(queue, req);
  }
  elements_stored = fifo_count(queue);

  if (!fifo_empty(queue))
  {
      fifo_pop(queue, req);
  }

  fifo_reset(queue);   // fifo reset is NOT thread-safe


  char_fifo_128* buffer = (char_fifo_128*)malloc(sizeof(char_fifo_128));

  fifo_reset(*buffer);

  if (!fifo_full(*buffer))
  {
      // ...
      fifo_push(*buffer, req);
  }
  elements_stored = fifo_count(*fifo);

  if (!fifo_empty(*buffer))
  {
      fifo_pop(*buffer, c);
  }

  free(buffer);
  return 0;
}

【讨论】:

  • 但无论如何,请在该语句之前的另一个语句中进行增量。它不会更改生成的代码,但它使非语言律师更容易阅读。在这里,要正确阅读语句,必须知道三元运算符保证其条件的副作用在其他操作数的值计算之前排序。
  • 非常感谢迈克尔·罗伊。
  • @史蒂夫。我很高兴,信息在回答中添加了。 @spectras: ++ 运算符具有明确定义的语义:++i 递增 i 并返回递增后的值。 i++ 复制i,递增i,然后返回递增前由i 复制的值。这就是为什么建议使用++i 的原因,因为它可以避免复制。
  • @dhein 为什么要删除我制作的关于该主题的文档?可怕的 fifo 设计是如此普遍……而且它在 C 中不再像“链接列表”文档或“置换同余生成器”那样离题,因为整个示例都是关于使用宏的。它们的使用在 C 中被认为是良好的做法,而在许多其他语言中并非如此。我无法评论该主题,因为您已将其删除,因此这里有此消息。
猜你喜欢
  • 1970-01-01
  • 2020-03-25
  • 1970-01-01
  • 2018-01-04
  • 1970-01-01
  • 2013-08-05
  • 1970-01-01
  • 1970-01-01
  • 2021-01-12
相关资源
最近更新 更多