2016-12-15 100 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功能,任何你想要的其他。