提问人:DevFish 提问时间:4/25/2023 最后编辑:DevFish 更新时间:4/26/2023 访问量:102
我将如何在此代码中正确使用 malloc 以避免崩溃?
How would I properly use malloc in this code to avoid a crash?
问:
我对在哪里需要使用 malloc 感到非常困惑,它不断崩溃。我不确定在导航到下一个列表时是否需要重新分配内存。
struct string_list {
char *s;
struct string_list *next;
};
typedef struct string_list slist;
slist *sl_cons(char *s, slist *strings){
slist *result = (slist*)malloc(sizeof(slist));
result -> s = s;
result -> next = strings;
return result;
}
slist *sl_shallow_copy(slist *strings){
slist *shallow = (slist*)malloc(sizeof(slist));
while(strings != NULL){
shallow->s = strings->s;
shallow = shallow->next;
strings = strings->next;
}
return shallow;
}
void sl_free(slist *strings){
if(strings != NULL){
sl_free(strings->next);
free(strings->s);
free(strings);
}
答:
3赞
Allan Wind
4/25/2023
#1
在表达式中,右侧 (rhs) 未初始化,即未定义行为 (UB)。任何事情都可能发生,包括崩溃。
shallow = shallow->next
在你分配的实例中,但想在循环中从你的浅层副本的 slists 实例中复制许多实例。
sl_shallow_copy()
1
slist
strings. Allocate new
如果失败(无论可能性如何),程序将由于 NULL 取消引用而出现段错误。
malloc()
sl_cons()
:如果传递常规字符串文本,则在尝试释放只读字符串时将出现段错误。如果你想释放,那么你要复制它以确保你可以。s
在 :首选迭代算法,以避免在节点过多时炸毁堆栈。C 不保证尾部调用优化 (TCO),这意味着堆栈下方会有足够多的节点。
sl_free()
sl_free()
:如果同时释放原始克隆和浅层克隆,则字符串将释放两次,即 UB。你想释放但不想释放浅层复制的。slist
s
您可以判断它们是否对浅层或深层数据进行操作,但是,请考虑参考计数设计。 现在保存我们的(副本)以及对它的引用计数。当我们创建列表时,我们初始化为 . 递增 ,并递减它,除非在这种情况下我们释放 和 。
sl_cons()
sl_free()
struct string_ref *sref
char *
n
n
0
sl_shallow_copy()
n
sl_free
0
sref->s
sref
#define _POSIX_C_SOURCE 200809L // strdup()
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct string_ref {
char *s;
size_t n;
};
typedef struct string_list {
struct string_ref *sref;
struct string_list *next;
} slist;
slist *sl_cons(char *s, slist *strings) {
slist *result = malloc(sizeof *result);
if(!result)
return NULL;
result->sref = malloc(sizeof *result->sref);
result->sref->s = strdup(s);
result->sref->n = 0;
result->next = strings;
return result;
}
void sl_free(slist *strings) {
while(strings) {
slist *tmp = strings;
strings = strings->next;
if(tmp->sref->n)
tmp->sref->n--;
else {
free(tmp->sref->s);
free(tmp->sref);
}
free(tmp);
}
}
slist *sl_shallow_copy(slist *strings){
slist *shallow = NULL;
for(slist **c = &shallow; strings; strings = strings->next, c = &(*c)->next) {
*c = malloc(sizeof **c);
if(!*c) {
sl_free(shallow);
return NULL;
}
(*c)->sref = strings->sref;
(*c)->sref->n++;
(*c)->next = NULL;
}
return shallow;
}
void sl_print(slist *strings) {
if(!strings)
return;
printf("%s (%zu)", strings->sref->s, strings->sref->n);
strings = strings->next;
for(; strings; strings = strings->next)
printf(" %s (%zu)", strings->sref->s, strings->sref->n);
printf("\n");
}
int main() {
slist *s = sl_cons("hello", sl_cons("world", sl_cons("!", NULL)));
sl_print(s);
slist *s2 = sl_shallow_copy(s);
sl_print(s2);
sl_free(s2);
sl_free(s);
}
输出示例:
hello (0) world (0) ! (0)
hello (1) world (1) ! (1)
评论
0赞
August Karlstrom
4/25/2023
UB = 未定义的行为(即任何事情都可能发生)
0赞
Allan Wind
4/25/2023
@AugustKarlstrom 首次使用时详细说明。谢谢。
0赞
Ian Abbott
4/25/2023
引用计数看起来还不太对。 当它被分配时,它初始化为 1,但当它为 1 时不释放它。sl_cons
sl_free
0赞
chux - Reinstate Monica
4/26/2023
@AllanWind 为什么是非 posix 问题?#define _POSIX_C_SOURCE 200809L
0赞
Allan Wind
4/26/2023
@chux-ReinstateMonica 在 Linux 上需要它。strdup()
评论
slist
strings->s
sl_shallow_copy()
shallow->next
shallow = shallow->next;
shallow->s = strings->s;