【问题标题】:Generic ring buffer通用环形缓冲区
【发布时间】:2018-10-29 00:13:59
【问题描述】:

我已经实现了一个通用的环形缓冲区,但我遇到了一些奇怪的结果。首先我成功推送了 15 个整数,但是当我尝试打印到缓冲区的内容时,它打印了一些错误的元素。

首先我为 10 个整数分配内存。然后我尝试推动 15 并调整缓冲区的大小。也许调整大小会以某种方式破坏它,因为没有它它工作正常。

RingBuffer.c

#include<stdlib.h>
#include<string.h>
#include <stdio.h>
#include"RingBuffer.h"

unsigned char RingInit(RingBuffer* buffer, int elemLen, int elemSize, void (*fn)(void*))
{
    buffer->dataLen = elemLen;
    buffer->dataSize = elemSize;
    
    if( (buffer->data = malloc( buffer->dataSize * buffer->dataLen )) == NULL){
        
        return 1;
    }

    buffer->head = 0;
    buffer->tail = 0;
    buffer->elemCount = 0;
    buffer->freeFn = fn;
    buffer->doResize = 1;
    buffer->resizeBy = 2;
    
    return 0;
}

unsigned char RingPush(RingBuffer* buffer, void* elem)
{
    if(RingIsFull(buffer)){

        if(!buffer->doResize || RingResize(buffer)){
            
            return 1;
        }
    }

    memcpy((buffer->data + (buffer->head*buffer->dataSize)), elem, buffer->dataSize);
    buffer->head = (buffer->head + 1) % buffer->dataLen;
    buffer->elemCount++;
    
    return 0;
}

unsigned char RingRead(RingBuffer* buffer, void* elem)
{
    if(!RingIsEmpty(buffer)){
        
        memcpy(elem, (buffer->data + (buffer->tail*buffer->dataSize)), buffer->dataSize);
        buffer->tail = (buffer->tail + 1) % buffer->dataLen;
        buffer->elemCount--;

        return 0;
    }

    return 1;
}

unsigned char RingIsEmpty(RingBuffer* buffer)
{
    return (buffer->elemCount == 0);
}

unsigned char RingIsFull(RingBuffer* buffer)
{
    return (buffer->elemCount == buffer->dataLen);
}

void RingFree(RingBuffer* buffer)
{
    buffer->freeFn(buffer->data);
}

unsigned char RingResize(RingBuffer* buffer)
{
    unsigned char* newMemory = NULL;

    if ( (newMemory = realloc(buffer->data, buffer->dataLen + (buffer->resizeBy * buffer->dataSize))) == NULL){
        
        return 1;
    }
    
    buffer->data = newMemory;
    buffer->dataLen += buffer->resizeBy;    
    buffer->head = buffer->elemCount;
    
    return 0;
}

RingBuffer.h

#ifndef RINGBUFFER_H_
#define RINGBUFFER_H_

typedef struct
{
    void *data;
    int dataSize;
    int dataLen;
    int head;
    int tail;
    int elemCount;
    int doResize;
    int resizeBy;
    void (*freeFn)(void*);

}RingBuffer;

unsigned char RingInit      (RingBuffer* buffer, int elemLen, int elemSize, void (*fn)(void*));
unsigned char RingRead      (RingBuffer* buffer, void* elem);
unsigned char RingIsEmpty   (RingBuffer* buffer);
unsigned char RingIsFull    (RingBuffer* buffer);
void          RingFree      (RingBuffer* buffer);
unsigned char RingResize    (RingBuffer* buffer);
unsigned char RingPush      (RingBuffer* buffer, void* elem);

#endif /* RINGBUFFER_H_ */

Test.c

#include<stdlib.h>
#include<string.h>
#include <stdio.h>
#include"RingBuffer.h"

#define ARRAY_LENGTH(a)     sizeof(a)/sizeof(a[0])

void freeInt(void* data)
{
    free(data);
}

