【问题标题】:Linked Lists in C without mallocC中没有malloc的链表
【发布时间】:2011-04-20 20:33:45
【问题描述】:
#include <stdio.h>

typedef struct node
{
      int i;
      struct node *next;
}node;

node getnode(int a)
{
      struct node n;
      n.i=a;
      n.next=NULL;
      return n;
}

main()
{
     int i;
     node newtemp,root,temp;

     scanf("%d",&i);
     root=getnode(i);
     temp=root;

     while(i--)
     {
         newtemp=getnode(i);

         temp.next=&newtemp;
         if(root.next==NULL)
         {
            root=temp;
         }
        temp=*(temp.next);
     }


     temp=root;

     while( temp.next != NULL )
     {
         printf(" %d ",temp.i);
         temp=*(temp.next);
     }
}

我正在尝试在不使用 malloc 的情况下创建链接列表。编程只打印根,后面没有节点。我找不到错误。如果有任何内存问题,gcc 编译器会抛出分段错误。(?)请忽略糟糕的编程风格..

【问题讨论】:

  • 不使用malloc的链表?这甚至可能吗?
  • 为什么?我不确定,但是当我们有堆栈分配和定义良好的复制构造函数时为什么不能呢???
  • 欢迎来到 SO :) 您可以并且应该使用“010101”按钮或 4 空格缩进将您的代码 sn-ps 标记为代码。我刚刚为你做了。
  • @gablin:当然。您可以静态声明一个节点数组,并将其用作内存池。它假设您知道节点数的上限是多少,但这是一种完全有效的方法(当我在大学里做 Fortran 77 时,它是 only 方法)。 malloc()/free() 为您提供更多灵活性,但并非绝对必要。
  • 这完全是因为不愿意检查 malloc() 返回值为零并实现内存不足逻辑,不是吗? :)

标签: c malloc linked-list


【解决方案1】:

当你初始化temp.next时,你分配给它的指针的值是多少?

temp.next=&newtemp;

为什么,每次都是一样的! (不服的就画个图。)

放弃吧。如果您需要不确定数量的内存(您需要的节点数量不确定),那么您需要分配内存。

