向链表添加内容时,列表中的其他项目采用最后添加的项目的名称

When adding something to linked list, the other items on the list take the name of the last item added

提问人:danilogames220 提问时间:10/22/2023 最后编辑:chqrliedanilogames220 更新时间:10/23/2023 访问量:79

问:

我有一个用 C 编写的控制台应用程序,它从用户那里获取输入以操作链表。

当用户输入“add”添加项目时,它将添加该项目,但以这种方式添加的所有节点将共享使用“add”命令创建的最后一个项目的名称。

列表.h

typedef struct node {
    char *name;
    int value;
    struct node *next;
} node_js;

static node_js *currentNode;

void printList(node_js *thing) {
    currentNode = thing;

    while (currentNode != NULL) {
        printf("%s\n", currentNode->name);
        printf(">%d\n", currentNode->value);

        currentNode = currentNode->next;
    }
    printf("\n");
}

void addLast(node_js **thing, const char *name, int val) {
    if (*thing == NULL) {
        *thing = (node_js *)malloc(sizeof(node_js));
        //(*thing)->name = name;
        (*thing)->name = name;
        (*thing)->value = val;
        (*thing)->next = NULL;
    } else {
        currentNode = *thing;

        while (currentNode->next != NULL) {
            currentNode = currentNode->next;
        }

        currentNode->next = (node_js *)malloc(sizeof(node_js));
        //currentNode->next->name = strdup(name);
        currentNode->next->name = name;
        currentNode->next->value = val;
        currentNode->next->next = NULL;
    }
}

void removeLast(node_js **thing) {
    if (*thing == NULL) {
        return;
    }

    if ((*thing)->next == NULL) {
        free(*thing);
        *thing = NULL;
        return;
    }

    currentNode = *thing;

    while (currentNode->next->next != NULL) {
        currentNode = currentNode->next;
    }

    free(currentNode->next);
    currentNode->next = NULL;
}

main.c

//the problem is on the addLast() inside the while(1), the probelm only occur with items that are
//added when user inputs "add"
#include <stdio.h>
#include <stdlib.h>
#include "list.h"

static int numInput;
static char textInput[32];

static char uInput[16];

node_js *list = NULL;

int main() {
     printf("--- Welcome to the program! ---\n>Type 'exit', 'quit' or 'q' to exit\n>Type 'help' for a list of the commands\n\n");

    //make list
    list = (node_js *)malloc(sizeof(node_js));

    list->name = "joe";
    list->value = 10;
    list->next = (node_js *)malloc(sizeof(node_js));

    list->next->name = "james";
    list->next->value = 20;
    list->next->next = NULL;

    addLast(&list, "jane", 30); //here it works fine

    while (1) {
        printf("input: ");
        scanf_s("%s", textInput, sizeof(textInput));

        if (strcmp(textInput, "quit") == 0 || strcmp(textInput, "q") == 0 || strcmp(textInput, "exit") == 0) {
            printf("Bye!");
            free(list);
            exit(1);
        }
        if (strcmp(textInput, "print") == 0) {
            printList(list);
        }
        if (strcmp(textInput, "add") == 0) {
            printf("name: ");
            scanf_s("%s", uInput, sizeof(uInput));/*the problem is on the addLast() inside the while(1), the probelm only occur with items that are added when user inputs "add"*/
            printf("number: ");
            scanf_s("%d", &numInput, sizeof(numInput));

            addLast(&list, uInput, numInput);
            printf("--- added (%s, %d) ---\n", uInput, numInput);   
        }
        if (strcmp(textInput, "remove") == 0) {
            removeLast(&list);
            printf("--- Removed last item of list ---\n");
        }
    }
    free(list);
    return 0;
}

我没有使用扫描用户输入,而是尝试为此制作另一个数组。找不到任何关于此的内容,还尝试询问 ChatGPT,但没有帮助。textInput

数组 c 字符串 链接列表

评论

4赞 pmg 10/22/2023
您总是将“的地址”添加到新节点。所有节点都具有相同的地址,无论该地址的内容在添加到链表时是否不同。您需要分配内存并从 复制。uInputuInput
1赞 pmg 10/22/2023
不相关:从“list.h”中删除代码;仅将声明保留在头文件中,将代码移动到(可能)“list.c”
1赞 William Pursell 10/23/2023
风格建议。而不是 , do(*thing)->name = name;struct node *t = *thing; t->name = name;
0赞 danilogames220 10/23/2023
@pmg我该怎么做呢?我对 C 字符串了解不多
0赞 pmg 10/23/2023
这不是那个地方......但不是你想要的.另外,不要忘记free((*thing)->name); 一旦你完成了对它们的工作(*thing)->name = name;(*thing)->name = malloc(strlen(name) + 1); /* check if ok */ strcpy((*thing)->name, name);

答:

2赞 Oka 10/23/2023 #1

大致总结一下评论:

addLast(&list, uInput, numInput)

反过来,

(*thing)->name = name;

将每个节点的成员设置为相同的指针值:指向 中存在的数组的第一个元素的指针。每次使用命令时,都会覆盖 的内容,结果是列表中的所有节点共享相同的字符串,该字符串始终是最近的输入名称。nameuInputmainuInputadd


