C语言char*分割为char**时触发Segmentation fault段错误排查
段错误核心原因
你的代码存在两处明确的未定义行为,这是故障偶现、valgrind环境下无法复现的根本原因:
- 非法释放非堆内存
查看main函数中的逻辑:
你先通过malloc申请了100字节堆内存,紧接着直接将char *buffer = malloc(sizeof(char) * 100); buffer = "hello world !\n"; // ... free (buffer);buffer指针指向了只读数据段存储的字符串常量,不仅造成了之前申请的堆内存泄漏,后续调用free(buffer)时,释放的根本不是堆地址,而是只读常量区的地址,会直接破坏进程堆的管理结构。
第一次调用get_words不崩溃属于概率事件:堆结构被破坏后,第二次调用malloc为words分配内存时,极有可能返回非法地址,访问时就会触发段错误。valgrind会替换默认的内存分配实现,对非法free操作的容错性更高,因此不会立刻崩溃。 - 潜在的只读内存写入风险
你直接将字符串字面量作为参数传入get_words,这类常量存储在只读内存页中,只要出现写入操作就会立刻触发段错误。虽然当前版本的get_words没有修改入参内容,但后续迭代如果新增修改入参的逻辑,会直接触发故障。 - 额外的越界隐患
你当前固定为words数组分配4096个char*指针、每个指针分配4096字节,内存浪费严重;如果输入的单词数超过4096、单个单词长度超过4095(需预留1字节存\0),就会出现数组越界,同样会触发段错误。
修复方法
- 修正字符串赋值与内存释放逻辑:如果要将字符串存入malloc申请的堆缓冲区,使用
strcpy完成拷贝,禁止直接修改指针指向;仅对自己申请的堆地址执行free操作:// 错误写法 // char *buffer = malloc(sizeof(char) * 100); // buffer = "hello world !\n"; // 正确写法 char *buffer = malloc(sizeof(char) * 100); strcpy(buffer, "hello world !\n"); // 使用完buffer后再释放 free(buffer); - 为get_words增加边界校验:如果保留预分配逻辑,需要判断当前写入的单词数、单单词长度是否超出预分配的内存大小,避免越界写入。更合理的实现是先遍历输入统计单词数、每个单词的长度,按需分配内存,避免空间浪费和越界风险。
- 明确只读入参的属性:如果get_words不会修改传入的buffer内容,将形参类型改为
const char *buffer,从编译层面拦截误修改只读内存的操作。 - 配套实现words数组的释放函数,避免内存泄漏。
修复后可稳定运行的示例代码
#include <unistd.h> #include <stdio.h> #include <string.h> #include <stdlib.h> #include <fcntl.h> #include <assert.h> char **get_words(const char *buffer, char delimiter) { printf("buffer = %s\n", buffer); char **words = malloc(sizeof(char *) * 100); if (words == NULL) { printf("Malloc Error\n"); exit(84); } for (int i = 0; i < 100; i++) { words[i] = malloc(sizeof(char) * 100); if (words[i] == NULL) { printf("Malloc Error\n"); exit(84); } } int word_count = 0; int l = 0; for (int i = 0; buffer[i] != '\0' && buffer[i] != '\n'; i++, l++) { // 增加边界判断,避免越界 if (word_count >= 99 || l >= 99) break; if (buffer[i] == delimiter) { words[word_count][l] = '\0'; word_count++; l = -1; } else words[word_count][l] = buffer[i]; } words[word_count][l] = '\0'; // 末尾置NULL标记数组结束 words[word_count + 1] = NULL; return words; } void free_words(char **words) { for (int i = 0; i < 100; i++) { free(words[i]); } free(words); } int main() { char *buffer = malloc(sizeof(char) * 100); strcpy(buffer, "hello world !\n"); char **words = get_words(buffer, ' '); printf("words[0]= %s\n", words[0]); free(buffer); free_words(words); char **reply = get_words("Second call\n", ' '); printf("reply[0] = %s\n", reply[0]); free_words(reply); return 0; }
内容的提问来源于stack exchange,提问作者cricciardi
相关产品推荐
相关产品推荐

