2010-10-14 31 views
1

我想爲c項目寫一個PriorityQueue。當我嘗試出列物品時,程序崩潰;但是我認爲問題來自我添加項目的方式,因爲如果我在添加第三個元素後嘗試訪問列表中的第一個元素,我也會崩潰。問題與C中的PriorityQueue

頭文件

#ifndef PQUEUE_H_INCLUDED 
#define PQUEUE_H_INCLUDED 

#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> 
#include <stdbool.h> 

//Data structure for holding one element in pqueue 
typedef struct _e { 
    void *data; 
    size_t datalen; 
    int priority; 
    struct _e *next; 
} ELEMENT; 

//data structure for the whole pqueue 
typedef struct { 
    ELEMENT *head;   //reference to first element 
    ELEMENT *tail;   //reference to last element 
    ELEMENT *beforefirst; //dummy element before first element; 
    int elements; 
} PQUEUE; 


extern PQUEUE* queue_new(void); 
extern void  queue_free(PQUEUE *); 
extern void  queue_add_end(PQUEUE *, void *, size_t); 
extern void  queue_add_priority(PQUEUE *, void *, size_t,int); 
extern void* queue_remove(PQUEUE *); 
extern bool  queue_has_next(PQUEUE *); 
extern int  queue_size(PQUEUE *); 


#endif 

的PriorityQueue代碼:

#include "pqueue.h" 

PQUEUE *queue_new(void) { 
    PQUEUE *pq = malloc(sizeof(PQUEUE)); 
    if (pq == NULL) { 
     perror("queue_new"); 
     exit(EXIT_FAILURE); 
    } 
    pq->head = NULL; 
    ELEMENT *newelement; 
    newelement = calloc(1,sizeof(ELEMENT)); 
    pq->beforefirst = newelement; 
    pq->beforefirst->next = pq->head; 
    pq->tail = NULL; 
    pq->elements = 0; 

    return pq; 
} 

void queue_free(PQUEUE *pq) { 
    ELEMENT *this, *save; 
    this = pq->head; 
    while(this!= NULL) { 
     save = this; 
     this = this->next; 
     free(save->data); 
     free(save); 
    } 
    free(pq); 
} 


void queue_add_priority(PQUEUE *pq, void *data, size_t datalen,int priority) { 
    ELEMENT *newelement; 
    newelement = calloc(1,sizeof(ELEMENT)); 
    if (newelement == NULL) { 
     perror("queue_add"); 
     exit(EXIT_FAILURE); 
    } 
    newelement->data = malloc(datalen); 
    newelement->priority = priority; 
    if(newelement->data == NULL) { 
     perror("queue_add"); 
     exit(EXIT_FAILURE); 
    } 
    memcpy(newelement->data,data,datalen); 
    newelement->datalen = datalen; 
    newelement->next = NULL; 
    //sets pointer at beforefirst element and iterates through queue until ptr->next 
    // priority is greater than newlement priority, or until end of queue. 
    ELEMENT *ptr = pq->beforefirst; 
    while (ptr->next != NULL) { 
     if (ptr->next->priority > priority) break; 
     ptr = ptr->next; 
    } 
    if (ptr == pq->beforefirst) { 
     pq->head = newelement; 
    } 
    if (ptr->next == NULL) { 
     pq->tail = newelement; 
    } 
    newelement->next = ptr->next; 
    ptr->next = newelement; 


    //ERROR HERE 
    //void* d; 
    //d = pq->head->data; 
    pq->elements++; 
} 

void* queue_remove(PQUEUE *pq) { 
    //ERROR HERE 
    void* item = pq->head->data; 
    pq->head = pq->head->next; 
    pq->elements--; 
    return item; 
} 

bool queue_has_next(PQUEUE *pq) { 
    return !(pq->elements == 0); 
} 

基本上,錯誤似乎快要當我試圖進入PQ->頭戴式>數據我已經添加了後第三個元素 - 我將其縮小到所評論的區域//錯誤在這裏。對我來說這似乎很奇怪,因爲添加第三個元素應該與添加第二個元素相同。 也不是pq-> head == NULL或pq-> head> data == NULL。

+0

