简体   繁体   English

为什么这个基本链表在 MacOS 上工作,但在 Linux 上出现段错误

[英]Why does this basic linked list work on MacOS but seg fault on Linux

I have a linked list lib I use occasionally on MacOS and I just tried using it on Linux and I'm getting all sorts of problems.我有一个偶尔在 MacOS 上使用的链表库,我只是尝试在 Linux 上使用它,但遇到了各种各样的问题。 I've broken it down into a much simpler version to get to the problems.我已经把它分解成一个更简单的版本来解决问题。 I've been able to find where the problems are with gdb , I just don't know why they're happening.我已经能够找到gdb的问题出在哪里,我只是不知道为什么会这样。 It works fine on MacOs even with address sanitizer.即使使用地址消毒剂,它也可以在 MacO 上正常工作。 I suspect I might be misusing the pointers here somehow.我怀疑我可能会以某种方式滥用这里的指针。

here's my list structure:这是我的列表结构:

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

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

And the functions:和功能:

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;
  }
}

main function:主要 function:

int main()
{
  list_t *l;

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

  print_list(l);

  return 0;
}

gdb is telling me that the NULL check in the push function ( if (l->head == NULL) ) is causing the set fault. gdb告诉我 NULL 检查推送 function ( if (l->head == NULL) ) 导致设置错误。 But it's also telling me that l->head is indeed NULL.但它也告诉我l->head确实是 NULL。 If i remove that, the seg fault just happens in the next place wherever l->head is called.如果我删除它,则段错误只会发生在调用l->head的下一个位置。

If i instead I don't declare my list as a pointer...like this:相反,如果我不将我的列表声明为指针...像这样:

int main()
{
  list_t l;

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

  print_list(&l);

  return 0;
}

It fixes the issue.它解决了这个问题。 However, it then makes it as far as the print_list function.但是,它随后会到达print_list function。 It will print the list then print a couple more junk values and then seg fault.它将打印列表,然后打印更多的垃圾值,然后是段错误。

I appreciate any help.我很感激任何帮助。 And I know no memory is freed here.而且我知道没有 memory 在这里被释放。 Just trying to keep the code small to fix the problem.只是试图保持代码小以解决问题。

For example this function例如这个 function

void list_init(list_t *l)
{
  l = malloc(sizeof(list_t));
  assert(l);
  l->head = NULL;
}

does not make a sense because the function deals with a copy of the value the argument of the type list_t * .没有意义,因为 function 处理list_t *类型参数的值的副本。

So changing the copy in this statement所以更改此语句中的副本

  l = malloc(sizeof(list_t));

does not influence on the original pointer used as an argument.不影响用作参数的原始指针。 So this function in fact invokes undefined behavior.所以这个 function 实际上调用了未定义的行为。 It does not initialize a list.它不初始化列表。

So after calling the function as in this code snippet因此,在调用 function 之后,如此代码片段

list_t *l;

list_init(l);

the pointer l stays uninitialized and has an indeterminate value.指针l保持未初始化并具有不确定的值。 On the other hand, the function produces a memory leak.另一方面,function 会产生 memory 泄漏。

And nothing is changed in this code snippet此代码段中没有任何更改

list_t l;

list_init(&l);

because within the function list_init after this statement因为在此语句之后的 function list_init

l = malloc(sizeof(list_t));

the function deals with a dynamically allocated object of the type list_t instead of the passed by reference object l declared in main. function 处理类型为list_t的动态分配的 object,而不是在 main 中声明的通过引用传递的l

To fix the problem define the function list_init like要解决此问题,请定义 function list_init类的

void list_init(list_t *l)
{
    assert(l);
    l->head = NULL;
}

and call it like并称它为

list_t l;

list_init(&l);

The function push can be written simpler function 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;
}

Correspondingly the function new_node can be defined like相应地,function 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;
}

Your list_init can't possibly work.你的list_init不可能工作。 C is pass-by-value, so nothing that list_init does will have any effect on the pointer variable l in main() , which remains full of uninitialized garbage. C 是按值传递的,因此list_init所做的任何事情都不会对main()中的指针变量l产生任何影响,它仍然充满了未初始化的垃圾。 You ought to get a compiler warning for it (enable -Wall .!).你应该得到一个编译器警告(启用-Wall .!)。

You probably want to have list_init() return its pointer instead of trying to pass by reference, so:您可能希望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();
  // ...
}

Your second version is likewise broken because when list_init() assigns to its local variable l , it just loses the pointer it was passed, so again nothing that it does has any effect on the l in main .你的第二个版本同样被破坏了,因为当list_init()分配给它的局部变量l时,它只是丢失了它传递的指针,所以它所做的一切对main中的l没有任何影响。 You do not need to allocate anything in this version since you are already passed a pointer to a valid list_t object.您不需要在此版本中分配任何内容,因为您已经传递了指向有效list_t object 的指针。 So if you want to write it this way, then you would simply want所以如果你想这样写,那么你只需要

void list_init(list_t *l)
{
  l->head = NULL;
}
// ...
int main()
{
  list_t l;
  list_init(&l);
  // ...
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM