2014-02-13 94 views
1

在該方法結束時,我所有的測試printfs輸出相同的結果。該文件的最後一行。但while循環中的當前printf正常工作。出於某種原因,我的節點具有所有相同的結果。我該如何解決它? 這是我的結構單元:鏈接列表給出了相同的結果C

struct unit 
{ 
    struct unit * next; 
    char *name; 
}; 

這是我的鏈接列表中添加行逐一鏈表功能:

void readFile(char fileName[], struct unit * units) 
{ 
    FILE * fp; 
    char *line = NULL; 
    int length = 1000; 
    fp = fopen(fileName, "r"); 
    int counter = 0; 
    int strLength = 0; 
    struct unit * current; 

    units = (struct units*)malloc(sizeof(struct unit)); 
    current = units; 

    while (getline(&line, &length, fp) != -1) 
    { 
     strLength = strlen(&line); 
     current->name = (char*)malloc(sizeof(char)* strLength); 
     current->next = (struct units*)malloc (sizeof(struct unit)); 
     strcpy(&current->name, &line); 
     printf("\nCurrent: %s",current->name); 
     current = current->next; 
     counter++; 
    } 
    printf("\nTest %s", units->name); 
    printf("\nTest %s", units->next->name); 
    printf("\nTest %s", units->next->next->name); 
    printf("\nTest %s", units->next->next->next->name); 
} 
+1

我建議讓一個白板或一張紙,並繪製出來遵循你的邏輯。這會幫助你找到錯誤。 – jia103

+0

