我是C的新手,我正在阅读K& R的“The C Programming Language”来学习它。我对第2版第109页上出现的这个示例函数有疑问:
/* readlines: read input lines */
int readlines(char *lineptr[], int maxlines)
{
int len, nlines;
char *p, line[MAXLEN];
nlines = 0;
while ((len = getline(line, MAXLEN)) > 0)
if (nlines >= maxlines || p = alloc(len) == NULL)
return -1;
else {
line[len-1] = '\0'; /* delete newline */
strcpy(p, line);
lineptr[nlines++] = p;
}
return nlines;
}
我想知道为什么*p
在这里是必要的? p
被分配内存,然后line
被复制到其中。为什么不能只使用line
,所以最后lineptr[nlines++] = p
可以替换为lineptr[nlines++] = line
。
答案 0 :(得分:1)
如果你没有为每一行分配内存,你最终将lineptr
作为一个数组,其中包含指向你读取的最后一行的指针(更不用说堆栈内存可能是覆盖)。在读取时为每一行分配内存会使返回的数组有意义。举个例子,假设line
恰好在地址0x1000的堆栈上分配。如果您进行了建议的更改,则生成的8行文件的lineptr
数组将为:
0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000
Yowch!在读取每行时为每行分配内存,然后将行复制到分配的内存中是唯一的解决方案。
答案 1 :(得分:1)
lineptr[nlines++] = line;
会向lineptr
填充指向该函数本地内存的指针,并且该函数返回后该内存将变为无效。 lineptr
数组的所有元素的值都相同且等于line
。
所以这里需要分配。您确实需要将line
的内容复制到新分配的内存位置,该位置在函数返回后仍然存在。
答案 2 :(得分:1)
你需要为每一行分配存储。你不能像你建议的那样捕获line
的值,因为那是一个本地变量函数返回后的范围(在这个特定的例子中,它在每次迭代时被覆盖)。
你可以通过line
直接对getline
的元素(你将随时分配)来避免lineptr
,但是你无法摆脱p
答案 3 :(得分:1)
需要为每一行分配一大块新内存。指针p
是我们对该内存的唯一句柄。我们指定lineptr[nlines++] = p
,以便我们可以引用每个内存块(例如行)作为数组lineptr
的一部分。
答案 4 :(得分:1)
c / c ++中lineptr[nlines++] = p
种类的分配,将lineptr[nlines++]
的地址设置为p
所指向的地址,此处不会复制数据。
因此,line
的地址始终是相同的地址;所以lineptr[nlines++] = line
意味着所有lineptr[i]
都指向同一个地址。
最糟糕的是,在函数返回后,line
不再存在,因此每个lineptr[i]
都指向一个无效的地址。
使用p
为每一行分配新内存,并确保该内存的地址在函数之间仍然有效(直到你释放它为止)。
答案 5 :(得分:0)
好的,让我们告诉你如何在没有char *p
的情况下做同样的事情......我将略微修改代码。
/* readlines: read input lines */
#include <string.h>
/* put that include line below #include <stdio.h> if you don't already have this
string.h defines strdup() function which we use below */
int readlines(char *lineptr[], int maxlines)
{
int len, nlines;
char line[MAXLEN];
nlines = 0;
while ((len = getline(line, MAXLEN)) > 0) {
line[len-1] = '\0'; /* delete newline */
lineptr[nlines] = strdup(line); /* allocate memory and make a copy */
if (lineptr[nlines] == NULL) {
return -1;
}
nlines++;
if (nlines >= marlines)
break;
}
return nlines;
}
此代码最接近,不使用临时char *p
。
事实是,虽然这在功能上是正确的,但使用临时变量char *p
来测试所有分配和检索使代码更清晰,易于遵循教学目的。它还明确地将字符串内存的分配显示为单独的步骤,隐藏在strdup
。