C语言二维数组重分配失效:第11个元素触发段错误
我来帮你搞定这个段错误的问题!你的代码里有几个关键的内存分配问题,直接导致了访问越界触发崩溃,下面我逐个给你分析并修正:
1. 内存分配时的sizeof用错了!
在vytvorPole和realokuj函数里,你分配每行内存的时候用了sizeof(*grid),但*grid是char**类型(指向指针的指针),它的大小是系统指针的长度(比如64位系统是8字节),而我们需要的是char类型的数组,应该用sizeof(char)或者更通用的sizeof(**grid)(因为**grid就是char类型)。
错误代码片段:
(*grid)[i]=(char *) malloc (ncols*sizeof(*grid)); // realokuj里同样的错误 (docasne)[i]=(char *) malloc (ncols*sizeof(*grid));
修正后:
(*grid)[i] = malloc(ncols * sizeof(**grid)); // 或者直接写sizeof(char),因为char的大小固定是1字节 (*grid)[i] = malloc(ncols * sizeof(char));
这个错误会导致每行分配的内存远大于实际需要的空间,虽然看起来不会直接崩溃,但后续如果输入的字符串长度超过预期,或者内存分配出现碎片,就容易触发越界访问。
2. realokuj没处理realloc失败的情况
realloc在内存不足时会返回NULL,如果直接把这个NULL赋值给*grid,会丢失原来的内存指针,而且后续访问docasne[i]时,相当于访问NULL的偏移,直接触发段错误。另外,分配新行内存时也没检查malloc是否成功,同样会有风险。
修正后的realokuj函数:
int realokuj(char ***grid, int nrows, int ncols) { // 把返回值改成int更合理,方便返回错误码 char **docasne = realloc(*grid, nrows * sizeof(*grid)); if (docasne == NULL) { printf("ERROR: 内存扩容失败\n"); return 1; } // 给新增的行分配内存 for(int i = nrows/2; i < nrows; i++) { docasne[i] = malloc(ncols * sizeof(**grid)); if (docasne[i] == NULL) { printf("ERROR: 第%d行内存分配失败\n", i); // 回滚:释放已经分配的新行,避免内存泄漏 for(int j = nrows/2; j < i; j++) { free(docasne[j]); } free(docasne); return 1; } } *grid = docasne; return 0; }
3. vytvorPole的内存泄漏问题
原函数里如果某一行分配失败,已经分配的行内存没有被释放,会造成内存泄漏。而且返回值用char类型不太规范,建议改成int。
修正后的vytvorPole:
int vytvorPole(char ***grid, int nrows, int ncols) { *grid = malloc(nrows * sizeof(*grid)); if (*grid == NULL) { printf("ERROR: 数组内存分配失败\n"); return 1; } for(int i = 0; i < nrows; i++) { (*grid)[i] = malloc(ncols * sizeof(**grid)); if ((*grid)[i] == NULL) { printf("ERROR: 第%d行内存分配失败\n", i); // 释放已经分配的所有行 for(int j = 0; j < i; j++) { free((*grid)[j]); } free(*grid); *grid = NULL; // 避免野指针 return 1; } } return 0; }
4. getline读取的换行符可能导致越界
getline会把输入的换行符\n也读进字符串里,strlen(str)会包含这个换行符的长度。如果后续用strcpy复制到数组,当输入的字符串长度(不含换行)刚好等于cDiagonaly时,加上换行符就会超出分配的内存,触发段错误。
可以在读取后去掉换行符,并且分配内存时多留一个位置存字符串结束符\0:
while (getline(&str, &capacity, stdin) != -1) { size_t len = strlen(str); // 去掉换行符 if (len > 0 && str[len-1] == '\n') { str[len-1] = '\0'; len--; } // 空行就退出循环 if (len == 0) break; if (first) { cDiagonaly = len; // 多分配1个字节存字符串结束符 if (vytvorPole(&diagonaly, rDiagonaly, cDiagonaly + 1) != 0) { free(str); return 1; } first = 0; } if (nr == rDiagonaly) { rDiagonaly *= 2; if (realokuj(&diagonaly, rDiagonaly, cDiagonaly + 1) != 0) { free(str); // 清理已分配的内存 for(int i=0; i<nr; i++) free(diagonaly[i]); free(diagonaly); return 1; } } // 用strncpy更安全,避免越界 strncpy(diagonaly[nr], str, cDiagonaly); diagonaly[nr][cDiagonaly] = '\0'; // 手动添加结束符 nr++; }
完整修正后的代码
把所有修正整合起来,完整代码如下:
#include <stdio.h> #include <stdlib.h> #include <string.h> int vytvorPole(char ***grid, int nrows, int ncols) { *grid = malloc(nrows * sizeof(*grid)); if (*grid == NULL) { printf("ERROR: 数组内存分配失败\n"); return 1; } for(int i = 0; i < nrows; i++) { (*grid)[i] = malloc(ncols * sizeof(**grid)); if ((*grid)[i] == NULL) { printf("ERROR: 第%d行内存分配失败\n", i); for(int j = 0; j < i; j++) { free((*grid)[j]); } free(*grid); *grid = NULL; return 1; } } return 0; } int realokuj(char ***grid, int nrows, int ncols) { char **docasne = realloc(*grid, nrows * sizeof(*grid)); if (docasne == NULL) { printf("ERROR: 内存扩容失败\n"); return 1; } for(int i = nrows/2; i < nrows; i++) { docasne[i] = malloc(ncols * sizeof(**grid)); if (docasne[i] == NULL) { printf("ERROR: 第%d行内存分配失败\n", i); for(int j = nrows/2; j < i; j++) { free(docasne[j]); } free(docasne); return 1; } } *grid = docasne; return 0; } int main (void) { char **diagonaly = NULL; int rDiagonaly = 10; int cDiagonaly = -1; char *str = NULL; size_t capacity = 0; int first = 1; int nr = 0; printf("Vypln:\n"); while (getline(&str, &capacity, stdin) != -1) { size_t len = strlen(str); if (len > 0 && str[len-1] == '\n') { str[len-1] = '\0'; len--; } if (len == 0) break; if (first) { cDiagonaly = len; if (vytvorPole(&diagonaly, rDiagonaly, cDiagonaly + 1) != 0) { free(str); return 1; } first = 0; } if (nr == rDiagonaly) { rDiagonaly *= 2; if (realokuj(&diagonaly, rDiagonaly, cDiagonaly + 1) != 0) { free(str); for(int i=0; i<nr; i++) free(diagonaly[i]); free(diagonaly); return 1; } } strncpy(diagonaly[nr], str, cDiagonaly); diagonaly[nr][cDiagonaly] = '\0'; nr++; } // 测试输出验证 printf("\n你输入的内容:\n"); for(int i=0; i<nr; i++) { printf("%s\n", diagonaly[i]); free(diagonaly[i]); } free(diagonaly); free(str); return 0; }
总结
你遇到的段错误核心原因是内存分配时sizeof的错误使用,导致内存分配不符合预期,加上没有处理内存分配失败的情况,最终引发越界访问。修正这些问题后,代码就能正常实现二维数组的扩容功能了。
内容的提问来源于stack exchange,提问作者user14725090