list->name = "joe";、 和 不要共享此问题,因为每个字符串文本都是指向不同的1、静态分配的只读字符数组的指针。list->next->name = "james";addLast(&list, "jane", 30);


相反,您必须创建每个字符串的副本,并在每个关联节点中存储指向该副本的指针。

这可以通过为字符串的长度(以字节为单位)分配内存,并为 null 终止字符 () 分配一个额外的字节来完成。'\0'

一般模式是

char *create_copy_of_a_string(const char *a_string)
{
    char *copy = malloc(1 + strlen(a_string));

    if (copy)
        strcpy(copy, a_string);

    return copy;
}

在许多平台上(例如,POSIX和即将到来的C23)都可用,它就是这样做的。strdup

请注意,使用这种方法时,通过将指向动态分配的字符串 () 的指针和指向静态分配的字符串(字符串文字)的指针混合来创建列表,当涉及到前者时,会产生问题。一般建议是避免混合使用这两种类型的指针。mallocfree

(addLast(&list, “简”, 30);在下面的示例中会很好,其中 list->name = “joe”; 将导致未定义的行为)


或者,将成员更改为数组,并将输入复制到该数组。使用此方法时,请注意缓冲区溢出name


其他问题包括:

single 仅释放列表中第一个节点的内存。free(list);

代码的一般重复(特别是)。addLast

当输入错误的内容时,将 () 用于用户输入可能会导致令人沮丧的结果,并且输入缓冲区变得一团糟。一般的建议是将整输入读入缓冲区,然后根据需要处理该数据(通常为 、、 等)。这不是万无一失的,但它确实大大简化了问题。scanfscanf_sfgetssscanfstrtolstrtok

函数的实现应放在文件 () 中,而不是文件 () 中。.c.h


在下面的粗略示例中,库函数 (, , ) 的签名保持不变。这并没有为干净地处理/传播错误留下太多空间,因此错误处理在很大程度上是疏忽的。特别是,健壮的程序应处理 () 返回的事件,并避免取消引用空指针值。printListaddLastremoveLastmalloccallocNULL

否则,它会被重构相当多(为了简洁起见,进行了简化)。

list.h:

#ifndef LIST_H
#define LIST_H

typedef struct node {
    char *name;
    int value;
    struct node *next;
} node_js;

void printList(node_js *);
void addLast(node_js **, const char *, int);
void removeLast(node_js **);

#endif

list.c:

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

#include "list.h"

void printList(node_js *node)
{
    while (node) {
        printf("<%s> <%d>\n", node->name, node->value);
        node = node->next;
    }
}

void addLast(node_js **head, const char *name, const int value)
{
    node_js *node = calloc(1, sizeof *node);

    /* create a copy of the string */
    node->name = malloc(1 + strlen(name));
    strcpy(node->name, name);

    node->value = value;

    if (!*head)
        *head = node;
    else {
        node_js *current = *head;

        while (current->next)
            current = current->next;

        current->next = node;
    }
}

void removeLast(node_js **node)
{
    if (!*node)
        return;

    while ((*node)->next)
        node = &(*node)->next;

    free((*node)->name);
    free(*node);
    *node = NULL;
}

main.c:

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

#define match(x, y) (0 == strcmp((x), (y)))

static int read_a_line(char *buf, int len)
{
    if (!fgets(buf, len, stdin))
        return 0;

    /* remove the newline character, if present */
    buf[strcspn(buf, "\n")] = 0;

    return 1;
}

static int add_to_list(node_js **list)
{
    char name[256];
    char value[256];

    printf("Name: ");
    if (!read_a_line(name, sizeof name))
        return 0;

    printf("Value: ");
    if (!read_a_line(value, sizeof value))
        return 0;

    /* assume this succeeds */
    addLast(list, name, strtol(value, NULL, 10));
    return 1;
}

int main(void)
{
    node_js *list = NULL;

    puts("------- Welcome to the program! -------");

    while (1) {
        char selection[256];

        printf("Enter a command (quit, print, add, remove): ");

        if (!read_a_line(selection, sizeof selection) || match(selection, "quit"))
            break;

        if (match(selection, "print"))
            printList(list);
        else if (match(selection, "add"))
            add_to_list(&list);
        else if (match(selection, "remove"))
            removeLast(&list);
        else
            printf("Invalid selection <%s>\n", selection);
    }

    while (list) {
        node_js *next = list->next;
        free(list->name);
        free(list);
        list = next;
    }

    puts("Goodbye.");
}

使用中:

------- Welcome to the program! -------
Enter a command (quit, print, add, remove): add
Name: foo
Value: 123
Enter a command (quit, print, add, remove): add
Name: bar
Value: 456
Enter a command (quit, print, add, remove): add
Name: qux
Value: 789
Enter a command (quit, print, add, remove): print
<foo> <123>
<bar> <456>
<qux> <789>
Enter a command (quit, print, add, remove): remove
Enter a command (quit, print, add, remove): print
<foo> <123>
<bar> <456>
Enter a command (quit, print, add, remove): quit
Goodbye.

1. 相同字符串值的字符串文字可能占用也可能不占用相同的内存(即,即使内容相等,也不能保证指向字符串文字的两个指针比较相等)。裁判。

评论

0赞 chqrlie 10/23/2023
相当全面的答案!我希望我能投更多的赞成票......(您可能希望测试和报告分配失败:)