【问题标题】:Issues with Ring buffer implementation using direct pointer manipulation使用直接指针操作实现环形缓冲区的问题
【发布时间】:2016-10-25 17:55:12
【问题描述】:

我已经为环形缓冲区编写了以下代码。我只想使用没有任何位置指示器的指针。但是我在写作时没有得到预期的结果。尽管发生了写入,但它不会在第一次进入后停止。它继续进一步。读取操作似乎是正确的。 我尝试使用调试器进行调试。但令我惊讶的是,if(head>(myRing+AVAILABLE_SIZE*sizeof(myRing[0]))) 没有在函数 writeToRing() 函数中执行。调试器正在跳过这一步。同样,第一次 head++ 没有在这个函数中执行,而是代码将首先转到 if(head==tail) 然后返回到 head++;找不到原因。 我在 MinGW 中使用 Code::Blocks

#define MAX_SIZE 2
#define AVAILABLE_SIZE (MAX_SIZE-1)

/*
Program to construct and access ring buffer without structure.
This program makes use of pointer. Even without pointer it is possible to manage
*/

int myRing[MAX_SIZE];
int *head=myRing;   // Initialize next element than tail
int *tail=myRing;

enum ERROR_LIST
{
    SUCCESS=0,
    BUFFER_FULL=-1,
    BUFFER_EMPTY=-2
    };

int writeToRing(int data)
{
    head++;
    if(head>(myRing+AVAILABLE_SIZE*sizeof(myRing[0])))
    {
        head=myRing;  //wraps over
    }
    else
    {
        // Do nothing
    }
    if(head==tail)
    {
        head--;
        if(head<myRing)     // In case head is less than starting address. assign max address
        {
            head=(myRing+AVAILABLE_SIZE*sizeof(myRing[0]));
        }
        printf("Buffer full\n");
        return(BUFFER_FULL);
    }
    else
    {
        *head=data;
    }
    return(SUCCESS);
}

 int readFromBuffer(int* data)
 {
     if(tail==head)
     {
         return(BUFFER_EMPTY);
     }
     else
     {
         tail++;
         if(tail>(myRing+AVAILABLE_SIZE*sizeof(myRing[0])))
         {
             tail=myRing;
         }
         *data=*tail;
         return(SUCCESS);
     }
 }

int main()
{
    int option;
    int data;
    while(1)
    {
    printf("Enter Your option. 1 for writing to buffer, 2 for reading from Buffer\n");
    scanf("%d",&option);
    if(option==1)
    {
        printf("Enter the data to be written\n");
        scanf("%d",&data);
        if(writeToRing(data))
        {
            printf("Buffer is Full. Remove the contents first\n");
        }
    }
    else if(option==2)
    {
        if(!readFromBuffer(&data))
        {
            printf("The data read = %d\n",data);
        }
        else
        {
            printf("Buffer is Empty\n");
        }
    }
    else
    {
        printf("Invalid Option\n");
    }

    }
    return(0);
}

编辑: 用另一种方法更新了代码。在此代码中,不会浪费一个数据字节。我进行了测试,似乎正在工作。但如果有任何其他问题,请告诉我。类型说明符为 %u 的 scanf 有两个警告,需要查看如何修复它。在这种情况下,我基本上想读取字节。如果我去整数读取,没有问题。

#include <stdio.h>
#include <stdint.h>
#define MAX_SIZE 3

uint8_t BUFFER_FULL=0;  // Initially buffer full flag is cleared
uint8_t BUFFER_EMPTY=1;   // Initially buffer empty flag is set

/*
Program to construct and access ring buffer without pointer.
Also this makes use of full buffer and checks for buffer empty or buffer full condition
before calling write or read functionality,
*/

uint8_t myRing[MAX_SIZE];
uint8_t head=0;   // Initialize the head
uint8_t tail=0;   // Initialize the tail
uint8_t maxPosition= MAX_SIZE-1;