這對我的作品。你可以發佈添加對象到隊列的代碼嗎? – Samaursa 2010-10-14 03:54:05

回答

3

我看到以下問題:

  • queue_free不釋放內存,其beforeFirst對象。
  • add_priority如果數據分配失敗,則不會釋放元素。這並不重要,因爲你正在退出,但如果你決定返回一個錯誤(換句話說,它會停止內存泄漏),這將是一個好的形式。

但是,我已經通過插入一個新元素,一個元素之前,然後一個元素在最後,它似乎很好,測試代碼。您要插入哪些優先級值(按順序)?

您可能想要發佈調用此代碼的代碼。這很可能是一個與這個實際代碼無關的內存損壞問題。


雖然我很欣賞你在介紹beforeFirst的東西,以保持你的代碼不錯的嘗試,你真的應該只是咬緊牙關,擺脫它。它的刪除可能會抵消爲了處理一個真正的空列表而需要添加的最小代碼量。這個更簡單的代碼應該處理所有場景,而不需要額外的處理來保持額外的指針同步。

我還沒有實際比我的溼件測試這等,但它應該(希望)好工作:

typedef struct _e { 
    void *data; 
    size_t datalen; 
    int priority; 
    struct _e *next; 
} ELEMENT; 

typedef struct { 
    ELEMENT *head;   //reference to first element 
    ELEMENT *tail;   //reference to last element 
    int elements; 
} PQUEUE; 

 

PQUEUE *queue_new(void) { 
    PQUEUE *pq = malloc(sizeof(PQUEUE)); 
    if (pq == NULL) { 
     perror("queue_new"); 
     exit(EXIT_FAILURE); 
    } 
    pq->head = pq->tail = NULL; 
    pq->elements = 0; 
    return pq; 
} 

void queue_free(PQUEUE *pq) { 
    ELEMENT *this, *save; 
    this = pq->head; 
    while(this!= NULL) { 
     save = this; 
     this = this->next; 
     free(save->data); 
     free(save); 
    } 
    free(pq); 
} 

 

void queue_add_priority(PQUEUE *pq, void *data, size_t datalen, int priority) { 
    ELEMENT *newelement; 
    newelement = calloc(1,sizeof(ELEMENT)); 
    if (newelement == NULL) { 
     perror("queue_add"); 
     exit(EXIT_FAILURE); 
    } 
    newelement->data = malloc(datalen); 
    if(newelement->data == NULL) { 
     perror("queue_add"); 
     free (newelement); 
     exit(EXIT_FAILURE); 
    } 
    memcpy(newelement->data,data,datalen); 
    newelement->datalen = datalen; 
    newelement->priority = priority; 
    newelement->next = NULL; 

    // Inserting into empty list. 
    if (pq->elements == 0) { 
     pq->head = pq->tail = newelement; 
     pq->elements = 1; 
     return; 
    } 

    // Inserting beyond tail. 
    if (pq->tail->priority <= priority) { 
     pq->tail->next = newelement; 
     pq->tail = newelement; 
     pq->elements++; 
     return; 
    } 

    // Inserting before head. 
    if (pq->head->priority > priority) { 
     newelement->next = pq->head; 
     pq->head = newelement; 
     pq->elements++; 
     return; 
    } 

    // Otherwise, we're inserting somewhere in the middle. 
    ELEMENT *ptr = pq->head; 
    while (ptr->next->priority <= priority) 
     ptr = ptr->next; 
    newelement->next = ptr->next; 
    ptr->next = newelement; 
    pq->elements++; 
} 

 

void* queue_remove(PQUEUE *pq) { 
    if (pq->elements == 0)  // added, just in case. 
     return NULL; 
    void* item = pq->head->data; 
    pq->head = pq->head->next; 
    pq->elements--; 
    return item; 
} 

bool queue_has_next(PQUEUE *pq) { 
    return (pq->elements > 0); // better, IMNSHO. 
} 

請記住,queue_add_priority會複製內存,以便您可以將它傳遞給動態分配的內存或以其他方式。該功能不承擔釋放您傳遞給它的任何分配內存的責任。如果它是動態分配的,你仍然必須自己釋放它。它是這樣做的,所以你可以通過任何種類的內存。

