为啥这个基本链表可以在 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
时,它只是丢失了它传递的指针,所以它所做的任何事情都不会对l
在main
。 (顺便说一句,就这个问题而言,您将所有这些不同的东西称为l
,这真的很令人困惑。)
在push
和print_list
中,您还应该检查输入参数(这里检查l
是否为空)。 new_node
和 list_init
中的先前断言不会阻止可以从输入错误的地方调用这些函数。
if
在push
中没有任何意义,因为 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 中有效,但在实际应用中无效?