【讨论】:

    【解决方案2】:

    你可以避免 malloc 但不是免费的:

    • 在 Linux/UNIX 上,您可以调用 brk() 并编写自己的内存分配器。
    • 在每个系统上,您都可以使用固定大小的数组作为内存源编写自己的分配器。
    • 我看不出有什么替代品会直接取代 malloc/free。他们在那里是有原因的。
    • 返回要在外部使用的局部变量会很简单,但会出错并且不起作用。

    【讨论】:

    • “你可以避免 malloc 但不是免费的”是一个非常糟糕的双关语:D
    • 编写自己的分配器的主要原因是当您有特定的需求时。这样你就可以获得一个非常适合你的程序的分配器。但大多数时候,这不值得。
    【解决方案3】:

    您只有两个可用于存储节点的内存空间,它们是rootnewtemp。当您将新节点分配给 newtemp 时,旧节点不再存在。

    假设您在 scanf 中输入数字 5,在循环的第一次迭代之前,您有:

    5 -> NULL
    

    循环的第一次迭代后,你有

    5 -> 4 -> NULL
    

    循环的第二次迭代后,你有

    5 -> 3 -> NULL
    

    (包含4的节点已被包含3的节点完全替换)。

    唯一的解决方案是使用malloc,并使getnode 返回node*

    【讨论】:

    • 严格来说,您可以将节点数组放在堆栈上或将它们声明为静态,并让您的函数从数组中拉下一个节点,而不是使用malloc
    • @R。你说的对。我为stackoverflow.com/questions/3002764/… 想出的一些技巧也可以在这里使用。
    【解决方案4】:

    您可以静态声明节点结构数组并从该数组中选择新节点。但是那样你就会实现一个蹩脚的、可悲的专用自定义分配器。可用节点的最大数量将是该数组的大小。

    或者,您可以使用递归作为分配机制,并对递归堆栈底部的列表执行操作。

    这两种方法都差不多。

    【讨论】:

    • 您实际上缺少一个,它比这两个都更容易使用:VLA。
    • @Nathan:对不起,变长数组,请看我的回答
    【解决方案5】:

    当然,您可以构建链表或任何其他无需动态内存分配的数据结构。 但是,无论您多么努力,都无法构建它根本不分配内存。

    替代方案:

    创建一个全局或静态内存池,您可以在其中放置对象,模仿 heap/malloc/free。 FreeRTOS 做了类似的事情。 在这种情况下,您将拥有一个静态分配的大内存块,因为您的程序开始 并且 将负责管理它,在需要新节点时返回正确的指针并将该内存标记为已使用.

    P.S.:我不会质疑您为什么要避免使用 malloc。我想你有一个很好的理由。

    在你的程序中,当你这样做时,在第 20 行:

         node newtemp,root,temp; 
    

    您正在分配三个节点,其中之一是“newtemp”。 然后你在第 28 行做:

         newtemp=getnode(i); 
    

    它只是将返回节点的内容复制覆盖在旧的“新节点”上(有争议,不是吗?)

    所以你做,只是咆哮:

         temp.next=&newtemp; 
    

    这会将最初来自“root.next”的指针设置为您的“newnode”。

         if(root.next==NULL) 
    

    在第一次传递时将是“NULL”,但随后只替换相同的内容。

        temp=*(temp.next); 
         { 
            root=temp; 
         } 
    

    再次将数据从单个分配的对象“*(temp.next)”复制到另一个对象“temp”。 它不会创建任何新节点。

    如果你这样做,它会起作用:

    #include <stdio.h>
    
    typedef struct node 
    { 
        int i; 
        struct node *next; 
    }
        node; 
    
    #define MAX_NODES   10
    
    node    *create_node( int a )
    { 
        // Memory space to put your nodes. Note that is is just a MAX_NODES * sizeof( node ) memory array.
        static node node_pool[ MAX_NODES ];
        static int  next_node = 0;
    
        printf( "[node  *create_node( int a )]\r\tnext_node = %d; i = %d\n", next_node, a );
        if ( next_node >= MAX_NODES )
        {
            printf( "Out of memory!\n" );
            return ( node * )NULL;
        }
    
        node    *n = &( node_pool[ next_node++ ] );
    
        n->i = a;
        n->next = NULL;
    
        return n; 
    } 
    
    int main( )
    { 
        int i; 
        node    *newtemp, *root, *temp; 
    
        root = create_node( 0 ); 
        temp = root; 
    
        for ( i = 1; ( newtemp = create_node( i ) ) && i < MAX_NODES; ++i )
        { 
            temp->next = newtemp; 
    
            if ( newtemp )
            {
                printf( "temp->i = %d\n", temp->i );
                printf( "temp->next->i = %d\n", temp->next->i );
                temp = temp->next;
            }
        }
    
    
        for ( temp = root; temp != NULL; temp = temp->next )
            printf( " %d ", temp->i );
    
        return  0;
    }
    

    输出:

        $ gcc -Wall -o list_no_malloc list_no_malloc.c 
    
    $ ./list_no_malloc
    [node   next_node = 0; i = 0)]
    [node   next_node = 1; i = 1)]
    temp->i = 0
    temp->next->i = 1
    [node   next_node = 2; i = 2)]
    temp->i = 1
    temp->next->i = 2
    [node   next_node = 3; i = 3)]
    temp->i = 2
    temp->next->i = 3
    [node   next_node = 4; i = 4)]
    temp->i = 3
    temp->next->i = 4
    [node   next_node = 5; i = 5)]
    temp->i = 4
    temp->next->i = 5
    [node   next_node = 6; i = 6)]
    temp->i = 5
    temp->next->i = 6
    [node   next_node = 7; i = 7)]
    temp->i = 6
    temp->next->i = 7
    [node   next_node = 8; i = 8)]
    temp->i = 7
    temp->next->i = 8
    [node   next_node = 9; i = 9)]
    temp->i = 8
    temp->next->i = 9
    [node   next_node = 10; i = 10
    Out of memory!
     0  1  2  3  4  5  6  7  8  9 
    

    【讨论】:

    • 两个词 - “自定义分配器” - 就足够了。这避免了 malloc() 在字面上但不是在精神上。
    【解决方案6】:

    链表是不定长的东西,没有malloc就不能创建任何不定长的东西。

    我建议你简单地使用 malloc 来分配链中的下一个链接。

    【讨论】:

    • 是的,这是我们所做的正常工作。我试图在不使用 malloc 的情况下实现它。没有malloc,我们的生活会变得不可能吗?请帮忙。
    • 是的,没有 malloc 是不可能的。
    【解决方案7】:

    使用 malloc 时,指向该位置的“指针”将传递给变量(这是一个指针)。

    node* new = (node*)malloc(sizeof(node));
    

    每次使用此代码时,都会指向一个“新”位置。相反,您使用的是具有“静态”分配内存的普通变量。这意味着,每当您提到“temp”或“newtemp”时,您每次都指的是同一个位置。

    现在您告诉我如何仅使用 3 个(根、临时、新临时)内存位置来存储 10 个元素的长列表。你可能想画出内存位置来想象正在发生的事情。但请记住,每次调用“temp”时,它的内存位置都相同。为此,我们必须使用 malloc(或 calloc)来动态分配内存。

    在这种情况下,我们可以使用少量指针(远小于列表使用的内存)。

    【讨论】:

      【解决方案8】:

      由于没有人本着现代 C(又名 C99)的精神回答这个关于 malloc 部分的问题。如果你的编译器符合你有可变长度数组

      node myNodes[n];
      

      其中n 具有一些仅在运行时确定的值。您可能不应该过度使用这种方法,因为这仅限于堆栈的大小,否则您可能会遇到堆栈溢出。

      【讨论】:

      • 仍然,可用节点的数量限制在数组初始化时的 n 值。这些 VLA 不是动态增长的,是吗?它只是围绕 malloc() 调用的语法糖,不是吗?
      • @Seva:不,它们不是动态增长的,但这不是问题所在。不,它们不是 malloc 周围的糖,但通常分配在堆栈上而不是堆上。
      • alloca() 周围的语法糖,然后 :)
      • @Seva:也不完全是,因为首先这种分配的范围是范围而不是功能。然后alloca 不是标准 C 的一部分,因此比假设 VLA 和 C99 更不便携;)
      • 嗯,分配范围是一个实现细节,不是吗?可以确定 VLA 直到初始化的那一刻才被分配(因为所需的大小只有在那时才知道),但是你怎么知道当函数返回时它没有与堆栈帧的其余部分一起被释放?这就是 alloca() 的行为 IIRC。
      猜你喜欢
      • 1970-01-01
      • 2023-01-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-04-27
      • 1970-01-01
      • 2011-10-03
      相关资源
      最近更新 更多