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

哈夫曼压缩程序中realloc异常,Valgrind报错求排查方案

哈夫曼压缩程序的realloc内存错误排查与修复

问题描述

我正在开发一款基于哈夫曼算法生成字典以压缩文本文件的C语言程序。程序可正常运行,但Valgrind检测出大量realloc相关的内存错误。尝试自行封装realloc逻辑以及直接调用库函数,均无法解决问题,恳请提供排查思路。

运行程序需使用参数格式:-o 1 -f read.txt -v,其中read.txt可为任意ASCII编码的文本文件。

完整代码如下:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <getopt.h>

const int expand_size = 4;
int length = 4;

typedef struct {
    char w[2]; //word
    int v; //value
    int d; //direction
    char b; //binary
} node_t;

int maxi(node_t * huff, int n) {
    int maxi = huff[0].v;
    for (int i = 0; i < n; i++)
        if (huff[i].v > maxi) {
            maxi = huff[i].v;
        }
    return maxi;
}

int mini(node_t * huff, int n) {
    int mini = maxi(huff, n);
    int w = 0;
    for (int i = 0; i < n; i++)
        if (huff[i].v <= mini && huff[i].v >= 0) {
            w = i;
            mini = huff[i].v;
        }
    return w;
}

node_t * realoc(node_t * huff) {
    node_t * temp = realloc(huff, (length + expand_size) * sizeof(node_t));
    return temp;
}

int huffman(node_t * huff, int n) {
    int j = 1;
    int i = 1;

    int index1;
    int index2;
    int value1;
    int value2;

    while (i != n) {
        index1 = mini(huff, n);
        value1 = huff[index1].v;
        huff[index1].v = -1;
        index2 = mini(huff, n);
        value2 = huff[index2].v;
        huff[index2].v = -1;

        huff[index1].d = n;
        huff[index2].d = n;

        huff[index1].b = '1';
        huff[index2].b = '0';

        if (n == length) {
            node_t * temp;
            temp = realoc(huff);
            huff = temp;
            for (int h = length; h < length + expand_size; h++) {
                huff[h].v = 0;
            }
            length += expand_size;
        }

        huff[n].v = value1 + value2;
        huff[n].w[0] = j + '0';
        j++;
        i = i + 2;
        n++;

    }
    return n;
}

int main(int argc, char ** argv) {
    int n;
    ///reading from a file///
    FILE * read;
    char word[2];
    char c;

    ///getopt parimeters///
    int opt;
    char * file = NULL;
    char steps = 0;
    char compression_level = 0;

    ///getopt///
    while ((opt = getopt(argc, argv, "o:f:v")) != -1) {
        switch (opt) {

        case 'f':
            file = optarg;
            break;

        case 'v':
            steps = 1;
            break;

        case 'o':
            compression_level = atof(optarg);
            break;

        case '?':
            printf("Złe parametry wywołania");
            return 1;
            break;
        }
    }

    read = fopen(file, "r");

    node_t * huff = malloc(sizeof(node_t) * (length));

    int counter = 0; ///how many nodes are in the table (nodes from the file not the ones that we create with huffman algorithm)

    for (int i = 0; i < length; i++)
        huff[i].v = 0;

    if (read == NULL) {
        printf("file can't be opened \n");
        return 1;
    }

    if (compression_level == 1) {
        while ((c = fgetc(read)) != EOF) {
            for (int i = 0; i < length; i++) {
                if (huff[i].v == 0) {
                    huff[i].w[0] = c;
                    huff[i].v++;
                    counter++;
                    break;
                }
                if (huff[i].w[0] == c) {
                    huff[i].v++;
                    break;
                }
                if (i == length - 1) {
                    node_t * temp;
                    temp = realoc(huff);
                    huff = temp;
                    for (int j = length; j < length + expand_size; j++) {
                        huff[j].v = 0;
                        //huff[j].w[0] = ' ';
                    }
                    length += expand_size;
                }
            }
        }
    }

    n = huffman(huff, counter);

    ///printing the table///

    int l;
    for (int i = 0; i < counter; i++) {
        l = i;

        if (compression_level == 1)
            printf("%c: ", huff[i].w[0]);

        if (compression_level == 2)
            printf("%c%c: ", huff[i].w[0], huff[i].w[1]);

        while (l != n - 1) {
            printf("%c", huff[l].b);
            l = huff[l].d;
        }
        printf("\n");
    }

    if (steps == 1) {
        for (int i = 0; i < counter; i++) {
            if (huff[i].w[0] == 10)
                printf("'%d' 'LINE FEED' occured %d razy\n", huff[i].w[0], huff[i].v);
            //else printf("'%d' '%s' wystapilo %d razy\n", huff[i].w, huff[i].w, huff[i].v);
        }
        printf("array length %d\n", length);
        printf("how many nodes %d\n", n);
        printf("how many primary nodes %d\n", counter);
        //printf("v = %d, o = %d\n", steps, compression_level);
    }
    free(huff);
    fclose(read);
    return 0;
}

