简体   繁体   中英

Struct values change after allocating memory

So I have some code which is eventually going to create a linked list of sentences which contains words and the punctuation that ends the sentence but right now I'm having a very peculiar issue:

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);
}

Whenever I run this code the two print statements have different results, the first is correct but the second is empty. I'm at a complete loss as to why this is happening.

The second struct has nothing to do with this.

Your code invokes undefined behavior. You're loading a wrack of dangling pointers into your words member. All those pointers point to memory within the automatic variable str in the function read_sentence , which is gone as soon a read_sentence returns. The deferences for printing back in main therefore utilize dangling pointers and thus, UB

I could tell you to provide a dynamic solution to this (dynamic allocation of each word found using strlen + malloc + memcpy , or strdup if you step into the POSIX arena, or we can do it the easy way: make the memory words refers to tied to the sentence structure itself. Ie Make a str that belongs to each sentence , then let words point to memory in its "sibling" member:

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;
}

Worth noting, this is probably more efficient than it first seems. Yes, each sentence gets its own line-buffer, but it doesn't need per-word allocations managing all those strings.

Hope it helps.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM