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

我试图在数组中执行循环缓冲区。 我将数据保存在结构中,并通过推送,弹出等方法管理它。程序或多或少具有function和行为符合预期,但是我在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; } 

这是我测试上述缓冲操作的function的主要方法:
(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)); 

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

这有一些问题。 首先,您不应该将数据转换为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的指针数组,所以没有人会分心,因为这可以让人们认为你实际上存储了指向整数的指针 – 通过使用指针来取消空虚,你可以让任何人都明白你之后使用这段代码。

因此我建议创建一个类似于此的结构:

 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指针的数量。
  • 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; } 

最后让我展示一下推送function的样子:

 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函数和您想要的任何其他函数。