You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C语言中输入字符串末尾加空格后循环终止问题求助

问题分析与修复方案

嘿,咱们来拆解下你的代码问题,顺便解决那个“末尾加空格后循环就卡壳”的事儿。你的代码里有几个关键错误,不仅导致了循环问题,还会引发内存bug和逻辑混乱:

1. 栈内存复用导致的无效引用

你直接把局部数组sentenceInput的地址赋值给words->pWord,然后又用strtok修改这个数组的内容。但sentenceInput是栈上的变量,每次fgets循环都会覆盖它,这会导致之前链表节点里的pWord指针全部指向同一块栈内存——最终所有节点的内容都会变成最后一次输入的句子,甚至程序运行到后面会出现野指针错误。

2. 句子节点的创建逻辑完全跑偏

你的需求是每行句子对应一个链表节点,但现在的代码是每个单词创建一个句子节点——每次调用strtok拿到一个单词,就遍历整个链表去添加新的sentence节点,这完全违背了你的初衷,也会把链表结构彻底搞乱。

3. strtok与循环终止条件的冲突

当你在句子末尾加空格时,strtok会自动忽略末尾的分隔符,但你的内层循环是处理单词,而外层循环的终止条件是sentenceInput[0] != '\n'——如果用户输入“hello world ”(末尾带空格)然后回车,fgets会读取到包含空格和换行的字符串,sentenceInput[0]不是换行,外层循环本应继续,但前面的逻辑错误已经把程序状态搞乱,导致循环异常终止。

4. 未初始化的结构体字段与内存泄漏

每次malloc出来的word结构体,你只设置了pWord字段,numOfChar、posOfWord、nextWord都没初始化,这会导致后续使用时出现未定义行为。同时,你每次处理单词都新建word和sentence,但没有正确管理链表,会造成严重的内存泄漏。


修复后的代码

下面是修正后的代码,完美实现每行句子对应一个链表节点,每个句子节点里存储该句子的所有单词链表:

// 辅助函数:计算字符串长度
int getStrLength(const char* str) {
    int len = 0;
    while(str[len] != '\0') len++;
    return len;
}

// 辅助函数:复制字符串到堆内存
char* copyString(const char* str) {
    int len = getStrLength(str);
    char* newStr = (char*)malloc(len + 1);
    if(newStr == NULL) return NULL;
    for(int i=0; i<=len; i++) {
        newStr[i] = str[i];
    }
    return newStr;
}

// 创建单词节点
word* createWord(const char* wordStr, int pos) {
    word* newWord = (word*)malloc(sizeof(word));
    if(newWord == NULL) return NULL;
    newWord->pWord = copyString(wordStr);
    newWord->numOfChar = getStrLength(wordStr);
    newWord->posOfWord = pos;
    newWord->nextWord = NULL;
    return newWord;
}

// 创建句子节点(包含该句子的单词链表)
sentence* createSentence(word* firstWord, int wordCount, int linePos) {
    sentence* newSentence = (sentence*)malloc(sizeof(sentence));
    if(newSentence == NULL) return NULL;
    newSentence->wordList = firstWord;
    newSentence->totalWords = wordCount;
    newSentence->posOfLine = linePos;
    newSentence->nextSentence = NULL;
    return newSentence;
}

void addSentence(control* list) {
    printf("Enter a Sentence (press Enter alone to stop):\n");
    char sentenceInput[1000];
    int linePos = list->sentenceCounter + 1; // 记录句子的位置序号

    while(fgets(sentenceInput, 1000, stdin) != NULL) {
        // 去除fgets读取到的换行符
        size_t len = strlen(sentenceInput);
        if(len > 0 && sentenceInput[len-1] == '\n') {
            sentenceInput[len-1] = '\0';
            len--;
        }

        // 输入为空(只按了回车),终止循环
        if(len == 0) {
            break;
        }

        // 构建当前句子的单词链表
        word* firstWord = NULL;
        word* currentWord = NULL;
        int wordCount = 0;
        int wordPos = 1;

        // 分割句子为单词
        char* token = strtok(sentenceInput, " ");
        while(token != NULL) {
            word* newWord = createWord(token, wordPos);
            if(newWord == NULL) {
                printf("Memory allocation failed for word!\n");
                // 这里可以添加内存清理逻辑,避免泄漏
                return;
            }

            // 将单词节点加入链表
            if(firstWord == NULL) {
                firstWord = newWord;
                currentWord = newWord;
            } else {
                currentWord->nextWord = newWord;
                currentWord = newWord;
            }

            wordCount++;
            wordPos++;
            token = strtok(NULL, " ");
        }

        // 创建当前句子的节点
        sentence* newSentence = createSentence(firstWord, wordCount, linePos);
        if(newSentence == NULL) {
            printf("Memory allocation failed for sentence!\n");
            // 内存清理逻辑
            return;
        }

        // 将句子节点加入主链表
        if(list->firstSentence == NULL) {
            list->firstSentence = newSentence;
        } else {
            // 找到链表末尾
            sentence* currentSentence = list->firstSentence;
            while(currentSentence->nextSentence != NULL) {
                currentSentence = currentSentence->nextSentence;
            }
            currentSentence->nextSentence = newSentence;
        }

        // 更新计数器和位置序号
        list->sentenceCounter++;
        linePos++;

        printf("Enter a Sentence (press Enter alone to stop):\n");
    }
}

关键修复点说明

  • 字符串复制:不再直接使用栈内存指针,而是通过copyString为每个单词分配独立的堆内存,彻底避免内容覆盖和无效引用问题。
  • 逻辑修正:先把当前句子的所有单词整理成word链表,再创建对应的sentence节点添加到主链表,完全符合“每行句子对应一个节点”的需求。
  • 换行符处理:手动去除fgets读取到的换行符,避免句子末尾带换行,同时正确识别空输入(只按回车)来终止循环。
  • 结构体初始化:所有结构体字段都做了初始化,杜绝未定义行为。
  • 内存管理:每个节点都正确分配内存,后续你可以添加对应的内存释放函数(比如freeSentenceList)来彻底避免内存泄漏。

现在你再测试末尾加空格的情况,程序会正常处理,循环也能继续执行,直到你输入空行(只按回车)才会终止。

内容的提问来源于stack exchange,提问作者hitthechooch

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.11 07:42:19