You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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. 新增变量记录当前数组的容量,初始值为1
  2. 在新增单词前,检查当前已存储的单词数是否等于数组容量,若相等则调用realloc将容量扩容为原来的2倍
  3. 处理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
相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.05 00:15:28