2016-12-15 73 views
1

我正在尝试在数组中执行循环缓冲区。我将数据保存在结构中,并通过push,pop等方法来管理它。该程序或多或少具有功能并且行为与预期相同,但是我在valgrind测试中遇到了错误。而且我无法找出我的代码出了什么问题。虽然看起来像通过指针在我的结构中管理数据是至关重要的问题。如果有人能指出我正确的方向,我会非常感激,因为我现在真的迷失了。从结构“无效读/写”中的指针获取数据

这是我的结构看起来像:

typedef struct queue_t{ 
    int* data; 
    int* end; 
    int* head; 
    int* tail; 
    int max_length; 
    int cur_length; 
} queue_t; 

这里是我的方法来管理缓冲区操作:
(注释代码产生几乎同样的错误作为的memcpy)

int* increase(int* point, queue_t* queue){ 
    if(point != queue->end){ 
     point = point + sizeof(int*); 
     return point; 
    }else{ 
     return queue->data; 
    } 
} 

    queue_t* create_queue(int capacity){ 
     queue_t* fifo; 
     fifo = malloc(sizeof(queue_t)); 
     fifo->data = malloc((capacity) * sizeof(int*)); 
     fifo->end = fifo->data + (capacity*sizeof(int*)); 
     fifo->head = fifo->data; 
     fifo->tail = fifo->data; 
     fifo->cur_length = 0; 
     fifo->max_length = capacity; 
     return fifo; 
    } 

    void delete_queue(queue_t *queue){ 
     free(queue->data); 
     free(queue); 
    } 

    bool push_to_queue(queue_t *queue, void *data){ 
     int *temp = (int*) data; 
     //*(queue->tail) = *temp; 
     memcpy(queue->tail, temp, sizeof(int)); 
     free(data); 
     if(queue->max_length != queue->cur_length){ 
      queue->cur_length++; 
     } 

     queue->tail = increase(queue->tail, queue); 

     if(queue->tail == queue->head){ 
      queue->head = increase(queue->head, queue); 
     } 
     return true; 
    } 

    void* pop_from_queue(queue_t *queue){ 
     if(queue->cur_length == 0){ 
      return NULL; 
     } 
     int *item = malloc(sizeof(int*)); 
     //*item = *(queue->head); 
     memcpy(item, queue->head, sizeof(int)); 
     queue->head = increase(queue->head, queue); 
     queue->cur_length--; 
     return item; 
    } 

这是我测试提到的缓冲区操作的功能性的主要方法:
(queue.h是我的功能而定义的)

#include "queue.h" 


void print_int(void* p){ 
    if(p != NULL){ 
     printf("%d\n", *((int*)p)); 
    } else { 
     printf("NULL\n"); 
    } 
} 

int main(){ 
    int n = 2; 
    int max = 10; 
    queue_t *q; 


    q = create_queue(n); 

    for(int i = 0; i<max;i++){ 
     int* p = malloc(sizeof(int)); 
     *p = i; 
     if(!push_to_queue(q, (void*)p)){ 
      free(p); 
      exit(101); 
     } 
    } 

    for(int i = 0;i<max;i++){ 
     void* p = pop_from_queue(q); 
     print_int(p); 
     free(p); 
    } 
    delete_queue(q); 

    return 0; 
} 

最后,这是我的valgrind输出:代码

==20293== HEAP SUMMARY: 
==20293==  in use at exit: 0 bytes in 0 blocks 
==20293== total heap usage: 15 allocs, 15 frees, 1,136 bytes allocated 
==20293== 
==20293== All heap blocks were freed -- no leaks are possible 
==20293== 
==20293== ERROR SUMMARY: 7 errors from 2 contexts (suppressed: 0 from 0) 
==20293== 
==20293== 1 errors in context 1 of 2: 
==20293== Invalid read of size 4 
==20293== at 0x40097C: pop_from_queue (queue.c:72) 
==20293== by 0x400713: main (main.c:30) 
==20293== Address 0x52030f0 is 16 bytes before a block of size 4 free'd 
==20293== at 0x4C2EDEB: free (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so) 
==20293== by 0x4008B8: push_to_queue (queue.c:51) 
==20293== by 0x4006D5: main (main.c:23) 
==20293== Block was alloc'd at 
==20293== at 0x4C2DB8F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so) 
==20293== by 0x4006B5: main (main.c:21) 
==20293== 
==20293== 
==20293== 6 errors in context 2 of 2: 
==20293== Invalid write of size 4 
==20293== at 0x4008AB: push_to_queue (queue.c:50) 
==20293== by 0x4006D5: main (main.c:23) 
==20293== Address 0x52030d0 is 16 bytes after a block of size 16 alloc'd 
==20293== at 0x4C2DB8F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so) 
==20293== by 0x4007FB: create_queue (queue.c:33) 
==20293== by 0x40069E: main (main.c:18) 
==20293== 
==20293== ERROR SUMMARY: 7 errors from 2 contexts (suppressed: 0 from 0) 

