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

C风格字符串指针操作求助:实现同字母组成单词前置功能

问题分析与代码修正

原代码的核心问题

  • 返回局部数组导致野指针:shiftWords中的tempAnsStr是栈上的局部数组,函数执行完毕后该内存会被系统回收,返回的指针完全无效,访问会触发未定义行为。
  • 字符串拼接逻辑混乱:直接对strtok返回的tempStr调用strcat是错误的——tempStr指向原字符串中被截断的单个单词(末尾是\0),拼接操作会破坏原字符串的结构;另外nextStr未初始化就调用strlen,直接导致运行时错误。
  • check函数低效冗余:循环中多次调用strlen会重复计算字符串长度,浪费性能;用冒泡排序对比字符的方式可以用更高效的字符计数法替代,代码冗余度高。
  • 遗漏第一个单词:最终结果未包含原句的第一个单词,不符合需求。
  • 缺少分隔符处理:拼接单词时未添加空格或逗号,输出的句子没有单词分隔符,格式错误。

修正后的完整代码

#include <cstring>
#include <cstdlib>
#include <iostream>

// 检查两个单词是否由相同字母组成(不区分顺序)
bool check(const char* A, const char* B) {
    const int lenA = strlen(A);
    const int lenB = strlen(B);
    if (lenA != lenB) {
        return false;
    }

    int charCount[256] = {0}; // 统计ASCII字符出现次数

    // 统计A中每个字符的出现次数
    for (int i = 0; i < lenA; ++i) {
        charCount[(unsigned char)A[i]]++;
    }

    // 用B的字符抵消计数,出现负数直接返回false
    for (int i = 0; i < lenB; ++i) {
        const unsigned char c = (unsigned char)B[i];
        if (--charCount[c] < 0) {
            return false;
        }
    }

    return true;
}

char* shiftWords(char* str) {
    if (!str || strlen(str) == 0) {
        return nullptr;
    }

    // 复制原字符串,避免strtok修改输入的原始数据
    char* strCopy = (char*)malloc(strlen(str) + 1);
    if (!strCopy) {
        return nullptr;
    }
    strcpy(strCopy, str);

    // 提取所有单词到动态数组
    char** words = nullptr;
    int wordCount = 0;
    char* token = strtok(strCopy, ", ");
    while (token != nullptr) {
        words = (char**)realloc(words, (wordCount + 1) * sizeof(char*));
        words[wordCount] = (char*)malloc(strlen(token) + 1);
        strcpy(words[wordCount], token);
        wordCount++;
        token = strtok(NULL, ", ");
    }

    // 计算结果所需总长度:所有单词长度 + 分隔符(wordCount-1个空格) + 终止符
    int totalLen = 1; // 留位置给'\0'
    for (int i = 0; i < wordCount; ++i) {
        totalLen += strlen(words[i]) + 1; // 单词长度 + 空格
    }
    char* result = (char*)malloc(totalLen);
    if (!result) {
        // 内存分配失败,释放已分配的资源
        for (int i = 0; i < wordCount; ++i) {
            free(words[i]);
        }
        free(words);
        free(strCopy);
        return nullptr;
    }
    result[0] = '\0';

    // 第一步:拼接第一个单词和其他同字母组成的单词
    strcpy(result, words[0]);
    for (int i = 1; i < wordCount; ++i) {
        if (check(words[0], words[i])) {
            strcat(result, " ");
            strcat(result, words[i]);
        }
    }

    // 第二步:拼接剩余单词
    bool firstOther = true;
    for (int i = 1; i < wordCount; ++i) {
        if (!check(words[0], words[i])) {
            if (firstOther) {
                strcat(result, " ");
                firstOther = false;
            } else {
                strcat(result, " ");
            }
            strcat(result, words[i]);
        }
    }

    // 释放临时资源
    for (int i = 0; i < wordCount; ++i) {
        free(words[i]);
    }
    free(words);
    free(strCopy);

    return result;
}

// 测试用例
int main() {
    char input[] = "listen, silent, hello, enlist, world";
    char* output = shiftWords(input);
    if (output) {
        std::cout << "处理后结果:" << output << std::endl;
        free(output); // 必须释放堆内存,避免泄漏
    }
    return 0;
}

关键改进说明

  1. 解决野指针问题:用malloc在堆上分配结果字符串,返回后由调用者负责释放(如示例中的free(output)),避免栈内存回收导致的无效指针。
  2. 优化check函数:用字符计数法替代排序,时间复杂度从O(n²)降至O(n),同时只计算一次字符串长度,提升性能;逻辑更简洁,减少冗余代码。
  3. 先收集所有单词:通过复制原字符串并提取所有单词到动态数组,避免strtok修改原字符串后无法重新分割的问题,分类拼接逻辑更清晰。
  4. 正确的拼接顺序:先加入第一个单词,再追加其他同字母组成的单词,最后拼接剩余单词,完全符合需求。
  5. 规范分隔符处理:拼接时用空格分隔单词,保证输出句子格式正确。
  6. 内存安全保障:添加了内存分配失败的处理逻辑,所有动态分配的内存都在最后释放,避免内存泄漏。

注意事项

  • 调用shiftWords后必须调用free释放返回的字符串,否则会造成内存泄漏。
  • 代码中复制了原字符串,因此不会修改输入的原始数据,保留了原字符串的完整性。
  • 目前仅支持ASCII字符,若需处理Unicode字符,需调整字符计数的逻辑(如使用宽字符或UTF-8解析)。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.23 13:35:24