C - 堆栈分配的链表

Posted

技术标签:

【中文标题】C - 堆栈分配的链表【英文标题】:C - Stack allocated linked list 【发布时间】:2017-12-23 23:53:27 【问题描述】:

我使用以下代码的目标是让用户输入一些整数,将这些整数存储在堆栈分配的 INT_NODE 类型的节点中,然后将所有这些节点链接在一起。最后,我想遍历列表并打印出每个节点处的元素(仅以下代码中的前 5 个)。但是,当我输入一些数字时,程序会打印出我输入的第一个数字,然后我输入的最后一个数字会重复 4 次。例如,如果我输入84 5 12 7 1 22 31[Enter],然后在下一行的开头按Ctrl+D 在这台Mac 上模拟EOF,我得到以下输出; 84 31 31 31 31。我无法弄清楚它为什么这样做。

我知道我可以使用malloc() 分配堆上的节点,并且我已经编写了一个函数来执行此操作。我只是想知道是否可以使用运行时堆栈来做到这一点。

在以下代码中,INT_NODE 类型在"SortingAlgs.h" 标头中定义如下;

typedef struct INT_NODE 
    int element;
    struct INT_NODE *next;
 INT_NODE;

#include <stdio.h>
#include <stdlib.h>
#include "SortingAlgs.h"

int main(void) 

    INT_NODE head = -999999999;
    int num;
    INT_NODE *pCurrentNode = &head;

    if (scanf("%d", &num) != EOF) 
        head.element = num;

        while (scanf("%d", &num) != EOF) 
            INT_NODE newNode;

            newNode.element = num;
            newNode.next = NULL;
            pCurrentNode->next = &newNode;
            pCurrentNode = pCurrentNode->next;
        
     
    int i;
    for (pCurrentNode = &head, i = 0; i < 5;
         pCurrentNode = pCurrentNode->next, i++)

        printf("%d  ", pCurrentNode->element);

    printf("\n");

    return 0; 

【问题讨论】:

谁会为此分配足够的内存? 要使用运行时堆栈,您需要 1) 通过alloca 进行非标准运行时分配或 2) 递归函数,其中每个递归级别将承载一个列表节点。就是这样。你现在拥有的东西是不可行的,只会导致未定义的行为。当然,您可以简单地将固定数量的节点预先分配为本地数组,并希望它足以满足您的列表...但我确定这不是您的意思。 INT_NODE newNode; :超出范围无效。 您每次在 while 循环中创建和销毁 yr 节点。必须在堆上做 请注意,您的打印循环停止的唯一原因是i &lt; 5 术语。对于真正的链表,您将继续前进,直到到达具有空下一个指针的节点(最常见的情况)。如果您尝试这样做,您很可能会得到31 的“永远”生产(直到您感到无聊并杀死您的程序)。这是你的问题的征兆。 【参考方案1】:

要使用运行时堆栈,您可以使用以下方法之一:

    通过alloca 进行运行时分配。这很简单:基本上只需将malloc 替换为alloca(并且不要尝试将alloca 包装到另一个函数中)。但是,alloca 不是标准函数。 递归函数,其中每个递归级别将承载单个列表节点(或固定数量的列表节点)。这有一些严重的限制,但从技术上讲,它满足您在堆栈上分配所有节点的要求。 预先分配固定数量的节点作为本地数组,并希望它足以满足您的列表...但我确定这不是您的意思。

就是这样。你现在拥有的东西是不可行的,只会导致未定义的行为。您的列表基本上“包含”INT_NODE 生命周期已经结束的对象。在实践中,您通常会一次又一次地重复使用相同的内存位置,从而有效地将单个节点链接到自身。

这是一个示例,如果一个实现遵循递归方法将整个列表“放在堆栈上”。当然,该列表仅在所有递归调用都“活动”时才存在。这限制了这种技术的适用性,但它有它的用途

#include <stdlib.h>
#include <stdio.h>

typedef struct INT_NODE 

  int element;
  struct INT_NODE *next;
 INT_NODE;

void print_list(const INT_NODE *head)

  for (const INT_NODE *current = head; current != NULL; current = current->next)
    printf("%d  ", current->element);
  printf("\n");


void build_list(INT_NODE *head, INT_NODE *last)

  INT_NODE new =  0 ;

  if (scanf("%d", &new.element) != 1)
  
    print_list(head);
    return;
  

  if (head == NULL)
    head = &new;
  else
    last->next = &new;

  build_list(head, &new);


int main(void) 

  build_list(NULL, NULL);

http://coliru.stacked-crooked.com/a/5a4f15a82c66d992

【讨论】:

哦,不,我要给绿色复选标记的答案不见了。 :(【参考方案2】:

我认为原因是变量的范围。 输入5,程序在栈上分配内存,但是会被返回。 输入12,分配相同的内存,返回。 所以最后,head next 指针是同一个内存,同一个内存 next 指针就是它自己。

你可以输出超过 5 个。也许你会得到 84 31 31 31 31 31 31 31 .....

【讨论】:

只有在您确定的情况下才发布答案。

以上是关于C - 堆栈分配的链表的主要内容,如果未能解决你的问题,请参考以下文章

如何将数据动态存储在C中的链表中?

C:我对堆和堆栈分配细节的理解是不是正确?

在未正确分配函数中初始化的链表结构

C即使分配了内存,修改链表的程序也会导致分段错误

c语言堆栈是啥意思?

Buddy(伙伴)系统分配器之分配page