int main(void)
{
    RingBuffer Ring;
    int buf[15] = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};
    int i = 0;
    
    if(RingInit(&Ring, ARRAY_LENGTH(buf)-5, sizeof(int), freeInt) == 0){
        
        printf("\nRING INIT OK");
    }
    else{
        
        printf("\nRING INIT FAIL");
        RingFree(&Ring);
        return 0;
    }
    
    Ring.doResize = 1;
    for(i = 0 ; i < ARRAY_LENGTH(buf) ; i++){
        
        if(RingPush(&Ring, &buf[i])){
            
            printf("\nRING PUSH FAIL");
            RingFree(&Ring);
            return 0;
        }
        
        printf("\nRING PUSH OK->%d", buf[i]);
        printf(" %d", ((int*)(Ring.data))[i]); 
    }
    
    printf("\n");
    for(i = 0 ; i < ARRAY_LENGTH(buf); i++){
        
        printf(" %d", ((int*)(Ring.data))[i]);
    }
    
    memset(buf, 0, sizeof(buf));
    printf("\nLOAD ZEROS:");
    for(i = 0 ; i < ARRAY_LENGTH(buf); i++){
        printf(" %d", buf[i]);
    }
    
    i = 0;
    while(!RingIsEmpty(&Ring)){
        
        if(RingRead(&Ring, &buf[i])){
            
            printf("\nRING READ FAIL");
            RingFree(&Ring);
            return 0;
        }
        
        printf("\nRING READ OK->%d", buf[i]);
        i++;
    }
    
    getchar();
    RingFree(&Ring);
    return 0;
}

结果:

RING INIT OK
RING PUSH OK->1 1
RING PUSH OK->2 2
RING PUSH OK->3 3
RING PUSH OK->4 4
RING PUSH OK->5 5
RING PUSH OK->6 6
RING PUSH OK->7 7
RING PUSH OK->8 8
RING PUSH OK->9 9
RING PUSH OK->10 10
RING PUSH OK->11 11
RING PUSH OK->12 12
RING PUSH OK->13 1313427978
RING PUSH OK->14 1431314503
RING PUSH OK->15 1327515731
 1 2 3 4 5 6 -1326086846 5687 12790216 12779712 11 12 926036256 842412848 909391924
LOAD ZEROS: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
RING READ OK->1
RING READ OK->2
RING READ OK->3
RING READ OK->4
RING READ OK->5
RING READ OK->6
RING READ OK->-1326086846
RING READ OK->5687
RING READ OK->12790216
RING READ OK->12779712
RING READ OK->11
RING READ OK->12
RING READ OK->1313427978
RING READ OK->1163010119
RING READ OK->1327514689   

它应该打印数字 1-15。它会打印一些错误的数字。每次我启动程序时,不同的元素都是错误的。

编译

gcc Test.c RingBuffer.c

编译器版本

gcc (MinGW.org GCC-6.3.0-1) 6.3.0 版权所有 (C) 2016 Free Software Foundation, Inc. 这是免费软件;查看复制条件的来源。没有 保修单;甚至不是为了适销性或特定用途的适用性。

【问题讨论】:

  • AddressSanitizer 在打印 RING PUSH OK-&gt;10 10 之前检测到堆缓冲区溢出。
  • ARRAY_LENGTH(buf) - 5
  • @ChristianGibbons 故意分配较小的缓冲区以测试调整大小功能是否有效
  • 尺寸请使用size_t

标签: c


【解决方案1】:

您将buffer-&gt;dataLen + (buffer-&gt;resizeBy * buffer-&gt;dataSize) 作为要在函数RingResize 中分配的大小。

很遗憾,buffer-&gt;dataLen元素的数量,而不是字节的数量。

因此,新分配的缓冲区将没有足够的字节数。 这会导致错误。

要分配的大小应该是buffer-&gt;dataSize * (buffer-&gt;dataLen + buffer-&gt;resizeBy)

【讨论】:

    猜你喜欢
    • 2012-04-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多