另一方面,queue_remove只是通過你回分配的內存,所以你負責完成後釋放它。您收到的內存將有總是已通過malloc獲得。

可以優化queue_add_priority,讓你可以指定該內存通過malloc被分配被傳遞,你是通過改變功能的第一部分,通過責任:

void queue_add_priority(PQUEUE *pq, void *data, size_t datalen, int priority) { 
    ELEMENT *newelement; 
    newelement = calloc(1,sizeof(ELEMENT)); 
    if (newelement == NULL) { 
     perror("queue_add"); 
     exit(EXIT_FAILURE); 
    } 
    newelement->data = malloc(datalen); 
    if(newelement->data == NULL) { 
     perror("queue_add"); 
     free (newelement); 
     exit(EXIT_FAILURE); 
    } 
    memcpy(newelement->data,data,datalen); 

到:

void queue_add_priority(PQUEUE *pq, void *data, size_t datalen, int priority, int xfer) { 
    ELEMENT *newelement; 
    newelement = calloc(1,sizeof(ELEMENT)); 
    if (newelement == NULL) { 
     perror("queue_add"); 
     exit(EXIT_FAILURE); 
    } 
    if (!xfer) { 
     newelement->data = malloc(datalen); 
     if(newelement->data == NULL) { 
      perror("queue_add"); 
      free (newelement); 
      exit(EXIT_FAILURE); 
     } 
     memcpy(newelement->data,data,datalen); 
    } else { 
     newelement->data = data; 
    } 

換句話說,如果數據是通過malloc獲得的,並且您同意放棄它的責任,請將最終參數設置爲true - 這樣,該功能只需要你的內存塊。

否則,請使用false並進行復制。

0

這是導致問題的訪問模式。

PQUEUE *queue = queue_new(); 
int x0 = 0; 
int x1 = 1; 
queue_add_priority(queue, &x0, sizeof(x0), x0); //add1 
queue_add_priority(queue, &x1, sizeof(x1), x1); //add2 
queue_remove(queue);       //remove 
queue_add_priority(queue, &x0, sizeof(x0), x0); //add3 
while (queue_has_next(queue)) 
    printf("%d\n", *(int *)queue_remove(queue)); 

應優先考慮具有較高優先級的項目嗎?如果應該的話,這不會發生在你的代碼中。

在前兩次添加之後,計數爲2,優先級較低的項目位於開頭(0 1,計數:2)。下面的刪除操作使head元素遞減計數。剩下的是優先級1項目(1,計數:1)。問題是添加另一個0優先級項目,實際上並沒有向隊列添加任何內容,並且計數仍然增加(1,計數:2)。然後,由於隊列中記錄了實際上只有1個項目時有2個項目,所以最後的移除失敗。

問題在於你遍歷隊列。更具體地說,在刪除後不會更新您開始的位置(beforefirst指針)。刪除後,它仍然指向「已刪除」節點。實際上,所有正在進行的添加將添加到先前移除的節點的末尾,使實際隊列處於不一致的狀態。這是爲什麼在不需要時持續釋放內存並將指針設置爲NULL之後的好主意的原因之一。

0

除了paxdiablo mentioned的問題之外,在刪除隊列頭的情況下,您也忘記更新beforefirst->next。這裏發生的事情:

 
Before queue_remove: 

    beforefirst    head    tail 
     |     |     | 
     V     V     V 
+-------------+  +-------------+  +-------------+ 
| placeholder | --> |  x  | --> |  y  | --> NULL 
+-------------+  +-------------+  +-------------+ 



After queue_remove: 

    beforefirst        head tail 
     |         |  | 
     V         V  V 
+-------------+  +-------------+  +-------------+ 
| placeholder | --> |  x  | --> |  y  | --> NULL 
+-------------+  +-------------+  +-------------+ 

您需要修復queue_remove使beforefirst->nexthead->next如果head爲非空(NULL,否則)。

0

如果插入的元素位於第一個位置,則必須修正queue_add以更改before_first。

通常,我不會使用before_first。只需改變你的循環來檢查ptr爲current == null,並將開始元素改爲第一個。

應該消除所有其他問題。

心連心

馬里奧