void writeToRing(uint8_t data)
{
    head=head+1;
    if(head>maxPosition)
    {
        head=0;
    }
    else
    {
        // Do nothing
    }
    // Write the data to buffer
    myRing[head]=data;
    BUFFER_EMPTY=0;  // Indicate that buffer is not empty

    if(head==tail)
    {
        printf("Buffer full No further data can be written\n");
        BUFFER_FULL=1;
    }
}

void readFromRing(uint8_t* data)
{
    // Initially buffer is empty (BUFFER_EMPTY=1). At that point, calling portion cannot call this function.
    // Later when data is written, writeToRing() function will clear BUFFER_EMPTY flag.

    tail++;
    if(tail>maxPosition)
    {
        tail=0;
    }
    *data=myRing[tail];
    // Once reading is done, ensure that BUFFER_FULL flag is cleared.
    BUFFER_FULL=0;
    if(tail==head)
    {
        printf("Buffer is now Empty. No further reading possible\n");
        BUFFER_EMPTY=1;
    }
}

int main()
{
    uint8_t option;
    uint8_t data;
    while(1)
    {
        printf("Enter Your option. 1 for writing to buffer, 2 for reading from Buffer\n");
        scanf("%u",&option);
        if(option==1)
        {
            if(!BUFFER_FULL)
            {
                printf("Enter the data to be written\n");
                scanf("%u",&data);
                writeToRing(data);
            }
            else
            {
                // Nothing to be done in case buffer is FULL
                printf("Buffer should be cleared first\n");
            }
        }
        else if(option==2)
        {
            if(!BUFFER_EMPTY)
            {
                uint8_t data;
                readFromRing(&data);
                printf("The data read = %d\n",data);
            }
            else
            {
                printf("Buffer is Empty. Write something before you read\n");
            }
        }
        else
        {
            printf("Invalid Option\n");
        }
    }
    return(0);
}

【问题讨论】:

    标签: c mingw circular-buffer


    【解决方案1】:

    正确的形式是:

    if(head>(myRing+AVAILABLE_SIZE))
    {
    ...
    }
    

    由于 myRing 是一个指针,“+”运算符本身执行元素大小的乘法。

    【讨论】:

    • 事实上,我最初尝试过不乘以 sizeof(int)。这让我很困扰,然后我出于测试目的进行了更改并忘记删除。不知道为什么调试器会按照我的指示跳过这一步。但是今天我按照你的建议做了,它正在工作。一定是在某个地方犯了一些愚蠢的错误。
    【解决方案2】:

    您可以进行一些改进以简化代码

    1. 添加另一个指针 (wrap) 来标记环形缓冲区的结束。当headtail 等于wrap 时,是时候将它们设置回缓冲区的开头了。

    2. writeToRing函数中,使用临时指针(temp)来检查缓冲区是否已满。这样head 不会受到影响,直到您知道写入将成功,因此您无需撤消计算。


    int myRing[MAX_SIZE];
    int *head = myRing;
    int *tail = myRing;
    int *wrap = &myRing[MAX_SIZE];
    
    int writeToRing( int data )
    {
        int *temp = head+1;
    
        if ( temp == wrap )
            temp = myRing;
    
        if ( temp == tail )
            return BUFFER_FULL;
    
        head = temp;
        *head = data;
        return SUCCESS;
    }
    
    int readFromRing( int *data )
    {
        if ( tail == head )
            return BUFFER_EMPTY;
    
        tail++;
        if ( tail == wrap )
            tail = myRing;
    
        *data = *tail;
        return SUCCESS;
    }
    

    至于调试器为何表现得如此有趣,请确保在调试时关闭优化。调试器很难跟踪优化的代码。

    【讨论】:

    • 我没有添加 temp 因为这是用于嵌入式代码并且不太可能溢出。所以考虑到没有溢出的可能性,我认为撤消“头”可能不会出现,因此可以避免额外的复制和额外的内存。还要调试:即使删除优化后也没有运气。我需要去 eclipse 看看这个问题是否解决(不确定调试特别需要的任何其他编译器设置)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-07-12
    • 2017-10-13
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多