C 从结构中的指针获取数据;无效读/写“;

C 从结构中的指针获取数据;无效读/写“;,c,memory-management,struct,valgrind,circular-buffer,C,Memory Management,Struct,Valgrind,Circular Buffer,我正在尝试在数组中实现循环缓冲区。我将数据保存在结构中,并通过push、pop等几种方法对其进行管理。该程序或多或少都具有功能,并按预期运行,但我在valgrind测试中遇到了错误。我无法找出我的代码有什么问题。尽管在我的结构中,通过指针管理数据似乎是关键问题。如果有人能给我指出正确的方向,我会非常感激,因为我现在真的迷路了 我的结构就是这样的: typedef struct queue_t{ int* data; int* end; int* head; int

我正在尝试在数组中实现循环缓冲区。我将数据保存在结构中,并通过push、pop等几种方法对其进行管理。该程序或多或少都具有功能,并按预期运行,但我在valgrind测试中遇到了错误。我无法找出我的代码有什么问题。尽管在我的结构中,通过指针管理数据似乎是关键问题。如果有人能给我指出正确的方向,我会非常感激,因为我现在真的迷路了

我的结构就是这样的:

typedef struct queue_t{
    int* data;
    int* end;
    int* head;
    int* tail;
    int max_length;
    int cur_length;
} queue_t;
==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));
以下是我管理缓冲区操作的方法:
(注释代码产生的错误与memcpy几乎相同)

这是我测试上述缓冲区操作功能性的主要方法:
(queue.h是定义my函数的位置)

代码的尖行是:

typedef struct queue_t{
    int* data;
    int* end;
    int* head;
    int* tail;
    int max_length;
    int cur_length;
} queue_t;
==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));

提前非常感谢,我希望有人能告诉我,我在这里做的不好的做法是什么://

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

现在您在这些线路上所做的工作:

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

正在分配一些永远不会被释放的内存,但更重要的是,实际上您甚至没有返回存储在queue->head中的值。您将返回刚刚分配给该项的内存块的地址。要获取该值,必须使用星号取消对其的引用,如:
return*item同样,您真正想要的是一个简单的赋值:
void*item=*(queue->head)

基于代码中某些函数的签名(特别是
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)但是,我建议你一个指向void的指针数组。为什么?因为没有人会因为你正在使用指向int的指针数组而分心,因为这会让人们认为你实际上在存储指向整数的指针-通过使用指向void的指针,你可以让任何使用t的人完全明白这一点他的密码跟在你后面

因此,我建议创建类似以下内容的结构:

typedef struct queue_t{
    void** base;
    size_t capacity;
    size_t used;
    void** head;
    void** tail;
} queue_t;
  • void**base
    将指向数组的第一个元素
  • size\u t capacity
    将存储数组的长度-最多可以存储多少个指针
  • size\t used
    将存储当前存储的空指针的数量
  • void**head
    将指向下一个可用数组元素(因此当用户调用
    push
    时,我们将把他的
    数据
    存储到
    *head
  • void**tail
    将指向数组中最早的元素(因此当用户调用
    pop
    时,我们将
    在某个点返回*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
函数和任何其他您想要的函数。

也许不是根本原因,但在我看来仍然是:这个
int*item=malloc(sizeof(int*))
真的没有意义。
指向一个
int
,所以它指向的字节数应该是
int
的大小,而不是
int*
的大小。因此,您需要双重检查指针,如何分配给它们,以及它最终复制到它们指向的内容的字节数和数量。非常感谢,更正了错误persist…我也会在其他分配中检查这一点。您也可以使用对象名称本身设置大小,而不必担心不正确的类型大小。虽然对于基本类型来说这很简单,但对于复杂定义的类型来说,这是非常有益的。这里只需要
int*item=malloc(sizeof*item)
你做这件事的方式没有错,但值得指出另一种选择。