【问题标题】:c - creating a linked list without mallocc - 创建一个没有 malloc 的链表
【发布时间】:2013-11-18 18:57:11
【问题描述】:

为了创建一个链表(其中将包含下一个和上一个节点的属性),我将使用两个下一个和上一个节点的指针,但我想知道我是否可以在不使用 malloc 的情况下完成代码(分配内存): 例如: 而不是 malloc-ing:

link *const l = (link *)malloc(sizeof(link));

if(l == NULL)
  /* Handle allocation failure. */
  ...

l->data = d;
l->next = list->head;

head = l;

我可以简单地创建一个具有格式化属性的新链接变量(值、指向下一个和上一个链接的指针),然后简单地将链中最后一个链接中的最后一个链接链接到这个链接吗? 例如,我的列表文件是 b。

link i;
i.date=d;
getlast(b).next=&i

我提前致歉,因为我是 c 新手,并且很高兴收到诚实的解决方案:D

编辑: 我尝试使用 malloc 来解决问题。如果有人能解决我在代码中的错误,我会很高兴,因为我似乎找不到它。

#include <stdio.h>
#include <malloc.h>

struct Node{
    int value;
    struct Node * Next;
    struct Node * Previous;


};
typedef struct Node Node;
struct List{
    int Count;
    int Total;
    Node * First;
    Node * Last;
};
typedef struct List List;
List Create();
void Add(List a,int value);
void Remove(List a,Node * b);
List Create()
{
    List a;
    a.Count=0;
    return a;

}

void Add(List a,int value)
{
    Node * b = (Node *)malloc(sizeof(Node));
    if(b==NULL)
        printf("Memory allocation error \n");
    b->value=value;
    if(a.Count==0)
    {
        b->Next=NULL;
        b->Previous=NULL;
        a.First=b;

    }
    else
    {
        b->Next=NULL;
        b->Previous=a.Last;
        a.Last->Next=b;

    }
    ++a.Count;
    a.Total+=value;
    a.Last=b;
    }
void Remove(List a,Node * b)
{
    if(a.Count>1)
    {
        if(a.Last==b)
        {
            b->Previous->Next=NULL;
        }
        else
        {
            b->Previous->Next=b->Next;
            b->Next->Previous=b->Previous;
        }

        }
    free(b);
    }

【问题讨论】:

  • 你可以吗?当然。您是否想要是有争议的,因为还有其他强大的方法可以将“链接”列表保存在自动存储中,并且按照您的方式进行操作有很多缺点。一个节点 array 包括数据和数组中下一个节点的索引就是这样的例子)。基于指针的链表的基本目的是提供动态扩展,我并不完全相信你对此很清楚。
  • 这总是会在有人这样做时发布,但不要强制转换malloc的返回值。
  • 您的代码中的问题是“当该函数返回时,'i' 会去哪里?”
  • 想想为什么传统的方法是使用malloc。您的版本是否解决了这些原因?
  • 我为我的无知提前道歉;我明白使用指针的目的。我不明白如果我重用 i 变量会发生什么:我正在内存中创建一个节点。之后所以,我将列表中的最后一个节点链接到内存中创建的这个新节点。如果我重用 i 变量会发生什么,为什么我必须使用 malloc?

标签: c malloc


【解决方案1】:

是的 - 你可以这样做。

例如

link l1,l2;
l1.next = &l2;
l2.next = NULL;

是一个完美且有效的 2 个节点的链表。 您还可以创建一堆节点,并根据您的需要将它们链接在一起,例如创建 argv 的链表:

 int main(int argc, char *argv[])
       int i;
       link links[100];
       for (i = 0; i < argc && i < 100; i++) {
          //assuming the nodes can hold a char*
           links[i].data = argv[i]; 
           links[i].next = NULL;
           if (i > 0)
              links[i-1].next = &links[i];
        }

当然也有一些缺点:

  • 在这些示例中,节点数是在编译时确定的。 (在最后一个示例中,可以为argc 分配一个缓冲区 节点而不是硬编码 100)
  • 这些节点的生命周期是它们被声明的作用域,当作用域结束时它们不再存在。

所以你不能这样做:

 void append_link(link *n, char *data)
 {
      link new_link;
      n->next = &new_link;
      new_link.next = NULL;
      new_link.data = data;
 }

这是无效的,因为当append_link 结束时,new_link 就消失了。而传入的n-&gt;next 现在指向一个无效的局部变量。如果 new_link 被 malloc 了,它将超出这个功能 - 一切都很好。

