在C中,我可以对链表中的一些节点使用分配,而另一些节点则不使用分配吗?

gstyhher  于 2023-10-15  发布在  其他
关注(0)|答案(3)|浏览(104)

我不明白为什么必须使用指针然后使用动态内存来分配结构。有没有什么东西可以阻止我这样做:

typedef struct node {
    char name[100];
    int age;
    struct node *next;
  
} node_t ;

int main(){
    
    node_t *head = NULL;
    node_t person2 = {"Alex", 15, NULL};
    head = person2.name;
    return 0;
}

而不是这个:

typedef struct node {
    char name[100];
    int age;
    struct node *next;
  
} node_t ;

int main(){

    node_t *head = NULL;

    node_t *person1;
    person1 = (node_t*)malloc(sizeof(node_t));
    person1->next = NULL;
    head = person1;
    return 0;
}

我知道我们分配内存的地方是不同的,但在一个实际的方式,这两种方式工作不同吗?我的问题的原因是因为我只看到指针malloc方式在每一个例子在线。
我可以同时使用这两种方法吗?
在这里,我要么将整个节点指向下一个节点:

head = person1;

下一个节点可以指向下一个节点的名称,例如:

person1->next = person2.name;

如果我没有弄错的话,当我们指向一个动态内存地址时,我们指向的是第一个数据类型的字节,在这种情况下,将是name[0] byte。

1cklez4t

1cklez4t1#

主要问题是知道如何以及何时释放链表使用的内存--用malloc分配的内存必须用free释放,而在堆栈上分配的内存不能用free释放,当包含帧退出时,内存将自动释放。更糟糕的是,将这样的指针传递给free或在帧退出后尝试使用它会导致未定义的行为,这两种情况可能一开始看起来都有效,但后来会导致问题。
一种可能性是用单个位标志onHeap标记每个节点,该标志为分配有malloc的节点设置,并为其他节点清除。这样你至少知道给定的节点是否应该传递给free。这仍然无助于解决在堆栈上分配的节点的悬挂指针在列表上时被释放的可能性。
另一种可能性是在堆上“正常”分配节点,但在极少数情况下,您希望在同一范围内分配和释放节点,您可以在堆栈上执行。所以你可能有这样的代码:

void func(struct node *list) {  // list (probably) on the heap
    struct node local;  // a new node we'll prepend
    // init fields of local with some data
    local.next = list;
    func2(&local);   // some other function
}

在这里,调用this的代码“拥有”传入的列表,并可能以任何方式分配它(使用malloc或其他方式分配堆),并以相同的方式清理它。然后func只拥有它要传递给func 2的单个节点(它在列表中不拥有任何东西,所以不会释放任何东西),而func在返回时自动释放local。在这里使用堆栈可以被看作是分配的局部优化。

332nm8kg

332nm8kg2#

寿命是个问题。如果你的节点是在main中自动声明的,你可以避开这个问题,但是一旦节点开始在其他函数中产生,这个问题就会出现。
假设我们创建一个create_node函数,它接受一个int值和一个指向下一个节点的指针,并返回一个新节点。

struct node {
    int value;
    struct node *next;
};

struct node *create_node(int value, struct node *next) {
    struct node new_node = {.value=value, .next=next};
    return &new_node;
}

这将返回一个指向局部变量的指针。未定义的行为。
如果我们动态地分配内存,节点的寿命会超过函数的作用域。

struct node *create_node(int value, struct node *next) {
    struct node *new_node = malloc(sizeof(struct node));
    if (!new_node) return NULL;
    
    new_node->value = value;
    new_node->next = next;

    return new_node;
}

我们现在可以安全地写这样的东西:

struct node *prepend_value(struct node *list, int value) {
    struct node *new_node = create_node(value, list);

    return new_node;
}
wsxa1bj1

wsxa1bj13#

在第一个代码片段中,

head = &person2;

而不是

head = person2.name;

当然,你可以使用局部变量作为列表的节点。但是在这种情况下,如果列表需要存储大量的节点,那么你将需要声明不同的大量局部变量来表示节点,这是低效的,并且使你的程序非常庞大,无法读取。

相关问题