簡體   English   中英

C Quicksort(鏈接列表)分段錯誤

[英]C Quicksort (linked list) segmentation fault

我必須在鏈接列表(在C中)上創建一個快速排序。 我的第一個和最后一個指針是樞軸(在此代碼中,它是列表的第一個元素)。 我必須使用的結構:

typedef struct list_element list_element;

struct list_element {
    char *password;
    int count;
    list_element* next;
};

typedef struct list list;

struct list {
    list_element* first;
    list_element* last;
};

我有一個包含100個密碼和數量的文件。 像這樣:password1 123(下一行)password2 435(下一行)password3 133 ...必須在此程序末尾對密碼進行排序(根據其數量)。 左右列表不需要任何額外的內存分配,因為我只需要使用下一個指針即可。 (這就是練習中的提示。)

給定的主要功能:

int main(int argc, char** args)
{
    if (argc != 2)
    {
        printf("Nutzung: %s <Dateiname>\n",args[0]);
        return 1;
    }
    list mylist;
    init_list(&mylist);
    read_data(args[1],&mylist);
    qsort_list(&mylist);
    printf("Sortierte Liste:\n");
    print_list(&mylist);
    free_list(&mylist);
    return 0;
}

我已經初始化了我的清單:

void init_list(list* mylist)
{
    mylist->first = NULL;
    mylist->last = NULL;
}

並在末尾插入一個新元素(passwort =文件中的密碼,hauefigkeit =文件中的計數):

void insert_list(list_element* le, list* mylist)
{
    if (mylist->first != NULL) {
        le->next = mylist->last;
        mylist->last = le;
        le->next= NULL;
    }
    else {
        mylist->last->next = le;
        mylist->last = le;
        mylist->last->next = NULL;
    }
}

從文件讀取數據:

void read_data(char* filename, list* mylist)
{
    FILE *file_in = fopen(filename, "r"); 

    if (file_in == NULL) {
        perror("Could not open input file!");
        exit(1);
    }

    char buffer[999] = "0";

    char *passwort = (char*) calloc(1,sizeof(passwort));
    int haeufigkeit = 0;

    while (fgets(buffer, sizeof(buffer), file_in) != NULL) {
        sscanf(buffer, "%s %d", passwort, &haeufigkeit);

        list_element* le = (list_element*)calloc(1,sizeof(list_element));

        for(int i = 0; i <=100; i++) {
            le->password[i] = passwort[i];
        }
        le->count = haeufigkeit;
        le->next = NULL;

        insert_list(le, mylist);
    }
    fclose(file_in);
}

列表分區:

list_element* partition( list* input, list* left, list* right )
{
    list_element* pivot = NULL;
    if (input->first != NULL) {
        list_element* temp;
        pivot = input->first;
        input->first = input->first->next;
        pivot->next = NULL;
        left->first = NULL;
        right->first = NULL;

        while (input->first != NULL) {
            if((pivot->count)>(input->first->count)){
                temp=input->first->next;
                insert_list(input->first, left);
                input->first=temp;
            } 
            else {
                temp = input->first->next;
                insert_list(input->first, right);
                input->first = temp;
            }
        }
    }
    return pivot;
}

實際的快速排序:

void qsort_list(list* mylist)
{
    if(mylist->first == mylist->last){
    }

    else{
        list* left = calloc(1,sizeof(list));
        list* right= calloc(1,sizeof(list));
        list_element* pivot = partition(mylist, left, right);

        qsort_list(left);
        qsort_list(right);

        if(left->first == NULL){
            mylist->first = pivot;
        }
        else{
            mylist->first = left->first;
            left->last->next = pivot;
        }

        if(right->first == NULL){
            pivot->next = NULL;
            mylist->last = pivot;
        }
        else{
            pivot->next = right->first;
            mylist->last = right->last;
        }

        free(right);
        free(left);

    }
}

在最后的打印列表中:

void print_list(list* mylist)
{
    list_element *elem = mylist->first;
    while (elem != NULL) {
        printf("%s %d\n", elem->password, elem->count);
        elem = elem->next;
    }     
}

以及免費清單:

void free_list(list* mylist)
{
    list_element *current;
    list_element *second;
    current = mylist->first;
    while (current != NULL) {
        second = current->next;
        free(current);
        current = second;
    }
}

語法應該可以。 GCC(c99,Wall)編譯沒有任何問題。

但是存在分段錯誤。 我已經搜索了幾個小時,不知道問題可能在哪里。 也許您可以幫助我解決這個問題。