[請不要在C中輸入malloc的結果](http://stackoverflow.com/questions/605845/do-i-cast-the-result-of-malloc) –

+0

getline在哪裏定義? – Shmoopy

回答

0

您的代碼具有若干不良做法,和幾個錯誤。進入循環之前,您不需要預先分配節點。您可以簡單地按需分配。有許多方法可以確保使用稱爲正向鏈接的技術將新分配的節點添加到列表的末尾。我會在一分鐘內達成。下面的列表是沒有特定的順序,不過我至少嘗試去解決它自上而下


主要:您readFile()函數應該它分配列表的頭部。如果你想以後加入這個其他一些列表,隨意,但功能應該與此開始:

struct unit* readFile(const char fileName[]) 

:還要注意我們沒有修改文件名,所以沒有理由把它作爲可變的,因此它是非常量的。


主要:使用它檢查您的文件打開操作成功前:

fp = fopen(fileName, "r"); 
if (fp == NULL) 
{ 
    perror("Failed to open file."); 
    return NULL; 
} 

主要:對API使用正確類型的變量調用你的決策。功能getline(),一個非標準擴展,是原型爲:

ssize_t getline(char ** , size_t * , FILE *) 

它返回一個ssize_t(一個「簽名」大小型),並採取了size_t*用於第二參數。作爲第二個參數,您要傳遞一個int變量的地址,即length。這不能保證兩者是兼容的類型。通過聲明length作爲正確的類型來解決這個問題。 size_t

size_t length = 0; 

:同樣的問題發生與返回值的類型的strlen(),這也是size_t,但將在一剎那間變得不重要的,你很快就會看到。


主要:您使用的getline()除了第二個參數之前提到的幾乎正確。第一個循環的初始輸入是一個NULL指針的地址和一個0值的length。如果在前一個循環中已經分配的緩衝區足夠大,每次迭代都會被重用。不幸的是,讀取更短的行,然後更長,然後然後更短,並且然後更長將引入不需要的額外分配。事實上,您可以完全放棄您的malloc()邏輯,只是使用getline()來分配緩衝區,因爲它被記錄爲使用malloc()兼容分配。因此,使用現有的邏輯(我們將在最後走了過來):

while (getline(&line, &length, fp) != -1) 
{ 
    // note: added to throw out empty lines 
    if (length > 0) 
    { 
     // note: added to null out trailing newline. see the 
     // documentation of getline() for more info. 
     if (line[length-1] == '\n') 
      line[length-1] = 0; 
    } 

    if (line[0] != 0) 
    { 
     // other code here 
     current->name = line; 
    } 
    else 
    { // not using this. release it. 
     free(line); 
    } 

    // reset line and length for next iteration 
    line = NULL; 
    length = 0; 
} 

主要:您原來的算法從未free() d行緩衝,一旦你用它做,從而引入一次性內存泄漏。使用上述替代方法,您無需擔心。


備用:最後,列表循環人口可以進行應用到目前爲止所討論的一切,並添加到其名爲前向鏈接技術更穩健。該技術使用指針指針pp,該指針始終保存將接收下一個節點分配的指針地址。如果列表最初是空的(它是),它保存着head指針的地址。每添加一個新節點pp都會分配最後一個節點的next成員的地址。當循環完成時(即使我沒有添加任何節點),我們通過設置*pp = NULL來結束列表。

這是readFile的最終代碼庫。我希望你覺得它很有用:

struct unit* readFile(char fileName[]) 
{ 
    FILE * fp; 
    char *line = NULL; 
    size_t length = 0; 

    // used for populating the list 
    struct unit *head = NULL; 
    struct unit **pp = &head; 

    // open file 
    fp = fopen(fileName, "r"); 
    if (fp == NULL) 
    { 
     perror("Failed to open file"); 
     return NULL; 
    } 

    while (getline(&line, &length, fp) != -1) 
    { 
     // note: added to throw out empty lines 
     if (length > 0) 
     { 
      // note: added to null out trailing newline. see the 
      // documentation of getline() for more info. 
      if (line[length-1] == '\n') 
       line[length-1] = 0; 
     } 

     if (line[0] != 0) 
     { 
      // allocate new node 
      *pp = malloc(sizeof(**pp)); 
      if (*pp != NULL) 
      { 
       (*pp)->name = line; 
       pp = &(*pp)->next; 
      } 
      else 
      { // could not allocate a new node. uh oh. 
       perror("Failed to allocate new node"); 
       free(line); 
       break; 
      } 
     } 
     else 
     { // not using this. release it. 
      free(line); 
     } 

     // reset line and length for next iteration 
     line = NULL; 
     length = 0; 
    } 
    *pp = NULL; 

    return head; 
} 
1

你爲什麼要&line突入strlenstrcpy?如果我沒有記錯,只需將linecurrent->name傳入這些函數。 (我不知道getline雖然;也許這是很好的。)

1

這對我有用(建立和運行一個文件有幾行,我必須改變我的編譯器的getline函數:也改變了幾個「單元」爲「單元」是該結構的名稱還用於緩衝的線是靜態爲255個字符的最大長度保留):

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

struct unit{ 
    struct unit * next; 
    char *name; 
}; 

void readFile(char fileName[], struct unit * units){ 
    FILE * fp; 
    char line[255]; 
    int length = 1000; 
    fp = fopen(fileName, "r"); 
    int counter = 0; 
    int strLength = 0; 
    struct unit * current; 

    units = (struct unit*)malloc(sizeof(struct unit)); 
    current = units; 

    while (fgets (line, sizeof line, fp) != NULL) /* read a line */ 
    { 
     strLength = strlen(line); 
     current->name = (char*)malloc(sizeof(char)* strLength); 
     current->next = (struct unit*)malloc (sizeof(struct unit)); 
     strcpy(current->name, line); 
     printf("\nCurrent: %s",current->name); 
     current = current->next; 
     counter++; 
    } 
    fclose (fp); 

    printf("\nTest %s", units->name); 
    printf("\nTest %s", units->next->name); 
    printf("\nTest %s", units->next->next->name); 
    printf("\nTest %s", units->next->next->next->name); 
} 

int main(){ 
    readFile("filename.txt", NULL); 
} 
+0

中打印與current->名稱相同的工作,謝謝。哇,我沒有看到我寫了「單位」而不是「單位」。問題可能在於此。 –

+0

那麼有人知道實際的錯誤是什麼? – jia103

+0

@ jia103首先,名稱緩衝區的長度不包含用於終止nulchar的空間,因此每個「strcpy()」運行一次 - 時隙通過分配空間末尾。奇怪的是,這個例子做了同樣的事情,但是由於某種原因被接受了,當它明確地調用未定義的行爲時。其次,這個答案也會導致OP代碼所犯的錯誤,即列表中的最後一個節點總是未被使用(包括一個空列表)。總之,我不知道爲什麼接受這個問題更少解決問題。 – WhozCraig

相關問題