C 将结构指针传递给函数的分段错误

C 将结构指针传递给函数的分段错误,c,pointers,struct,C,Pointers,Struct,我是C语言的新手,为了学习structs,我正在构建一个程序,为struct ll_字符串提供一组有限的功能,它基本上是一个字符串链接列表 我试图实现的函数集包括一个insert_ll_string()函数,该函数应将传入的struct ll_string元素浓缩到另一个struct ll_string元素的末尾,但由于在我的测试用例中调用该函数时,程序因sig错误而崩溃,因此未能实现。这是test_insert()函数的STILL WORKS和SIG FAULT注释 这是它的头文件: 文件:l

我是C语言的新手,为了学习structs,我正在构建一个程序,为struct ll_字符串提供一组有限的功能,它基本上是一个字符串链接列表

我试图实现的函数集包括一个insert_ll_string()函数,该函数应将传入的struct ll_string元素浓缩到另一个struct ll_string元素的末尾,但由于在我的测试用例中调用该函数时,程序因sig错误而崩溃,因此未能实现。这是test_insert()函数的STILL WORKS和SIG FAULT注释

这是它的头文件:

文件:ll_string.h

struct ll_string {
    char *string;
    struct ll_string *next;
};

struct ll_string *create_ll_string(char *, struct ll_string *);
void insert_ll_string(struct ll_string *, struct ll_string *);
void remove_item_from_ll_string(struct ll_string *, struct ll_string *);
void free_ll_string(struct ll_string *);
void print_ll_string(struct ll_string *);
这是对应的.c文件,它缺少了在ll_string.h中声明的函数的一些定义,但我想我的问题可能只是围绕着函数create_ll_string()和insert_ll_string()

文件:ll_string.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "ll_string.h"

/* create_ll_string: allocates memory for a new struct ll_string and 
 * initializes it with given arguments returns a pointer to new struct */
struct ll_string *create_ll_string(char *string, struct ll_string *next) {
    struct ll_string *new_ll_string;

    if (!string) {
        printf("string can\'t be NULL\n");
        return NULL;
    }

    if (*string == '\0') {
        printf("string needs to be at least 1 char long\n");
        return NULL;
    }

    if (!(new_ll_string = (struct ll_string *) malloc(sizeof(struct ll_string)))) {
        printf("couldn\'t allocate mem for new ll_string\n");
        exit(EXIT_FAILURE);
    }

    new_ll_string->string = strdup(string);
    new_ll_string->next = next;

    return new_ll_string;
}

/* insert_ll_string: concanates item to the end of dest */
void insert_ll_string(struct ll_string *dest, struct ll_string *item) {
    struct ll_string *cur;

    if (!dest) {
        printf("dest and item can\'t be NULL\n");
        return;
    }
    if (!item) {
        printf("item can\'t be NULL\n");
        return;
    }

    cur = dest;
    while (!cur->next) {
        cur = cur->next;
    }

    cur->next = item;

    return ;
}

/* remove_item_from_ll_string: removes item from list src */
void remove_item_from_ll_string(struct ll_string *src, struct ll_string *item) {
    return ;
}

/* printf_ll_string: prints each string in ll_string */
void print_ll_string(struct ll_string *ll_string) {
    if (!ll_string) {
        printf("ll_string is NULL\n");
        return ;
    }

    do {
        printf("%s\n", ll_string->string);
    } while (!(ll_string = ll_string->next));
}

/* free_ll_string: frees all memory pointed to by ll_string */
void free_ll_string(struct ll_string *ll_string) {
    struct ll_string *next;

    if (!ll_string) {
        return ;
    }

    while ((next = ll_string->next)) {
        free(ll_string->string);
        free(ll_string);
        ll_string = next;
    }
}
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "minunit.h"

#include "ll_string.h"

#define MAX_ERROR_MSG_LENGTH 1000

int tests_run = 0;

static char *test_create(void) {
    struct ll_string *test_ll;
    struct ll_string *test_null_ll;
    char *empty_string = strdup("");
    char *null_string = NULL;
    char *correct_string = strdup("this should work");
    char *correct_string2 = strdup("this should also work");
    char *error_msg;

    if (!(error_msg = (char *) malloc(sizeof(char) * MAX_ERROR_MSG_LENGTH))) {
        printf("couldn\'t allocate mem for error msg");
        exit(EXIT_FAILURE);
    }

    // test_ll->string == correct_string
    // test_ll->next == NULL
    test_ll = create_ll_string(correct_string, NULL);
    sprintf(error_msg, "error, test_ll->string != \"%s\" is %s", correct_string, test_ll->string);
    mu_assert(
            error_msg,
            strcmp(test_ll->string, correct_string) == 0);

    // test_ll->next->string == correct_string
    // test_ll->string == correct_string2
    test_ll = create_ll_string(correct_string2, test_ll);
    sprintf(error_msg, "error, test_ll->string != \"%s\" is %s", correct_string2, test_ll->string);
    mu_assert(
            error_msg,
            strcmp(test_ll->string, correct_string2) == 0);

    sprintf(error_msg, "error, test_ll->next->string != \"%s\" is \"%s\"", correct_string, test_ll->next->string);
    mu_assert(
            error_msg,
            strcmp(test_ll->next->string, correct_string) == 0);

    test_null_ll = test_ll;
    test_null_ll = create_ll_string(empty_string, test_ll);
    // test_null_ll == NULL
    mu_assert(
            "error, test_null_ll != NULL",
            test_null_ll == NULL);

    test_null_ll = test_ll;
    test_null_ll = create_ll_string(null_string, test_ll);
    // test_null_ll == NULL
    mu_assert(
            "error, test_null_ll != NULL",
            test_null_ll == NULL);

    sprintf(error_msg, "error, test_ll->string != \"%s\" is \"%s\"", correct_string2, test_ll->string);
    mu_assert(
            error_msg,
            strcmp(test_ll->string, correct_string2) == 0);

    sprintf(error_msg, "error, test_ll->next->string != \"%s\" is \"%s\"", correct_string, test_ll->next->string);
    mu_assert(
            error_msg,
            strcmp(test_ll->next->string, correct_string) == 0);

    free_ll_string(test_ll);
    free(correct_string);
    free(correct_string2);
    free(empty_string);
    free(error_msg);

    return 0;
}

