我将如何在此代码中正确使用 malloc 以避免崩溃?

How would I properly use malloc in this code to avoid a crash?

提问人:DevFish 提问时间:4/25/2023 最后编辑:DevFish 更新时间:4/26/2023 访问量:102

问:

我对在哪里需要使用 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);
    }
c 字符串 内存 崩溃 malloc

评论

1赞 Gaurav Pathak 4/25/2023
什么?slist
0赞 Allan Wind 4/25/2023
不相关,但不要施放虚空 *
1赞 Ian Abbott 4/25/2023
如果执行浅拷贝并释放原始副本和副本,则将释放两次,这会导致未定义的行为strings->s
0赞 Ian Abbott 4/25/2023
中存在问题。 未初始化,因此在下一次迭代中后跟 by 是无效的指针取消引用,导致 UB。sl_shallow_copy()shallow->nextshallow = shallow->next;shallow->s = strings->s;
1赞 user207421 4/25/2023
“它不断崩溃”不是问题描述。崩溃是怎么回事?哪里?有什么错误消息?

答:

3赞 Allan Wind 4/25/2023 #1
  1. 在表达式中,右侧 (rhs) 未初始化,即未定义行为 (UB)。任何事情都可能发生,包括崩溃。shallow = shallow->next

  2. 在你分配的实例中,但想在循环中从你的浅层副本的 slists 实例中复制许多实例。sl_shallow_copy()1sliststrings. Allocate new

  3. 如果失败(无论可能性如何),程序将由于 NULL 取消引用而出现段错误。malloc()

  4. sl_cons():如果传递常规字符串文本,则在尝试释放只读字符串时将出现段错误。如果你想释放,那么你要复制它以确保你可以。s

  5. 在 :首选迭代算法,以避免在节点过多时炸毁堆栈。C 不保证尾部调用优化 (TCO),这意味着堆栈下方会有足够多的节点。sl_free()

  6. sl_free():如果同时释放原始克隆和浅层克隆,则字符串将释放两次,即 UB。你想释放但不想释放浅层复制的。slists

  7. 您可以判断它们是否对浅层或深层数据进行操作,但是,请考虑参考计数设计。 现在保存我们的(副本)以及对它的引用计数。当我们创建列表时,我们初始化为 . 递增 ,并递减它,除非在这种情况下我们释放 和 。sl_cons()sl_free()struct string_ref *srefchar *nn0sl_shallow_copy()nsl_free0sref->ssref

#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_conssl_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()