您的代码具有若干不良做法,和几个错误。进入循环之前,您不需要预先分配节点。您可以简单地按需分配。有许多方法可以确保使用称为正向链接的技术将新分配的节点添加到列表的末尾。我会在一分钟内达成。下面的列表是没有特定的顺序,不过我至少尝试去解决它自上而下
主要:您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;
}
我建议让一个白板或一张纸,并绘制出来遵循你的逻辑。这会帮助你找到错误。 – jia103
[请不要在C中输入malloc的结果](http://stackoverflow.com/questions/605845/do-i-cast-the-result-of-malloc) –
getline在哪里定义? – Shmoopy