核心问题排查

1. 指针传递失效导致野指针

huffman函数的参数是node_t * huff,属于传值传递。当你在函数内调用realloc并将返回值赋值给局部变量huff时,main函数中的原始指针并不会更新。这会导致main里的指针仍然指向已经被realloc释放的旧内存,后续访问时触发内存错误。

2. 未处理realloc失败的情况

封装的realoc函数没有检查realloc的返回值。如果内存分配失败,realloc会返回NULL,此时直接返回NULL会导致后续代码访问空指针,同时原内存块会丢失,造成内存泄漏。

3. 全局变量length引发的逻辑混乱

length是全局变量,在main和huffman函数中都被修改,这种共享状态容易导致数组长度的不同步,比如huffman扩容后修改了length,但main中的循环可能基于旧的length值访问越界。

4. 文件读取循环的逻辑漏洞

在main的字符处理循环中,当遍历到数组最后一个元素(i == length -1)才触发扩容,但扩容后当前循环直接结束,导致当前字符未被处理。另外,扩容后的新元素初始化不完整,可能引发未定义行为。

5. 哈夫曼算法的循环条件错误

原循环条件while (i != n)不符合哈夫曼算法的逻辑:哈夫曼算法需要循环counter-1次(每次合并两个节点,直到只剩根节点),原条件会导致循环次数错误,可能访问超出数组范围的元素。

具体修复步骤

1. 修复指针传递问题

将huffman函数的参数改为指针的指针,确保main中的指针能同步更新到realloc后的地址:

int huffman(node_t ** huff, int n) {
    int j = 1;
    while (n > 1) {
        int index1 = mini(*huff, n);
        int value1 = (*huff)[index1].v;
        (*huff)[index1].v = -1;

        int index2 = mini(*huff, n);
        int value2 = (*huff)[index2].v;
        (*huff)[index2].v = -1;

        (*huff)[index1].d = n;
        (*huff)[index2].d = n;

        (*huff)[index1].b = '1';
        (*huff)[index2].b = '0';

        if (n == length) {
            node_t * temp = realloc(*huff, (length + expand_size) * sizeof(node_t));
            if (!temp) {
                perror("realloc failed");
                free(*huff);
                exit(EXIT_FAILURE);
            }
            *huff = temp;
            for (int h = length; h < length + expand_size; h++) {
                (*huff)[h].v = 0;
            }
            length += expand_size;
        }

        (*huff)[n].v = value1 + value2;
        (*huff)[n].w[0] = j + '0';
        j++;
        n--;
    }
    return n;
}

同时修改main中的调用:

n = huffman(&huff, counter);

2. 添加realloc错误检查

修改realoc函数,增加错误处理:

node_t * realoc(node_t * huff) {
    node_t * temp = realloc(huff, (length + expand_size) * sizeof(node_t));
    if (!temp) {
        perror("realloc failed");
        free(huff);
        exit(EXIT_FAILURE);
    }
    return temp;
}

3. 重构全局变量length(可选但推荐)

将length改为main函数的局部变量,通过参数传递给需要的函数,避免全局变量带来的不可控性:

int main() {
    int length = 4;
    // ...
    // 调用函数时传递length的指针或值
}

4. 修复文件读取循环逻辑

调整字符处理逻辑,确保扩容后能正确处理当前字符:

if (compression_level == 1) {
    while ((c = fgetc(read)) != EOF) {
        int found = 0;
        for (int i = 0; i < length; i++) {
            if (huff[i].v == 0) {
                huff[i].w[0] = c;
                huff[i].v++;
                counter++;
                found = 1;
                break;
            }
            if (huff[i].w[0] == c) {
                huff[i].v++;
                found = 1;
                break;
            }
        }
        if (!found) {
            node_t * temp = realoc(huff);
            huff = temp;
            for (int j = length; j < length + expand_size; j++) {
                huff[j].v = 0;
            }
            huff[length].w[0] = c;
            huff[length].v++;
            counter++;
            length += expand_size;
        }
    }
}

5. 修正哈夫曼循环条件

将循环条件改为while (n > 1),符合哈夫曼算法的合并逻辑,确保循环正确终止。

内容的提问来源于stack exchange,提问作者Michał Jagodzinski

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.28 18:37:03