C语言词频统计程序出现IOT指令错误及内存越界问题求助
C语言单词统计程序内存错误排查与解决
问题描述
开发的C语言程序接收字符串参数,按单词出现顺序统计并显示每个单词的出现次数。当字符串包含超过3个不同单词时,程序触发以下错误:double free or corruption (out)IOT instruction (core dumped)
错误信息
double free or corruption (out) [1] 189175 IOT instruction (core dumped) ./test "like test test love love like pop"
Valgrind检测日志
==189553== Memcheck, a memory error detector ==189553== Copyright (C) 2002-2022, and GNU GPL'd, by Julian Seward et al. ==189553== Using Valgrind-3.19.0 and LibVEX; rerun with -h for copyright info ==189553== Command: ./test like\ test\ test\ love\ love\ like\ pop ==189553== Parent PID: 185694 ==189553== ==189553== Invalid write of size 8 ==189553== at 0x10930B: handle_word (test.c:32) ==189553== by 0x1093F6: count_word_occurrences (test.c:49) ==189553== by 0x109523: main (test.c:75) ==189553== Address 0x4a74048 is 0 bytes after a block of size 8 alloc'd ==189553== at 0x4841888: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so) ==189553== by 0x10938B: count_word_occurrences (test.c:43) ==189553== by 0x109523: main (test.c:75) ==189553== ==189553== Invalid write of size 4 ==189553== at 0x109324: handle_word (test.c:33) ==189553== by 0x1093F6: count_word_occurrences (test.c:49) ==189553== by 0x109523: main (test.c:75) ==189553== Address 0x4a74094 is 0 bytes after a block of size 4 alloc'd ==189553== at 0x4841888: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so) ==189553== by 0x109399: count_word_occurrences (test.c:44) ==189553== by 0x109523: main (test.c:75) ==189553== ==189553== Invalid read of size 8 ==189553== at 0x10925B: word_exists (test.c:19) ==189553== by 0x1092BE: handle_word (test.c:27) ==189553== by 0x1093F6: count_word_occurrences (test.c:49) ==189553== by 0x109523: main (test.c:75) ==189553== Address 0x4a74048 is 0 bytes after a block of size 8 alloc'd ==189553== at 0x4841888: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so) ==189553== by 0x10938B: count_word_occurrences (test.c:43) ==189553== by 0x109523: main (test.c:75) ==189553== ==189553== Invalid read of size 4 ==189553== at 0x1092DC: handle_word (test.c:29) ==189553== by 0x1093F6: count_word_occurrences (test.c:49) ==189553== by 0x109523: main (test.c:75) ==189553== Address 0x4a74094 is 0 bytes after a block of size 4 alloc'd ==189553== at 0x4841888: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so) ==189553== by 0x109399: count_word_occurrences (test.c:44) ==189553== by 0x109523: main (test.c:75) ==189553== ==189553== Invalid write of size 4 ==189553== at 0x1092E1: handle_word (test.c:29) ==189553== by 0x1093F6: count_word_occurrences (test.c:49) ==189553== by 0x109523: main (test.c:75) ==189553== Address 0x4a74094 is 0 bytes after a block of size 4 alloc'd ==189553== at 0x4841888: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so) ==189553== by 0x109399: count_word_occurrences (test.c:44) ==189553== by 0x109523: main (test.c:75) ==189553== ==189553== Invalid read of size 4 ==189553== at 0x10943E: count_word_occurrences (test.c:58) ==189553== by 0x109523: main (test.c:75) ==189553== Address 0x4a74094 is 0 bytes after a block of size 4 alloc'd ==189553== at 0x4841888: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so) ==189553== by 0x109399: count_word_occurrences (test.c:44) ==189553== by 0x109523: main (test.c:75) ==189553== ==189553== Invalid read of size 8 ==189553== at 0x109453: count_word_occurrences (test.c:58) ==189553== by 0x109523: main (test.c:75) ==189553== Address 0x4a74048 is 0 bytes after a block of size 8 alloc'd ==189553== at 0x4841888: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so) ==189553== by 0x10938B: count_word_occurrences (test.c:43) ==189553== by 0x109523: main (test.c:75) ==189553== ==189553== Invalid read of size 8 ==189553== at 0x109499: count_word_occurrences (test.c:61) ==189553== by 0x109523: main (test.c:75) ==189553== Address 0x4a74048 is 0 bytes after a block of size 8 alloc'd ==189553== at 0x4841888: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so) ==189553== by 0x10938B: count_word_occurrences (test.c:43) ==189553== by 0x109523: main (test.c:75) ==189553== ==189553== ==189553== HEAP SUMMARY: ==189553== in use at exit: 0 bytes in 0 blocks ==189553== total heap usage: 10 allocs, 10 frees, 1,070 bytes allocated ==189553== ==189553== All heap blocks were freed -- no leaks are possible ==189553== ==189553== For lists of detected and suppressed errors, rerun with: -s ==189553== ERROR SUMMARY: 25 errors from 8 contexts (suppressed: 0 from 0)
原始程序代码
#include <stdbool.h> #include <stdio.h> #include <stdlib.h> #include <string.h> bool is_separator(char c) { return c == ' ' || c == '\0' || c == '\t'; } char *extract_word(char *str, size_t start, size_t len) { char *curr_word = malloc(len + 1); strncpy(curr_word, str + start, len); curr_word[len] = '\0'; return curr_word; } int word_exists(char **words, size_t word_count, char *word) { for (size_t i = 0; i < word_count; i++) { if (strcmp(words[i], word) == 0) { return i; } } return -1; } void handle_word(char **words, int *counts, size_t *word_count, char *curr_word) { int found_index = word_exists(words, *word_count, curr_word); if (found_index != -1) { counts[found_index]++; free(curr_word); } else { words[*word_count] = curr_word; counts[*word_count] = 1; (*word_count)++; } } size_t count_word_occurrences(char *str) { size_t len = strlen(str); size_t curr_word_start = 0; size_t curr_word_len = 0; size_t word_count = 0; char **words = malloc(sizeof(char*)); int *counts = malloc(sizeof(int)); size_t i = 0; while (i <= len) { if (is_separator(str[i])) { char *curr_word = extract_word(str, curr_word_start, curr_word_len); handle_word(words, counts, &word_count, curr_word); curr_word_start = i + 1; curr_word_len = 0; } else { curr_word_len++; } i++; } for (size_t i = 0; i < word_count; i++) { printf("%s %d\n", words[i], counts[i]); } for (size_t i = 0; i < word_count; i++) { free(words[i]); } free(words); free(counts); return 0; } int main(int argc, char **argv) { if (argc != 2) { printf("\n"); return 0; } char *str = argv[1]; count_word_occurrences(str); return 0; }
问题定位
Valgrind的错误信息明确指向内存越界访问,根源是:
count_word_occurrences函数中,words和counts数组初始化时仅分配了能存储1个元素的空间(malloc(sizeof(char*))和malloc(sizeof(int)))- 当新增第2个及以上不同单词时,
handle_word函数中对words[*word_count]和counts[*word_count]的写入操作超出了已分配的内存范围,破坏了堆内存结构,最终触发double free或核心转储错误。
修复方案
需要为words和counts数组实现动态扩容逻辑:
- 新增变量记录当前数组的容量,初始值为1
- 在新增单词前,检查当前已存储的单词数是否等于数组容量,若相等则调用
realloc将容量扩容为原来的2倍 - 处理
realloc扩容失败的情况,避免程序崩溃
修复后的代码
#include <stdbool.h> #include <stdio.h> #include <stdlib.h> #include <string.h> bool is_separator(char c) { return c == ' ' || c == '\0' || c == '\t'; } char *extract_word(char *str, size_t start, size_t len) { char *curr_word = malloc(len + 1); if (!curr_word) { perror("malloc failed"); exit(EXIT_FAILURE); } strncpy(curr_word, str + start, len); curr_word[len] = '\0'; return curr_word; } int word_exists(char **words, size_t word_count, char *word) { for (size_t i = 0; i < word_count; i++) { if (strcmp(words[i], word) == 0) { return i; } } return -1; } // 新增扩容逻辑的辅助函数 bool resize_arrays(char ***words, int **counts, size_t *capacity) { size_t new_capacity = *capacity * 2; char **new_words = realloc(*words, new_capacity * sizeof(char*)); if (!new_words) { perror("realloc words failed"); return false; } int *new_counts = realloc(*counts, new_capacity * sizeof(int)); if (!new_counts) { perror("realloc counts failed"); free(new_words); return false; } *words = new_words; *counts = new_counts; *capacity = new_capacity; return true; } void handle_word(char ***words, int **counts, size_t *word_count, size_t *capacity, char *curr_word) { int found_index = word_exists(*words, *word_count, curr_word); if (found_index != -1) { (*counts)[found_index]++; free(curr_word); } else { // 检查是否需要扩容 if (*word_count >= *capacity) { if (!resize_arrays(words, counts, capacity)) { free(curr_word); exit(EXIT_FAILURE); } } (*words)[*word_count] = curr_word; (*counts)[*word_count] = 1; (*word_count)++; } } size_t count_word_occurrences(char *str) { size_t len = strlen(str); size_t curr_word_start = 0; size_t curr_word_len = 0; size_t word_count = 0; size_t capacity = 1; // 初始容量为1 char **words = malloc(capacity * sizeof(char*)); int *counts = malloc(capacity * sizeof(int)); if (!words || !counts) { perror("malloc failed"); exit(EXIT_FAILURE); } size_t i = 0; while (i <= len) { if (is_separator(str[i])) { // 跳过空单词(比如连续空格的情况) if (curr
相关产品推荐
相关产品推荐

