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; }
关键改进说明
- 解决野指针问题:用
malloc在堆上分配结果字符串,返回后由调用者负责释放(如示例中的free(output)),避免栈内存回收导致的无效指针。 - 优化check函数:用字符计数法替代排序,时间复杂度从O(n²)降至O(n),同时只计算一次字符串长度,提升性能;逻辑更简洁,减少冗余代码。
- 先收集所有单词:通过复制原字符串并提取所有单词到动态数组,避免
strtok修改原字符串后无法重新分割的问题,分类拼接逻辑更清晰。 - 正确的拼接顺序:先加入第一个单词,再追加其他同字母组成的单词,最后拼接剩余单词,完全符合需求。
- 规范分隔符处理:拼接时用空格分隔单词,保证输出句子格式正确。
- 内存安全保障:添加了内存分配失败的处理逻辑,所有动态分配的内存都在最后释放,避免内存泄漏。
注意事项
- 调用
shiftWords后必须调用free释放返回的字符串,否则会造成内存泄漏。 - 代码中复制了原字符串,因此不会修改输入的原始数据,保留了原字符串的完整性。
- 目前仅支持ASCII字符,若需处理Unicode字符,需调整字符计数的逻辑(如使用宽字符或UTF-8解析)。
内容的提问来源于stack exchange,提问作者meoww
相关产品推荐
相关产品推荐

