从结构 "Invalid read/write" 中的指针获取数据
Getting data from pointer in struct "Invalid read/write"
我正在尝试在数组中实现循环缓冲区。我将我的数据保存在结构中并通过 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));
非常感谢,我希望有人能够告诉我,我在这里做的那个坏习惯是什么:/
这有一些问题。首先,您不应该将数据转换为 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。为什么?因为没有人会因为您正在使用 i 而分心。 e.一个指向 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 指针的数量。
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
函数和您想要的任何其他函数。
我正在尝试在数组中实现循环缓冲区。我将我的数据保存在结构中并通过 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));
非常感谢,我希望有人能够告诉我,我在这里做的那个坏习惯是什么:/
这有一些问题。首先,您不应该将数据转换为 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。为什么?因为没有人会因为您正在使用 i 而分心。 e.一个指向 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 指针的数量。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
函数和您想要的任何其他函数。