链表问题中字符串的动态分配

Dynamic allocation of a string in a linked list problem

我已经创建了 2 个函数,它们从一个文件中读取一些数据并将数据写入另一个文件,但是使用链表和在该列表中动态分配的字符串,但是我有一个我找不到的逻辑错误:

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

struct product {
    char id[6];
    char *name;
    int price;
    struct product *next;
};

struct product *read(struct product *head, FILE *input) {
    struct product *p, *q, *aux;
    p = (struct product *)malloc(sizeof(struct product));
    char aux_id[6];
    char aux_name[20];
    int aux_price;
    fscanf(input, "%s %s %d", aux_id, aux_name, &aux_price);
    strcpy(p->id, aux_id);
    p->name = (char *)malloc(strlen(aux_name) * (sizeof(char)));
    strcpy(p->name,aux_name);
    p->price = aux_price;
    p->next = NULL;
    head = p;

    while (fscanf(input, "%s %s %d", aux_id, aux_name, &aux_price) != EOF) {
        q = (struct product *)malloc(sizeof(struct product));
        q->name = (char *)malloc(strlen(aux_name) * (sizeof(char)));
        q->next = NULL;
        strcpy(q->name, aux_name);
        strcpy(q->id, aux_id);
        q->price = aux_price;
        p->next = q;
        p = q;
    }
    return head;
}

void write(struct product *head, FILE *output) {
    struct product *p;
    p = head;
    while (p != NULL) {
        fprintf(output, "%s %s %d\n", p->id, p->name, p->price);
        p = p->next;
    }
}

int main() {
    struct product *head, *p, *q;
    FILE *input = fopen("input.txt", "r+");
    FILE *output = fopen("output.txt", "w+");
    head = read(head, input);
    write(head, output);
    fclose(input);
    fclose(output);
}

输入文件如下所示:

333444 Cola 3
332312 Pepsi 4
123451 Mountain 3

输出文件如下所示

333444 Cola 3
332312°)q   4
123451à)q   3

您的代码中存在多个问题:

  • 您读取字符串的数组太小:6 个字节不足以存储 6 位数字的 ID,您需要 space 作为空终止符。结构定义中的相同问题。

  • 因为您没有提供要存储到这些数组中的最大字节数,fscanf() 将空终止符存储在 aux_id 数组的末尾之后,导致未定义的行为这解释了输出。

  • 参数 head 未在函数 read 中使用,并在 main() 中未初始化传递。

  • 您没有为空终止符分配足够的 space。您应该使用:

    p->name = malloc(strlen(aux_name) + 1);
    strcpy(p->name, aux_name);
    

或者简单地说:

  p->name = strdup(aux_name);
  • 另请注意,将函数命名为 readwrite 可能会导致问题,因为这些名称在 C 库中用于系统调用包装器。
  • 无需打开文件进行更多更新:只需使用 "r""w"
  • 您不检查 fopen()malloc() 失败。

这是修改后的版本:

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

struct product {
    char id[7];
    char *name;
    int price;
    struct product *next;
};

struct product *read(FILE *input) {
    struct product *head = NULL, *tail = NULL, *p;
    char buf[100];
    char aux_id[7];
    char aux_name[20];
    int aux_price;

    while (fgets(buf, sizeof buf, input)) {
        if (sscanf(buf, "%6s %19s %d", aux_id, aux_name, &aux_price) != 3) {
            fprintf(stderr, "line format error: %s", buf);
            continue;
        }
        p = (struct product *)malloc(sizeof(struct product));
        if (p == NULL) {
            fprintf(stderr, "memory allocation failure");
            break
        }
        strcpy(p->id, aux_id);
        p->name = strdup(aux_name);
        if (p->name == NULL) {
            fprintf(stderr, "memory allocation failure");
            free(p);
            break;
        }
        p->price = aux_price;
        p->next = NULL;
        if (head == NULL) {
            head = p;
        } else {
            tail->next = p;
        }
        tail = p;
    }
    return head;
}

void write(struct product *head, FILE *output) {
    struct product *p = head;
    while (p != NULL) {
        fprintf(output, "%s %s %d\n", p->id, p->name, p->price);
        p = p->next;
    }
}

int main() {
    struct product *head;
    FILE *input = fopen("input.txt", "r");
    if (input == NULL) {
        fprintf(stderr, "cannot open input file\n");
        return 1;
    }
    FILE *output = fopen("output.txt", "w");
    if (output == NULL) {
        fprintf(stderr, "cannot open output file\n");
        return 1;
    }
    head = read(head, input);
    write(head, output);
    fclose(input);
    fclose(output);
    return 0;
}

strdup() 是大多数系统上可用的 POSIX 函数,将包含在下一版本的 C 标准中。如果你的系统没有,可以这样写:

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

char *strdup(const char *s) {
    size_t size = strlen(s) + 1;
    char *p = malloc(size);
    if (p == NULL)
        return NULL;
    return memcpy(p, s, size);
}

如果 char aux_id[6]; 的长度为 6,则“332312”太大,您需要 space 终止符号 '\0' 才能与字符串函数一起使用。

这里有两个问题,都是因为没有考虑字符串的终止空字节。

由于没有留下足够的空间 space,您写入的内容超出了存储字符串的数组/分配内存的边界。这会触发 undefined behavior,它表现为您在输出文件中看到的乱码输出。

第一个在这里(在两个地方):

malloc(strlen(aux_name)*(sizeof(char)));

这应该是:

malloc(strlen(aux_name)*(sizeof(char)) + 1);

第二个在你的结构中,也在 read:

char id[6];

应该是:

char id[7];

后一种情况最有可能是输出文件看起来如此的原因,因为在结构中写入/读取过去的 id 可能会踩到 name

此外,readwrite是库函数的名称。您应该将它们重命名为与它们不冲突的其他名称。您还应该在使用完毕后释放分配的内存。