了解内存分配的C代码示例

我是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代替。

如果你没有为每一行分配内存,你最终会得到一个数组,其中包含指向你读取的最后一行的指针(更不用说可能被覆盖的堆栈内存)。 在读取时为每一行分配内存会使返回的数组有意义。 举个例子,假设该line恰好在地址0x1000的堆栈上分配。 如果进行建议的更改,则生成的8行文件的lineptr数组将为:

 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000 

Yowch! 在读取每行时为每行分配内存,然后将行复制到分配的内存中是唯一的解决方案。

 lineptr[nlines++] = line; 

将使用指向该函数本地的内存的指针填充lineptr ,并且该函数返回后该内存将变为无效。 lineptr数组的所有元素的值都将相同并且等于line

所以这里需要分配。 您确实需要将行的内容复制到新分配的内存位置,该位置在函数返回后仍然存在。

您需要为每条线路分配存储空间 你不能像你建议的那样捕获line的值,因为这是一个局部变量,它会在函数返回后超出范围(在这个特定的例子中,它会在每次迭代时被覆盖)。

你可以通过将getline直接放入lineptr的元素(你可以lineptr分配)来避免line ,但是你无法摆脱p

需要为每一行分配一大块新内存。 指针p是我们对该内存的唯一句柄。 我们分配了lineptr[nlines++] = p因此我们可以引用每个内存块(例如行)作为数组lineptr一部分。

在c / c ++中分配lineptr[nlines++] = p ,将lineptr[nlines++]的地址设置为p指向的地址,此处不复制数据。

所以,行的地址始终是相同的地址; 所以lineptr[nlines++] = line意味着所有lineptr[i]都指向同一个地址。 最糟糕的是,在函数返回后, line不再存在,所以每个lineptr[i]然后指向一些无效的地址。

使用p为每一行分配新的内存,并确保该内存的地址在函数之间仍然有效(直到你释放它)。

好的,让我们告诉你如何在没有char *p的情况下做同样的事情…我将略微修改代码。

 /* readlines: read input lines */ #include  /* put that include line below #include  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使用。

问题在于,虽然这在function上是正确的,但使用临时变量char *p来测试所有分配和检索使代码更清晰,易于遵循教学目的。 它还明确地将字符串内存的分配显示为单独的步骤,该步骤隐藏在strdup