C语言中按字母排序字符串数组并避免原数组修改的问题及代码排查
如何在C语言中按字母顺序排序字符串数组且不修改原数组?
咱们来一步步拆解你的问题,先看看代码里的几个关键错误,然后给出正确的实现方式。
你的代码里的核心错误
- 内存分配错误:你声明的
sortedArray是char*类型,但实际上你需要的是一个指针数组(char**)来存储多个字符串的指针。malloc(size)只分配了size个字节,这远远不够存储size个指针(比如64位系统中每个指针占8字节,4个元素就需要32字节,但你只分配了4字节)。 - 字符串复制方式错误:
snprintf(&sortedArray[i], size, "%s", data[i])完全不符合预期——你试图把字符串直接写到指针数组的内存空间里,这会破坏指针的地址值,导致后续操作出现未定义行为。正确的做法是为每个字符串单独分配内存,然后复制内容。 - 函数返回值类型不匹配:函数声明返回
char*,但你实际需要返回的是字符串数组(char**),类型不匹配会导致编译错误或者运行时崩溃。 - main函数的未定义行为:
char *names[size];创建的是一个指针数组,但每个元素都是未初始化的野指针,scanf("%s", names[i])会直接往野指针指向的内存写数据,大概率会导致程序崩溃。
修正后的实现方案
要实现不修改原数组的排序,核心是创建原数组的深度副本:先创建一个新的指针数组,然后为每个字符串分配独立的内存并复制内容,最后对这个新数组排序。
完整修正代码
#include <stdio.h> #include <stdlib.h> #include <string.h> /** * sort data alphabetically without modifying original array * @param data[] array of strings * @param size array size * @return an alphabetically sorted array (needs to be freed properly) */ char **sortAlphabetically(char *data[], int size) { // 1. 分配指针数组的内存:size个char*元素 char **sortedArray = malloc(size * sizeof(char *)); if (!sortedArray) { return NULL; } // 2. 深度复制原数组的每个字符串 for (int i = 0; i < size; i++) { // 为每个字符串分配内存,长度是原字符串长度+1(存'\0') sortedArray[i] = malloc(strlen(data[i]) + 1); if (!sortedArray[i]) { // 如果分配失败,要释放已经分配的内存,避免内存泄漏 for (int j = 0; j < i; j++) { free(sortedArray[j]); } free(sortedArray); return NULL; } // 复制字符串内容 strcpy(sortedArray[i], data[i]); } // 3. 冒泡排序(可以换成更高效的qsort) for (int index = 0; index < size; index++) { for (int nextIndex = index + 1; nextIndex < size; nextIndex++) { if (strcmp(sortedArray[index], sortedArray[nextIndex]) > 0) { // 交换指针,比交换字符串内容高效 char *temp = sortedArray[index]; sortedArray[index] = sortedArray[nextIndex]; sortedArray[nextIndex] = temp; } } } return sortedArray; } // 可选:如果你的环境不支持strdup,可以自己实现一个 // char *strdup(const char *s) { // size_t len = strlen(s) + 1; // char *dup = malloc(len); // if (dup) { // strcpy(dup, s); // } // return dup; // } int main() { int size = 4; // 为每个名字分配足够的内存,这里假设名字最长不超过255字符 char *names[size]; for (int i = 0; i < size; i++) { names[i] = malloc(256 * sizeof(char)); if (!names[i]) { fprintf(stderr, "Memory allocation failed\n"); // 释放已分配的内存 for (int j = 0; j < i; j++) { free(names[j]); } return 1; } printf("(%d) Enter a name: ", i); // 使用scanf限制输入长度,避免缓冲区溢出 scanf("%255s", names[i]); } char **result = sortAlphabetically(names, size); if (result) { printf("Sorted names:\n"); for (int i = 0; i < size; i++) { printf("%s\n", result[i]); // 先释放每个字符串的内存 free(result[i]); } // 再释放指针数组的内存 free(result); } // 释放原数组的内存 for (int i = 0; i < size; i++) { free(names[i]); } return 0; }
优化建议
- 使用更高效的排序算法:上面的冒泡排序效率较低,对于较大的数组,建议使用标准库的
qsort函数,代码会更简洁且高效:// 替换冒泡排序的部分 qsort(sortedArray, size, sizeof(char*), (const void*)strcmp); // 直接用strcmp作为比较函数 - 内存管理注意事项:调用完
sortAlphabetically后,必须先逐个释放每个字符串的内存,再释放指针数组的内存,否则会造成内存泄漏。
避免原数组被修改的最佳方式
- 深度复制原数组:不要只复制指针(浅复制),因为浅复制的话,新数组的指针还是指向原字符串的内存,虽然原数组的指针不会被修改,但如果排序过程中修改了字符串内容(比如交换内容而不是指针),还是会影响原数组。而深度复制每个字符串的内容,能完全隔离原数组和新数组。
- 排序时交换指针而非字符串内容:这样不仅效率更高(不需要移动大量字符),还能保证原字符串的内容完全不受影响。
内容的提问来源于stack exchange,提问作者Ismail
相关产品推荐
相关产品推荐

