You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C语言链表addValue函数给指针赋值异常地址问题排查

问题背景

我正在自学Clang,从指针相关知识点开始入门。为了熟悉指针与内存地址操作,我编写了一个简单的链表程序进行实操练习:

#include <stdio.h>

struct Node
{
  double value;
  struct Node *nextNode;
};

struct LinkedList
{
  struct Node *initialNode;
};

int
addFirstElement (struct LinkedList *container, struct Node *newElement)
{
  container->initialNode = newElement;
  return 1;
}

int
add (struct LinkedList *container, struct Node *newElement)
{
  struct Node *iterator = container->initialNode;
  int addedCorrectly = 1;
  if (iterator == NULL)
    addedCorrectly = addFirstElement (container, newElement);
  else
    {
      while (iterator->nextNode != NULL)
    iterator = iterator->nextNode;
      iterator->nextNode = newElement;
    }
  return addedCorrectly;

}
/*Esta funcion no genera bien la dirección de memoria, pasa algo raro*/
int
addValue (struct LinkedList *container, double val)
{
  struct Node newElement = {val, NULL};
  return add (container, &newElement);
}

void
printList (struct LinkedList *container)
{
  struct Node *element = container->initialNode;
  printf("HEADER => ");
  while (element != NULL)
    {
      printf ("%f -> ", element->value);
      element = element->nextNode;
    }
    printf("NULL\n");
}

int
main ()
{
  struct LinkedList container = { NULL };
  struct Node val = { 15., NULL };
  struct Node val2 = { -19., NULL };
  struct Node val3 = {29., NULL};
  add(&container, &val);
  add(&container, &val2);
  add(&container, &val3);
  addValue(&container, 29.5);
  printList(&container);
}

程序运行一直正常,直到我编写了addValue函数,该函数的设计逻辑是创建一个Node结构体实例并将其添加到LinkedList结构体的尾部。但当我通过该函数创建新元素并调用printList打印链表时出现了异常。调试时发现反常现象:函数执行完成后,链表中出现了异常节点,其内存地址为0x7fffffffdda8,存储的值为6.9533558074010171e-310,nextNode指针指向0x402e000000000000的非法地址。

问题根因

这是典型的栈局部变量生命周期失效导致的悬空指针未定义行为:

  • C语言中函数内部定义的非静态局部变量,存储在当前函数的栈帧上,生命周期从变量定义开始,到当前函数返回时结束,函数返回后对应的栈内存会被回收,可被后续的函数调用覆写使用。
  • addValue函数中创建的newElement就是栈上的局部变量,把它的地址存入链表后,addValue返回时这个节点的内存就已经失效。后续调用printList时,这块原属于addValue栈帧的内存被printList的执行逻辑覆写,就出现了看到的垃圾值、非法指针现象。
  • 之前直接在main里定义val、val2、val3再传地址能正常运行,是因为main函数是程序入口,整个程序运行期间main的栈帧不会被回收,这些变量的内存全程有效,不存在悬空问题。
修复方案

可根据需求选择以下两种修复方式:

  • 方式1:在addValue中使用堆内存分配创建节点
    堆内存的生命周期由开发者手动控制,不会随函数返回自动回收,适合需要跨函数生命周期存储的数据:
    #include <stdlib.h> // 引入malloc对应的头文件
    
    int
    addValue (struct LinkedList *container, double val)
    {
      struct Node *newElement = malloc(sizeof(struct Node));
      if (newElement == NULL) return 0; // 处理内存分配失败的边界情况
      newElement->value = val;
      newElement->nextNode = NULL;
      return add (container, newElement);
    }
    

    注意:所有通过malloc申请的堆内存,在链表使用完毕后需要手动遍历链表逐个调用free释放,否则会出现内存泄漏。

  • 方式2:保持现有内存管理逻辑,要求调用方传入生命周期足够长的节点地址
    即不在addValue内部创建节点,只负责接收外部传入的、已经分配好内存的节点指针,和现有add函数的设计保持一致,这种方式不需要工具函数处理内存释放逻辑,但需要调用方自行保证节点内存的有效性。

内容的提问来源于stack exchange,提问作者user14104509

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.29 10:09:21