尖线有:

72: memcpy(item, queue->head, sizeof(int)); 
50: memcpy(queue->tail, temp, sizeof(int)); 

非常感谢,我希望有人能够告诉我,我在这里做的那个坏习惯是什么:/

+0

也许不是根本原因,但仍然会出现在我眼前:这个int * item = malloc(sizeof(int *));'没有任何意义。 'item'指向一个'int',所以它指向的字节数应该是int的大小,而不是int *的大小。所以你想仔细检查指针,如何分配给它们以及它们最终复制到它们指向的内容。 – alk

+0

非常感谢,纠正了,但错误仍然存​​在......我也会在其他分配中检查这个 – shade254

回答

1

这有几个问题。首先,你不应该将数据转换为int *,因为它可以是任何指针。在你的struct声明中,数据数组和所有其他指针应该声明为void **,因为它指向存储在数组中的void *类型。你根本不需要memcpy。你只需像这样分配它:*(queue->tail) = data;其中数据的类型是void *。在我看来,更清晰的方法是将头部和尾部存储为整数(作为相对于数组的索引) - 那么您可以这样做:queue->data[queue->tail] = data;而不必手动处理指针。

右键你在做这些线路上现在是什么:

int *item = malloc(sizeof(int*)); 
memcpy(item, queue->head, sizeof(int)); 

被分配一些内存,永远不会被释放的时候,但更重要的是,你不居然连返回已存储在queue-值>头。您将返回刚为项目分配的内存块地址。要获取的价值,你就必须取消对它的引用与一个明星,如:return *item;同样,你真正想要的,虽然什么是一个简单的任务:void *item = *(queue->head);

+0

您也可以使用对象名称本身来设置大小,而不必担心不正确的字体大小。尽管对于基本类型来说它是微不足道的,但使用复杂的定义类型是非常有益的。在这里它只是'int * item = malloc(sizeof * item);'你做这件事的方式并没有错,但值得指出另一种选择。 –

0

在你的代码基础上的一些功能特征(尤其是bool push_to_queue(queue_t *queue, void *data) { ...)我怀疑你想要什么 是存储指向你想要的任何数据的指针的结构。这个结构应该像一个队列一样。此外,你要实现它作为循环队列

的第一个问题,我与你的代码中看到的是队列的设计:

typedef struct queue_t{ 
    int* data; 
    int* end; 
    int* head; 
    int* tail; 
    int max_length; 
    int cur_length; 
} queue_t; 

最重要的 - 为什么你会喜欢这些指针存储(在int* data;)整数数组?也许指针阵列会更好?在C中,无论指向哪种类型的指针都具有相同的大小 - 它们必须能够存储任何内存地址,在64位操作系统上它通常意味着它们占用8个字节(8 * 8 = 64)。不过,我建议你指针阵列无效。为什么?因为你正在使用我,所以没有人会分心。即一个指向int的指针数组,因为这可以使人们认为你实际上是在存储指向整数的指针 - 通过使用指向void的指针,你对任何在你之后使用这段代码的人都是完全清楚的。

因此,我建议创建类似这样的结构:

typedef struct queue_t{ 
    void** base; 
    size_t capacity; 
    size_t used; 
    void** head; 
    void** tail; 
} queue_t; 
  • void** base将指向该阵列的第一个元素。
  • size_t capacity将存储数组长度 - 最多可以存储多少指针
  • size_t used将存储当前存储的空指针的数量。
  • void** head将指向下一个可用的数组元素(所以当用户调用push,我们将保存他data*head
  • void** tail将指向数组的最古老的元素(因此,当用户调用pop,我们会在某个时候return *tail;

然后你可以使用函数这样创建结构:

queue_t* create_queue(size_t capacity) { 
    queue_t* nq = malloc(sizeof(queue_t)); 
    // Let's allocate the array of pointers to void: 
    nq->base = malloc(sizeof(void*) * capacity); 
    nq->capacity = capacity; 
    nq->used = 0; 
    nq->head = nq->tail = nq->base; 
    return nq; 
} 

终于让我给推功能将如何看起来像:

bool push(queue_t* queue, void* data) { 
    if(queue == NULL || (queue->used == queue->capacity)) 
      return false; 
    *(queue->head++) = data; // this is equivalent to *(queue->head) = data; queue->head += 1; 
    if(queue->head >= queue->base + queue->capacity) 
      queue->head = queue->base; // We went to far, so we go back. 
    return true; 
} 

并使用相同的逻辑,你可以写pop功能,任何你想要的其他。