为什么在此 K&R 示例中函数传递指针是有效的?

Why is it valid for the function to pass out a pointer in this K&R example?

我通过阅读 K&R(ANSI 版) 学习 C,辅以 21 世纪 C 。我会说我已经对指针的大部分基础知识非常有信心。这意味着我知道您必须非常小心地从一个函数中传递指针,这些指针既不是首先传递给它的,也不是 malloced 的。所以这个例子让我难住了。它来自 §5.6,第 109 页:

#define MAXLEN 1000 /* max length of any input line */
int getline(char *, int);
char *alloc(int);

/* readlines: readinputlines */
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]='[=10=]'; /* delete newline */
         strcpy(p, line);
         lineptr[nlines++] = p;
      }
   return nlines;
}

strcpy() 之前定义为:

/* strcpy: copy t to s; pointer version 3 */
void strcpy(char *s, char *t)
{
   while (*s++ = *t++)
      ;
}

我不明白 p 指向的内存如何在函数 returns 后保留在范围内。这就是我的理解。指针 p 在函数内声明 - 因此它在自动内存中。至于它指向什么,它没有明确分配任何内存。然后,当调用 strcpy 时,值将从 line 复制到 p 指向的任何位置。

我也在 21st Century C 中讨论了指针的危险,并给出了试图将在自动内存中声明的数组传递出函数的示例肯定不行。 (第 109 页:"A pointer to a block of memory that has already been automatically freed is worse than useless.")所以我能理解上述代码有效的唯一方法是声明 p 不是一个数组,其中一个内存块被显式分配到自动内存中,而是声明为一个指针,它的内存分配以某种其他方式隐式处理。那是对的吗?内存分配是如何工作的?

注意: 通过阅读 21st Century C 我试图消除任何不良做法 K&R 可能会向我介绍。我知道这不是最好的标准,而且我可能永远不会按照本例中演示的方式编写代码。不过我还是想了解一下。

p 是一个局部变量,它包含一个值,该值是在堆上分配的某个内存区域的地址。堆不是本地内存,不在栈帧中。堆分配的对象永远不会在经典 C 中自动收集,因此它们会持续存在并比任何调用和 returns 都长寿,直到被手动释放。请不要将堆分配的对象与驻留在堆栈中的本地数组混淆,它们不应该真正被传递出去。

确实,指针p本身驻留在自动内存中。但是,在评估以下 if 语句的条件期间,它被分配了一个内存块:

if (nlines >= maxlines || (p = alloc(len)) == NULL)

更具体地说,如果条件的第一项为假(因为 or 逻辑运算符是短路运算符),则计算第二项,执行内存分配并检查是否成功。

根据我对 K&R 的旧知识,alloc 函数是 malloc 的简化版本,在您到达此示例之前已在某处定义。但是,内存分配函数(例如标准 malloc,此处以 alloc 为例)的要点是允许您在堆上保留内存。堆是在程序执行期间定义的内存区域,它不会自动管理,即编译器不会对其进行任何操作,除非您明确告诉它这样做。这样,你就可以return/pass给指针p取值了。变量本身的内存没有多大用处;它的价值才是最重要的。它指向一个已分配的内存区域,只要你愿意,它就会一直留在那里(直到你明确释放它)。只要确保在释放该区域之前不会丢失该区域的内存地址即可。

旁注任何人都应尝试编译上述示例:在

while ((len = getline(line, MAXLEN)) > 0)
  • 函数getline()确实是同一本书第1.9节中定义的函数,而不是stdio.h

    的现代迭代中包含的函数
  • 这里的> 0其实应该是 > 1 ,因为K&R版本的getline() returns空串长度为1 , 即字符数组 { '[=17=]' }

此外,

if (nlines >= maxlines || (p = alloc(len)) == NULL)

也适用于标准 malloc