在前兩個答案之后,沒有任何分割錯誤。 但是read_data函數仍然有問題。 該程序無法正確讀取密碼。 也許我誤解了您對閱讀功能的回答。 這是當前功能:

void read_data(char* filename, list* mylist)
{
    FILE *file_in = fopen(filename, "r"); 

    if (file_in == NULL) { 
        perror("Could not open input file!");
        exit(1);
    }

    char buffer[999] = "0";

    int haeufigkeit = 0;

    while (fgets(buffer, sizeof(buffer), file_in) != NULL) {

        char passwort[100];

        sscanf(buffer, "%s %d", passwort, &haeufigkeit);

        list_element* le = (list_element*)    

    calloc(1,sizeof(list_element));


        le->password = passwort;
        le->count = haeufigkeit;
        le->next = NULL;

        insert_list(le, mylist);
    }
    fclose(file_in);
}

正如Leonardo Alves Machado指出的那樣,當遇到C / C ++程序問題時,第一個反射就是使用gdb類的調試器運行它。 這是基礎知識:

gcc -g main.c -o main
gdb main
(gdb) run

注意-g編譯標志:這會將調試信息添加到可執行文件中。


read_data ,各行

for(int i = 0; i <=100; i++) {
    le->password[i] = passwort[i];
}

真的很煩我。 您為passwort分配了空間(您永遠不會釋放它),然后嘗試將其復制到le->password ,這是一個簡單的指針(沒有分配空間)。 您實際需要的是使le->password 指向 passwort ,即

le->password = passwort;

free_list ,在使用以下方法釋放passwort空間之前,請不要忘記釋放list_element空間:

while (current != NULL) {
    second = current->next;
    free(current->password);
    free(current);
    current = second;
}

程序遇到的第一個問題是read_data()沒有為passwort分配足夠的空間。 實際上,目前尚不清楚,為什么要動態地分配它,但是鑒於這樣做, sizeof(passwort)是一個指向char的指針的大小(因為那是passwort的意思),可能是4或8個字節。 稍后,您似乎假定(嘗試)將其內容復制到列表元素中時,分配的空間為100字節長。 為什么不簡單地將其聲明為100個字符的數組?

char passwort[100];

的確,如果您也以相同的方式聲明list_element.passwort ,則循環中的密碼復制代碼將是正確的,盡管有點不習慣。

實際上,正如@Derlin所觀察到的那樣,該代碼是有問題的。 然而,他提出的解決方案是不正確的。 只要在整個例程中只分配一次本地passwort ,就不能使列表元素指向本地passwort 然后,所有列表元素將具有相同的密碼字符串,這不是您想要的。 如果你希望你的列表元素包含指向密碼,因為他們現在這樣做,那么你將要移動的聲明和分配passwort 循環,使您獲得分配給每個列表元素單獨的口令空間。 那么分配le->password = passwort建議是正確的。


另一個早期的問題是您的insert_list()函數嚴重損壞。

首先考慮當您嘗試將元素插入到由init_list()初始化的空列表中時發生的情況。 列表的nextlast成員都將為null,因此insert_list()將嘗試執行以下代碼:

    mylist->last->next = le;
    mylist->last = le;
    mylist->last->next = NULL;

觀察到mylist->last為null,因此第一行通過嘗試取消引用null指針來調用未定義的行為。 分割錯誤是一個非常合理的觀察結果。 您可以通過將其中的第一行更改為

    mylist->next = le;

現在考慮當您嘗試插入非空列表時會發生什么。 在這種情況下,執行以下行:

    le->next = mylist->last;
    mylist->last = le;
    le->next= NULL;

由於您打算在末尾插入新元素(即附加它),因此將新元素的next指針設置為列表的最后一個元素很奇怪。 以后再用NULL覆蓋該值是特別奇怪的。 您似乎將其向后:您希望將初始的last元素設置為指向新元素作為其下一個元素,而不是相反:

    mylist->last->next = le;

確實,這正是空列表情況下的錯誤代碼,但是當列表為非空時這很好。

總體而言,您的函數還遭受奇怪的缺乏並行性和某些隱藏代碼重復的困擾。 我可能會編寫整體函數,像這樣:

void append_to_list(list_element* le, list* mylist)
{
    le->next= NULL;
    if (mylist->first != NULL) {
        mylist->last->next = le;
        mylist->last = le;
    }
    else {
        mylist->first = le;
        mylist->last = le;
    }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM