将 getline() 输出保存到外部数组
Saving getline() output to an external array
外部数组 srclns 应保留文本文件中的每个读取行。但是之后阅读它的内容似乎阅读行是空字符串。我在下面的代码中缺少什么?
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#define MAXSRC 20
char *srclns[MAXSRC]; /* source lines */
size_t read_lines(char *path)
{
FILE *stream;
ssize_t read;
char *lnptr;
size_t n;
size_t count;
stream = fopen(path, "r");
lnptr = NULL;
n = 0;
count = 0;
if (!stream) {
fprintf(stderr, "Can't open source '%s'\n", path);
exit(EXIT_FAILURE);
}
while ((read = getline(&lnptr, &n, stream)) != -1) {
srclns[count++] = lnptr;
}
free(lnptr);
fclose(stream);
return count;
}
int main()
{
size_t n = read_lines("foo.txt");
for (size_t i = 0; i<n; i++)
printf("%zu %s\n", i, srclns[i]);
exit(EXIT_SUCCESS);
}
此后仅打印看似空字符串的行号:
0
1
2
3
4
5
函数 getline
仅在 lnptr
为 NULL
(ref) 时才分配内存。第一次迭代是这样,但之后需要重新设置为NULL
:
while ((read = getline(&lnptr, &n, stream)) != -1) {
srclns[count++] = lnptr;
lnptr = NULL;
}
否则,lnptr
仍将指向在所有后续迭代的第一次迭代中分配的内存,并且 getline
将反复尝试写入该位置。
即使这不是问题的原因,分配的内存也应该被释放。例如,通过在 exit(EXIT_SUCCESS)
:
之前添加这些行
for (size_t i = 0; i<n; i++)
free(srclns[i]);
使用 getline
是否是一个好的做法是另一个您可能想要研究的讨论。它不是最便携的解决方案。
据我所知,您的程序不仅无法运行,而且可能存在内存泄漏。这是由于 getline 使用动态分配的行为。
让我们仔细看看您的程序做了什么,特别是 while ((read = getline(&lnptr, &n, stream)) != -1)
循环:
getline 将与类型为 char**
.
的 &lnptr
一起使用
- 如果指针是
NULL
它将在堆上分配足够的内存(动态)来存储正在读取的行。
- 如果指针不是
NULL
那么它应该指向大小为 n
的缓冲区
- 如果缓冲区足够大(大于或等于行长度),它用于存储字符串。
- 如果缓冲区太小,那么
getline
会重新分配内存,以便有足够大的缓冲区可用。重新分配后,n
会更新为新的缓冲区大小。在某些情况下,重新分配将意味着 lnptr
必须修改并且将会修改。 (如果在当前缓冲区之后没有足够的连续可用内存,则可能会发生这种情况。在这种情况下,内存将分配到堆上的其他地方。如果您对此感兴趣,我建议您进行研究,因为动态内存分配是一个相当复杂的主题,否则只知道指针可能会改变,现在就足够了)。
下面是你的程序的问题(至少这是我可以从我所掌握的信息中推断出来的。我可能是错的,但这似乎是最合理的解释):
- 在循环的第一次迭代中
lnptr
是NULL
。因此 getline 在堆上分配内存并存储该行,并更新 lnptr
以指向新分配的缓冲区。
- 在循环中,您将指向已分配缓冲区的指针存储在
srclns[0]
中
- 在随后的迭代中 缓冲区被覆盖并且可能被 getline 调整大小,您仍然存储指向同一缓冲区的指针
srclns[count]
.
- 循环后释放缓冲区并丢弃
srclns
中每个指针指向的内存。
- 当你打印时,你很可能读取了一个无效的内存区域(这是你刚刚释放的指针所指向的区域),幸运的是它似乎以终止符开头(您文件的最后一行可能是一个空行,并且在 free 之后没有任何主动更改此内存区域...)
如何解决:
您可以使用 malloc
and/or calloc
显式处理动态分配,但这看起来有点复杂,如前所示,getline
可以为您处理。我的建议如下:
- 将
srclns
中的所有元素设置为 NULL
for(int i = 0; i < MAXSRC; ++i)
{
srclns[i] = NULL;
}
- 然后修改 while 循环以在每次迭代中传递
srclns
的新元素。每次调用 getline
都会看到一个 NULL
指针,从而分配内存并更新 srclns
的单元格以指向它。这个实现的好处是你永远不会超出 srclns
的范围:
for(int i = 0; i < MAXSRC; ++i)
{
n = 0
if(getline(&srclns[i], &n, stream) == -1)
{
break; // We get out if we reached OEF
}
}
- 在您为
printf
访问 main 后释放所有分配的内存
for(int i = 0; i < MAXSRC; ++i)
{
if(srclns[i] != NULL)
{
free(srclns[i]);
}
}
- 调整。我没有对代码进行测试,所以我可能犯了一些错误......请随时纠正它。您可能还想调整代码以满足您的需要。
外部数组 srclns 应保留文本文件中的每个读取行。但是之后阅读它的内容似乎阅读行是空字符串。我在下面的代码中缺少什么?
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#define MAXSRC 20
char *srclns[MAXSRC]; /* source lines */
size_t read_lines(char *path)
{
FILE *stream;
ssize_t read;
char *lnptr;
size_t n;
size_t count;
stream = fopen(path, "r");
lnptr = NULL;
n = 0;
count = 0;
if (!stream) {
fprintf(stderr, "Can't open source '%s'\n", path);
exit(EXIT_FAILURE);
}
while ((read = getline(&lnptr, &n, stream)) != -1) {
srclns[count++] = lnptr;
}
free(lnptr);
fclose(stream);
return count;
}
int main()
{
size_t n = read_lines("foo.txt");
for (size_t i = 0; i<n; i++)
printf("%zu %s\n", i, srclns[i]);
exit(EXIT_SUCCESS);
}
此后仅打印看似空字符串的行号:
0
1
2
3
4
5
函数 getline
仅在 lnptr
为 NULL
(ref) 时才分配内存。第一次迭代是这样,但之后需要重新设置为NULL
:
while ((read = getline(&lnptr, &n, stream)) != -1) {
srclns[count++] = lnptr;
lnptr = NULL;
}
否则,lnptr
仍将指向在所有后续迭代的第一次迭代中分配的内存,并且 getline
将反复尝试写入该位置。
即使这不是问题的原因,分配的内存也应该被释放。例如,通过在 exit(EXIT_SUCCESS)
:
for (size_t i = 0; i<n; i++)
free(srclns[i]);
使用 getline
是否是一个好的做法是另一个您可能想要研究的讨论。它不是最便携的解决方案。
据我所知,您的程序不仅无法运行,而且可能存在内存泄漏。这是由于 getline 使用动态分配的行为。
让我们仔细看看您的程序做了什么,特别是 while ((read = getline(&lnptr, &n, stream)) != -1)
循环:
getline 将与类型为 char**
.
&lnptr
一起使用
- 如果指针是
NULL
它将在堆上分配足够的内存(动态)来存储正在读取的行。 - 如果指针不是
NULL
那么它应该指向大小为n
的缓冲区- 如果缓冲区足够大(大于或等于行长度),它用于存储字符串。
- 如果缓冲区太小,那么
getline
会重新分配内存,以便有足够大的缓冲区可用。重新分配后,n
会更新为新的缓冲区大小。在某些情况下,重新分配将意味着lnptr
必须修改并且将会修改。 (如果在当前缓冲区之后没有足够的连续可用内存,则可能会发生这种情况。在这种情况下,内存将分配到堆上的其他地方。如果您对此感兴趣,我建议您进行研究,因为动态内存分配是一个相当复杂的主题,否则只知道指针可能会改变,现在就足够了)。
下面是你的程序的问题(至少这是我可以从我所掌握的信息中推断出来的。我可能是错的,但这似乎是最合理的解释):
- 在循环的第一次迭代中
lnptr
是NULL
。因此 getline 在堆上分配内存并存储该行,并更新lnptr
以指向新分配的缓冲区。 - 在循环中,您将指向已分配缓冲区的指针存储在
srclns[0]
中
- 在随后的迭代中 缓冲区被覆盖并且可能被 getline 调整大小,您仍然存储指向同一缓冲区的指针
srclns[count]
. - 循环后释放缓冲区并丢弃
srclns
中每个指针指向的内存。 - 当你打印时,你很可能读取了一个无效的内存区域(这是你刚刚释放的指针所指向的区域),幸运的是它似乎以终止符开头(您文件的最后一行可能是一个空行,并且在 free 之后没有任何主动更改此内存区域...)
如何解决:
您可以使用 malloc
and/or calloc
显式处理动态分配,但这看起来有点复杂,如前所示,getline
可以为您处理。我的建议如下:
- 将
srclns
中的所有元素设置为NULL
for(int i = 0; i < MAXSRC; ++i) { srclns[i] = NULL; }
- 然后修改 while 循环以在每次迭代中传递
srclns
的新元素。每次调用getline
都会看到一个NULL
指针,从而分配内存并更新srclns
的单元格以指向它。这个实现的好处是你永远不会超出srclns
的范围:for(int i = 0; i < MAXSRC; ++i) { n = 0 if(getline(&srclns[i], &n, stream) == -1) { break; // We get out if we reached OEF } }
- 在您为
printf
访问 main 后释放所有分配的内存for(int i = 0; i < MAXSRC; ++i) { if(srclns[i] != NULL) { free(srclns[i]); } }
- 调整。我没有对代码进行测试,所以我可能犯了一些错误......请随时纠正它。您可能还想调整代码以满足您的需要。