static char *test_insert(void) {
    struct ll_string *ll_test1;
    struct ll_string *ll_test2;
    struct ll_string *ll_test3;

    char *test_string1 = strdup("test_string1");
    char *test_string2 = strdup("test_string2");
    char *test_string3 = strdup("test_string3");
    char *error_msg;

    if (!(error_msg = (char *) malloc(sizeof(char) * MAX_ERROR_MSG_LENGTH))) {
        printf("couldn\'t allocate mem for error msg");
        exit(EXIT_FAILURE);
    }


    ll_test1 = create_ll_string(test_string1, NULL);
    ll_test2 = create_ll_string(test_string2, NULL);
    ll_test3 = create_ll_string(test_string3, NULL);

    // STILL WORKS

    insert_ll_string(ll_test1, ll_test2); // SEG FAULT
    insert_ll_string(ll_test1, ll_test3);

    sprintf(error_msg, "error, ll_test1->string != \"%s\" is \"%s\"", test_string1, ll_test1->string);
    mu_assert(
            error_msg,
            strcmp(ll_test1->string, test_string1) == 0);
    sprintf(error_msg, "error, ll_test1->next->string != \"%s\" is \"%s\"", test_string2, ll_test1->next->string);
    mu_assert(
            error_msg,
            strcmp(ll_test1->next->string, test_string2) == 0);
    sprintf(error_msg, "error, ll_test1->next->next->string != \"%s\" is \"%s\"", test_string1, ll_test1->next->next->string);
    mu_assert(
            error_msg,
            strcmp(ll_test1->next->next->string, test_string3) == 0);

    free_ll_string(ll_test1);
    free_ll_string(ll_test2);
    free_ll_string(ll_test3);
    free(test_string1);
    free(test_string2);
    free(test_string3);

    return 0;
}

static char *all_tests(void) {
    mu_run_test(test_create);
    mu_run_test(test_insert);
    return 0;
}

int main(int argc, char* argv[]) {
    char *result = all_tests();
    if (result != 0) {
        printf("%s\n", result);
    } else {
        printf("ALL TESTS PASSED\n");
    }
    printf("Tests run: %d\n", tests_run);

    return result != 0;
}
是什么导致了这个信号错误?除了程序崩溃部分中的局部变量外,我没有访问任何内存。我不会取消对传递给insert_ll_struct()的指针的引用,至少不会在调用函数后立即取消引用


提前感谢您的帮助

我将查看
免费字符串()中的逻辑
。你确定没有两次释放内存吗?在代码中,它看起来像是释放了链中的所有字符串。因此,我认为您将在
test\u create
中多次释放
test\u ll
。查看禁用
test\u create
时是否仍会出现错误,如果没有,则您的问题我认为可能是由于未定义的行为造成的,因为您多次释放了内容

在释放指针指向的内存后,最好将任何释放的指针设置为NULL,这样可以避免此问题

/* free_ll_string: frees all memory pointed to by ll_string */
void free_ll_string(struct ll_string *ll_string) {
    struct ll_string *next;

    if (!ll_string) {
        return ;
    }

    while ((next = ll_string->next)) {
        free(ll_string->string);
        free(ll_string);
        ll_string = next;
    }
}

我想答案就在眼前。在
中插入字符串()

应该是

while (cur->next) {
而(!cur->next){cur=cur->next;}
此代码将导致崩溃。假设cur->next返回null,而您尝试访问无效的null next,您应该在while循环中对cur进行null检查,就像这样while(null!=cur&&!cur->next)

使用调试器-它会告诉您seg故障发生的位置,或者使用调试信息编译并在
valgrind
下运行。如果您只运行test_insert(即禁用test_create),您是否会遇到seg故障?我正在考虑使用gdb对其进行测试,但我仍然需要找出如何使用它,但现在可能是学习它的正确时间。:)在此期间,如果您能回答我的问题,我将不胜感激。谢谢您的回答。seg故障也发生在我刚刚运行test_insert时。我看不到我能两次释放记忆。free\u ll\u string函数应该释放链表中从传入元素开始到列表末尾的所有字符串,然后也应该释放每个结构。啊,好吧,那么可能不是这个seg错误的原因,因为
test\u null\u ll
null
,您也不会试图对其调用
free\u ll\u string
。然而,我认为这里仍然存在一个危险:如果你
a=创建ll_字符串(“a”,NULL)
,然后
B=创建ll_字符串(“B”,a)
,那么释放
B
将释放
a
,然后释放
B
将再次释放
B
。这是因为
next
create\u ll\u string
中不重复和/或在
free\u ll\u string
中未将指针设置为
NULL
。因此,如果程序员努力为每个
create\ull\u string
语句调用
free\ull\u string
,他们就会遇到这个问题。(事实上,在
free\ull\u string
中将指针设置为NULL在这种情况下没有帮助,但无论如何这是一种很好的防御风格)同时调用
a=create\ull\u string(“a”,NULL)
then
free\u ll\u string(A)
不会释放任何东西,因为
while((next=ll\u string->next))
不执行迭代。确定。我重写了
create_ll_string
free_ll_string
的部分内容。以下是新版本:[要点][
while (!cur->next) {
while (cur->next) {