2014-02-13 57 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

相关问题