【讨论】:

  • 好的。我用我的新代码解决方案用 malloc 编辑了我的问题。你能指出我的错误吗,因为我似乎找不到它?(到目前为止它只包括插入和删除
  • @user3005945 不要那样做。您的问题现在完全不同了,因此您应该为它创建一个新问题。
【解决方案2】:

不是真的。

您可以为列表中的每个节点创建一个变量,但是当您想要另一个节点时会发生什么?还有五十个节点?在您离开定义它们的范围后,这些变量也不会保留,这意味着您要么必须将所有内容设为全局,要么使用静态存储并公开指向它们的指针。这意味着在该范围之后指向它们的所有指针都将无效。这些都是非常丑陋的解决方案。

如果你不明白我所说的范围是什么意思,这里有一个简单的例子:

int main() { /* Entering function scope. */
    int x = 5;
    { /* Entering block scope. */
        int y = 7;
        printf("%d\n", y);
    } /* Exiting block scope, all variables of this scope are gone. (y) */
    printf("%d %d\n", x, y); /* Won't compile because y doesn't exist here. */
} /* Exiting function scope, all non-static storage variables are gone. (x)

您也可以创建一个全局数组,认为这可以解决很多不同的变量,但如果您的解决方案是使用数组来实现,为什么要使用链表而不是数组?至此,您已经失去了链表的好处。

【讨论】:

  • 感谢您的回复,只是为了确保我理解问题:当我在函数内部创建一个变量并创建一个指向该变量的指针时,我实际上是指向变量本身而不是指向变量所在的内存块。这就是为什么当函数终止(结束)时,变量不再存在?
  • 嗯,没有。您仍然指向变量使用的内存。问题是变量通常存储在堆栈中,堆栈是您不拥有的一段内存,除了该块或函数的持续时间。具体如何发生取决于编译器,但您无法保证该范围之外的内存——您的编译器可能会决定重用该内存!
  • 这是create-linked-list-no-malloc.c 示例,它向用户隐藏了将数组用作存储的事实。使用自定义分配器并不少见。一些 c++ stl 容器可以识别分配器,例如 forward_list&lt;&gt;
  • 是的,你可以做到,但如果你没有理由,为什么还要麻烦呢?任意插入和删除并不像您需要手动跟踪(或搜索)空闲插槽那么简单。随着列表变大并且元素变得不那么有序,空间局部性的好处就会减少 - 所以现在您必须定期重新排列元素。如果您从一个大列表开始并且删除比插入更多,则可能会造成浪费。成长更多的是一种痛苦。具有固定内存和用例的嵌入式系统?绝对地。具有动态大小的通用 PC?并非没有充分的理由。
  • 我会稍微修改一下这个答案,以强调“你可以但可能不应该,除非......”一面。
【解决方案3】:

在 C 中只有两种方法可以创建没有固定编译时大小的内存数据结构:

  • 具有分配的存储持续时间,即通过malloc
  • 带有自动存储持续时间,就实现而言,意思是“在堆栈上”,可以使用可变长度数组或递归(这样您就可以在每个递归级别获得一个新实例) .

后者(自动存储)具有生命周期在其声明的块执行终止时结束的属性,因此很难用于长期数据。您可以获得的此类存储量通常也有限制,并且无法检测何时超出该限制(通常这会导致崩溃或内存损坏)。所以从实际的角度来看,malloc 是制作运行时动态大小的数据结构的唯一方法。

请注意,如果您的链表不需要具有动态大小(即它是固定大小或有界大小),您也可以为其使用静态存储持续时间。

【讨论】:

  • 好的。我用我的新代码解决方案用 malloc 编辑了我的问题。你能指出我的错误吗,因为我似乎找不到它?(到目前为止它只包括插入和删除
【解决方案4】:

新节点的内存必须来自somwhere。您当然可以创建单个变量并手动链接它们:

link a, b, c;
...
a.next = &b;
b.next = &c;
c.next = NULL;

您可以想象,这种方法无法扩展;如果您想要列表中的元素超过 3 个,则必须分配超过 3 个 link 变量。请注意,以下不会起作用:

void addToList( link *b )
{
  link new;
  ...
  b->next = &new;
}

因为当addToList退出时,new不再存在,所以指针不再有意义1

可以做的是使用link 的数组作为您的“堆”,并从该数组进行分配。您需要跟踪哪些元素可供使用;一种简单的方法是初始化数组,使每个a[i] 指向a[i+1](最后一个元素除外,它指向NULL),然后有一个指向第一个可用元素的指针。类似于以下内容:

// You really want your "heap" to have static storage duration
static link a[HEAP_SIZE];

// Initialize the "heap"
for ( size_t i = 0; i < SIZE - 1; i++ )
  a[i].next = &a[i+1];
a[i].next = NULL;

// Set up the freeList pointer; points to the first available element in a
link *freeList = &a[0];  

// Get an element from the "heap"
link *newNode = freeList;
freeList = freeList->next;
newNode->next = NULL;

// Add a node back to the "heap" when you're done with it:
deletedNode->next = freeList;
freeList = deletedNode;

同样,您可以创建的列表节点数量是有限的,但是通过这种方式,您可以创建一个足够大的“堆”来满足您的要求。


1。显然,new 占用的物理内存位置仍然存在,但它现在可供其他进程/线程使用,因此该地址中包含的值将不再是您所期望的。

【讨论】:

  • 好的。我用 malloc 的新代码解决方案编辑了我的问题。你能指出我的错误吗,因为我似乎找不到它?(到目前为止它只包括插入和删除)跨度>
  • 对于AddRemove 函数,您需要将a 作为指针 传递给List。请记住,C 传递所有函数参数按值,因此形参与实参在内存中是不同的对象;对形式参数所做的任何更改都不会反映在调用者中。
  • okay.also,c# 中的列表如何在没有任何 mallocing 的情况下运行?(例如:pastebin.com/9VU5BAx5 注意到插入)另外,我解决了代码的问题,没有将 a 作为指向列表的指针传递,但作为实际列表
  • @user3005945:C 和 C# 是不同的语言;对一个人正确的事情不一定对另一个人正确。
猜你喜欢
  • 2011-04-20
  • 1970-01-01
  • 2023-01-22
  • 1970-01-01
  • 1970-01-01
  • 2017-04-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多