了解内存分配的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
。