【问题标题】:Struct values change after allocating memory分配内存后结构值发生变化
【发布时间】:2019-12-02 23:40:59
【问题描述】:

所以我有一些代码最终将创建一个包含单词和结束句子的标点符号的句子链接列表,但现在我遇到了一个非常特殊的问题:

struct sentence {
    char *words[10];
    char punc;
    struct sentence *next;
};

void split(char *buf, char *split[], size_t max) {
    char * token = strtok(buf, " ");
    int i = 0;
    while (token != NULL) {
        split[i] = token;
        i++;
        token = strtok(NULL, " ");
    }
    split[i] = NULL;
}

void read_sentence(struct sentence *a) {
    printf("> ");
    char str[30];
    scanf("%[^.!?\n]s", str);
    split(str, a->words, sizeof(a->words));
    char temp[1];
    scanf("%c", temp);
    a->punc = temp[0];
    a->next = NULL;
}

int main(int argc, char *argv[]) {
    struct sentence *a = malloc(sizeof(struct sentence));
    read_sentence(a);
    printf("\nfirst contains: %s %s%c\n", a->words[0], a->words[1], a->punc);
    struct sentence *b = malloc(sizeof(struct sentence));
    printf("\nfirst contains: %s %s%c\n", a->words[0], a->words[1], a->punc);
}

每当我运行此代码时,两个打印语句都会产生不同的结果,第一个是正确的,但第二个是空的。我完全不知道为什么会这样。

【问题讨论】:

    标签: c struct malloc


    【解决方案1】:

    第二个结构与此无关。

    您的代码调用了未定义的行为。您正在将大量悬空指针加载到您的 words 成员中。所有这些指针都指向函数read_sentence 中的自动变量str 中的内存,一旦read_sentence 返回,该变量就消失了。因此,在main 中打印回的尊重利用了悬空指针,因此,UB

    我可以告诉您为此提供动态解决方案(使用strlen + malloc + memcpystrdup 找到的每个单词的动态分配,如果您踏入 POSIX 领域,或者我们可以这样做最简单的方法:使内存words 与句子结构本身相关联。即创建一个属于每个sentencestr,然后让words 指向其“兄弟”成员中的内存:

    struct sentence {
        char line[80];   // <<=== here
        char *words[10];
        char punc;
        struct sentence *next;
    };
    
    void split(char *buf, char *split[], size_t max) {
        char * token = strtok(buf, " ");
        int i = 0;
        while (i < max && token != NULL) {
            split[i+] = token;
            i++;
            token = strtok(NULL, " ");
        }
        split[i] = NULL;
    }
    
    void read_sentence(struct sentence *a) {
        printf("> ");
        scanf("%[^.!?\n]s", a->line); // <<=== HERE
        split(a->line, a->words, sizeof a->words / sizeof a->words[0]); // <<=== HERE
        a->punc = getchar();
        a->next = NULL;
    }
    
    int main(int argc, char *argv[]) 
    {
        struct sentence *a = malloc(sizeof *a);
        read_sentence(a);
        printf("\nfirst contains: %s %s%c\n", a->words[0], a->words[1], a->punc);
    
        struct sentence *b = malloc(sizeof *b);
        printf("\nfirst contains: %s %s%c\n", a->words[0], a->words[1], a->punc);
    
        free(a);
        free(b);
    
        return 0;
    }
    

    值得注意的是,这可能比最初看起来更有效率。是的,每个句子都有自己的行缓冲区,但它不需要按单词分配来管理所有这些字符串。

    希望对你有帮助。

    【讨论】:

    • Re "所有这些指针都指向函数str中的自动变量read_sentence中的内存",手册页没有说明为什么会这样,所以我将解释:strtok 不会返回新字符串。它将正在处理的字符串 (str) 中的分隔符替换为 NUL 字符,并返回一个指向该字符串内的指针。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-14
    • 1970-01-01
    • 2010-11-20
    • 2013-05-11
    • 2014-10-19
    • 1970-01-01
    相关资源
    最近更新 更多