为什么这个基本链表在 MacOS 上工作但在 Linux 上出现段错误
Why does this basic linked list work on MacOS but seg fault on Linux
我有一个偶尔在 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。如果我删除它,段错误只会发生在调用 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
函数。它将打印列表,然后打印更多垃圾值,然后出现段错误。
感谢任何帮助。
而且我知道这里没有释放内存。只是试图保持代码小以解决问题。
比如这个函数
void list_init(list_t *l)
{
l = malloc(sizeof(list_t));
assert(l);
l->head = NULL;
}
没有意义,因为函数处理值的副本 list_t *
.
类型的参数
因此更改此语句中的副本
l = malloc(sizeof(list_t));
不影响用作参数的原始指针。所以这个函数实际上调用了未定义的行为。它不初始化列表。
因此在调用此代码片段中的函数后
list_t *l;
list_init(l);
指针 l
保持未初始化状态并且具有不确定的值。另一方面,该函数会产生内存泄漏。
这段代码没有任何变化
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;
}
您的 list_init
不可能工作。 C 是按值传递的,所以 list_init
所做的任何事情都不会对 main()
中的指针变量 l
产生任何影响,它仍然充满未初始化的垃圾。你应该得到一个编译器警告(启用 -Wall
!!)。
您可能希望 list_init()
return 它的指针而不是试图通过引用传递,所以:
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
时,它只是失去了传递给它的指针,所以它所做的任何事情都不会对 l
在 main
中。您不需要在此版本中分配任何内容,因为您已经传递了一个指向有效 list_t
对象的指针。所以如果你想这样写,那么你只需要
void list_init(list_t *l)
{
l->head = NULL;
}
// ...
int main()
{
list_t l;
list_init(&l);
// ...
}
我有一个偶尔在 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。如果我删除它,段错误只会发生在调用 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
函数。它将打印列表,然后打印更多垃圾值,然后出现段错误。
感谢任何帮助。 而且我知道这里没有释放内存。只是试图保持代码小以解决问题。
比如这个函数
void list_init(list_t *l)
{
l = malloc(sizeof(list_t));
assert(l);
l->head = NULL;
}
没有意义,因为函数处理值的副本 list_t *
.
因此更改此语句中的副本
l = malloc(sizeof(list_t));
不影响用作参数的原始指针。所以这个函数实际上调用了未定义的行为。它不初始化列表。
因此在调用此代码片段中的函数后
list_t *l;
list_init(l);
指针 l
保持未初始化状态并且具有不确定的值。另一方面,该函数会产生内存泄漏。
这段代码没有任何变化
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;
}
您的 list_init
不可能工作。 C 是按值传递的,所以 list_init
所做的任何事情都不会对 main()
中的指针变量 l
产生任何影响,它仍然充满未初始化的垃圾。你应该得到一个编译器警告(启用 -Wall
!!)。
您可能希望 list_init()
return 它的指针而不是试图通过引用传递,所以:
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
时,它只是失去了传递给它的指针,所以它所做的任何事情都不会对 l
在 main
中。您不需要在此版本中分配任何内容,因为您已经传递了一个指向有效 list_t
对象的指针。所以如果你想这样写,那么你只需要
void list_init(list_t *l)
{
l->head = NULL;
}
// ...
int main()
{
list_t l;
list_init(&l);
// ...
}