我有一个用C编写的控制台应用程序,它接受用户的输入以操作链表.

当用户输入"Add"来添加一个项目时,它将添加该项目,但以这种方式添加的所有 node 都将共享使用"Add"命令创建的最后一个项目的名称.

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

我没有使用textInput来扫描用户输入,而是try 为此创建另一个array.在这方面找不到任何东西,也试图询问ChatGPT,但没有帮助.

推荐答案

以下是这些 comments 的主要摘要:

addLast(&list, uInput, numInput)

而反过来,

(*thing)->name = name;

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


list->next->name = "james";addLast(&list, "jane", 30);不存在这个问题,因 for each 字符串都是指向不同的1、静态分配的只读字符数组的指针.


相反,您必须 for each 字符串创建一个copy,并在每个关联 node 中存储一个指向副本的指针.

这可以通过为the length of the string分配内存(以字节为单位),再为null-terminating character('\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都是可用的,它就是这样做的.

请注意,使用这种方法,通过混合指向动态分配的字符串(malloc)的指针和指向静态分配的字符串(字符串文字)的指针来创建列表,会在达到free时产生问题.一般建议是避免将这两种类型的指针混为一谈.

(101 would be fine in the example below, wheres 102 would lead to 100.)


或者,将name成员更改为数组,并将输入复制到其中.使用这种方法要当心buffer overflows人.


其他问题包括:

单个free(list);只为列表中的first个 node 释放内存.

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

对用户输入使用scanf(scanf_s)可能会在输入错误时导致令人沮丧的结果,并且输入缓冲区变得一团糟.一般的建议是用fgets将整个lines的输入读入缓冲区,然后以您想要的方式处理数据(通常是sscanfstrtolstrtok等).这不是万无一失的,但它确实大大简化了问题.

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


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

否则,将对其进行相当程度的重构(为简洁起见进行简化).

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.相同string value的字符串文字可以占用相同的存储器,也可以不占用相同的存储器(即,不保证两个pointers到字符串文字的比较相等,即使在内容方面相同).Ref.

C++相关问答推荐

VS代码C/C++扩展intellisense无法检测环境特定函数'

丑陋的三重间接:可扩展的缓冲区管理 struct

__VA_OPT__(,)是否可以检测后面没有任何内容的尾随逗号?

在CLANG中调试预处理器宏

在每种If-Else情况下执行语句的最佳方式

为什么数组的最后一个元素丢失了?

ifdef __cplusplus中的整数文字单引号

在编写代码时,Clion比vscode有更多的问题指示器

可变宏不能编译

正数之和是负数

试图创建一个基本的Word克隆,但遇到了障碍

我在C程序的Flex/Bison中遇到语法错误

Go和C中的数据 struct 对齐差异

合并对 struct 数组进行排序

从不兼容的指针类型返回&&警告,但我看不出原因

分配给静态变量和动态变量的位置之间有区别吗?

在C中,为什么这个带有递增整数的main函数从不因溢出而崩溃?

指向返回 struct 成员的指针,安全吗?

如何用用户输入的多个字符串填充数组?

仅使用其内存地址取消引用 C 中的 struct