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

时间:2016-12-15 17:27:31

标签: c memory-management struct valgrind circular-buffer

我正在尝试在数组中执行循环缓冲区。我将数据保存在结构中,并通过推送,弹出等几种方法对其进行管理。程序或多或少具有功能,并且按预期运行,但是我在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));

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

2 个答案:

答案 0 :(得分:1)

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

现在你在这些方面做了什么:

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

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

答案 1 :(得分: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指针数。
  • void** head将指向下一个可用的数组元素(因此,当用户调用push时,我们会将他的data存储到*head < / LI>
  • 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函数以及您想要的任何其他函数。