为啥这个基本链表可以在 MacOS 上工作,但在 Linux 上会出现段错误

Posted

技术标签:

【中文标题】为啥这个基本链表可以在 MacOS 上工作,但在 Linux 上会出现段错误【英文标题】:Why does this basic linked list work on MacOS but seg fault on Linux为什么这个基本链表可以在 MacOS 上工作,但在 Linux 上会出现段错误 【发布时间】:2021-08-02 05:26:32 【问题描述】:

我有一个链表库,我偶尔在 MacOS 上使用,我只是尝试在 Linux 上使用它,但遇到了各种各样的问题。我已经把它分解成一个更简单的版本来解决问题。我已经找到了gdb 的问题所在,我只是不知道为什么会这样。即使使用地址消毒剂,它也可以在 MacO 上正常工作。我怀疑我可能以某种方式滥用了这里的指针。

这是我的列表结构:

struct node 
  int value;
  struct node *next;
;
typedef struct node node_t;

struct list 
  node_t *head;
;
typedef struct list list_t;

以及功能:

void list_init(list_t *l)

  l = malloc(sizeof(list_t));
  assert(l);
  l->head = NULL;


static node_t *new_node(int value)

  node_t *new = malloc(sizeof(node_t));
  assert(new);

  new->value = value;
  new->next = NULL;

  return new;


void push(list_t *l, int value)

  node_t *node = new_node(value);
  if (l->head == NULL) 
    l->head = node;
   else 
    node->next = l->head;
    l->head = node;
  


void print_list(list_t *l)

  node_t *tmp = l->head;
  while (tmp) 
    printf("%d\n", tmp->value);
    tmp = tmp->next;
  

主要功能:

int main()

  list_t *l;

  list_init(l);
  push(l, 2);
  push(l, 4);
  push(l, 6);

  print_list(l);

  return 0;

gdb 告诉我推送函数 (if (l->head == NULL)) 中的 NULL 检查导致设置错误。但它也告诉我l->head 确实为NULL。如果我删除它,seg 错误就会发生在下一个调用 l->head 的地方。

如果我没有将我的列表声明为指针...像这样:

int main()

  list_t l;

  list_init(&l);
  push(&l, 2);
  push(&l, 4);
  push(&l, 6);

  print_list(&l);

  return 0;

它解决了这个问题。但是,它随后会到达print_list 函数。它将打印列表,然后打印更多的垃圾值,然后是段错误。

感谢您的帮助。 而且我知道这里没有释放任何内存。只是试图保持代码较小以解决问题。

【问题讨论】:

你的list_init 不可能工作。 C 是按值传递的,所以list_init 所做的任何事情都不会对main() 中的指针变量l 产生任何影响,它仍然充满了未初始化的垃圾。你应该得到一个编译器警告(启用-Wall!!)。 你的第二个版本没有帮助 - 当list_init() 分配给它的局部变量l 时,它只是丢失了它传递的指针,所以它所做的任何事情都不会对lmain。 (顺便说一句,就这个问题而言,您将所有这些不同的东西称为l,这真的很令人困惑。) pushprint_list 中,您还应该检查输入参数(这里检查l 是否为空)。 new_nodelist_init 中的先前断言不会阻止可以从输入错误的地方调用这些函数。 ifpush 中没有任何意义,因为 else 对两个分支都做了正确的事情。 @stark 在if 的两个成员中,l->head = node; 的代码相同,但它的作用不同。靠近它看。 【参考方案1】:

例如这个函数

void list_init(list_t *l)

  l = malloc(sizeof(list_t));
  assert(l);
  l->head = NULL;

没有意义,因为该函数处理值的副本list_t * 类型的参数。

所以在这个语句中更改副本

  l = malloc(sizeof(list_t));

不影响用作参数的原始指针。所以这个函数实际上调用了未定义的行为。它不会初始化列表。

所以在调用此代码sn-p中的函数之后

list_t *l;

list_init(l);

指针l 保持未初始化并具有不确定的值。另一方面,该函数会产生内存泄漏。

这段代码sn-p没有任何改变

list_t l;

list_init(&l);

因为在这个语句之后的函数list_init

l = malloc(sizeof(list_t));

该函数处理list_t 类型的动态分配对象,而不是main 中声明的引用对象l 传递。

要解决这个问题,请定义函数list_init like

void list_init(list_t *l)

    assert(l);
    l->head = NULL;

然后这样称呼它

list_t l;

list_init(&l);

函数push可以写得更简单

int push( list_t *l, int value )

    node_t *node = new_node( value, l->head );
    int success = node != NULL;

    if ( success )
    
        l->head = node;
    

    return success;

相应地函数new_node可以定义为

static node_t * new_node( int value, node_t *next )

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

    if ( node != NULL )
    
        node->value = value;
        node->next = next;
    
  
    return node;

【讨论】:

这行得通。也感谢其他建议。 @breakthatbass 完全没有。不客气。:)【参考方案2】:

您的list_init 不可能工作。 C 是按值传递的,所以list_init 所做的任何事情都不会对main() 中的指针变量l 产生任何影响,它仍然充满了未初始化的垃圾。你应该得到一个编译器警告(启用-Wall!!)。

您可能希望list_init() 返回其指针而不是尝试通过引用传递,所以:

list_t *list_init(void)

  list_t *l = malloc(sizeof(list_t));
  assert(l);
  l->head = NULL;
  return l;

//...
int main()

  list_t *l = list_init();
  // ...

你的第二个版本同样被破坏了,因为当list_init() 分配给它的局部变量l 时,它只是丢失了它被传递的指针,所以它再次对main 中的l 没有任何影响.您不需要在此版本中分配任何内容,因为您已经传递了一个指向有效list_t 对象的指针。所以如果你想这样写,那么你只是想要

void list_init(list_t *l)

  l->head = NULL;

// ...
int main()

  list_t l;
  list_init(&l);
  // ...

【讨论】:

head 指向实际的第一个节点,或者 NULL 指向空列表。我认为 print_list 是可以的(或者如果 list_init 是固定的)。 @stark:哦,是的,你是对的。删除了那部分。 谢谢。我很高兴为list_init 显示这两个选项。你会说一个比另一个更受欢迎,还是认为它们同样可行?

以上是关于为啥这个基本链表可以在 MacOS 上工作,但在 Linux 上会出现段错误的主要内容,如果未能解决你的问题,请参考以下文章

为啥这个 c++ 代码在 Linux 中可以正常工作,但在 Windows 中却不能[关闭]

Traefik docker 映像不能在 Windows 上运行但在 MacOS 上运行?

为啥在 Windows 上使用 Mechanize 访问 SSL 站点会失败,但在 Mac 上可以工作?

为啥 macOS 中的 SwiftUI 多行换行文本在 Preview 中有效,但在实际应用中无效?

Cordova - 为啥 $http get 请求在 android 设备上失败但在 chrome 上工作

为啥 Net-SSLeay 停止通过 GitHub 工作流程在 macOS 上安装?