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

Huffman编码处理随机输入偶现段错误,文本输入正常

问题描述

我用C语言实现了Huffman编码,目前能正常读取文件、生成编码树并将压缩数据写入输出文件。但用dd if=/dev/random of=foo bs=100 count=1生成的随机输入文件时,程序会因段错误(segfault)终止,而且只有部分随机文件会触发这个问题,另一部分能正常运行。我确定空字符不是问题,因为没用到任何str*()函数(已知printTree()依赖\0但没调用它),而且包含空字节的随机文件也能正常运行。我搞不懂为什么代码不能平等处理所有输入(预期相同长度的输入要么全部失败要么全部正常)。另外,如果我提问太多请告诉我,同时希望得到自主排查这类错误的技巧,我已经花了好几个小时还是没找到问题。

huffman.c

#include "huffman.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

struct node {
    node* left;
    node* right;
    double weight;
    char* bytes;
    size_t nmemb;
};

void printTree(struct node* root, int depth)
{
    if (root == NULL) return;

    for (int i = 0; i < depth; i++) {
        printf(" │ ");
    }
    printf("(%s: %.2f)\n", root->bytes, root->weight);

    printTree(root->left, depth + 1);
    printTree(root->right, depth + 1);
}

// Order nodes in array, smallest weight last
static int compareFrequency(const void* a, const void* b)
{
    if ((*(node**)a)->weight < (*(node**)b)->weight) return 1;
    if ((*(node**)a)->weight > (*(node**)b)->weight) return -1;
    if ((*(node**)a)->nmemb < (*(node**)b)->nmemb) return 1;
    if ((*(node**)a)->nmemb > (*(node**)b)->nmemb) return -1;
    return 1;
}

// Encode input to output
void encode(FILE* input, FILE* output)
{
    unsigned int possibleBytes[256] = { 0 }, differentBytes = 0, i;
    unsigned long long totalBytes = 0;
    char currChar;

    // Count number of occurence of each individual byte
    while ((currChar = fgetc(input)) != EOF) {
        possibleBytes[(int)currChar]++;
        totalBytes++;
    }

    // Count number of unique bytes
    for (i = 0; i < 256; i++) {
        if (possibleBytes[i]) differentBytes++;
    }

    // Make array filled with nodes (at the moment only the used bytes)
    node* nodes = calloc(differentBytes * 2 - 1, sizeof(node));
    node* nodesP = nodes;

    for (i = 0; i < 256; i++) {
        if (possibleBytes[i]) {
            nodesP->bytes = calloc(1, sizeof(char));
            nodesP->bytes[0] = i;
            nodesP->weight = (double)possibleBytes[i] / totalBytes;
            nodesP->nmemb = 1;
            nodesP++;
        }
    }

    // Fill trees array with nodes
    node** trees = calloc(differentBytes, sizeof(node*));
    node** treesP = trees;
    nodesP = nodes;
    unsigned int treesLeft = differentBytes;

    for (i = 0; i < treesLeft; i++) {
        *treesP = nodesP;
        treesP++;
        nodesP++;
    }

    // Build tree
    node* lastTree;
    node* secondLastTree;

    while (treesLeft > 1) {
        qsort(trees, treesLeft, sizeof(node*), &compareFrequency);

        lastTree = *(trees + treesLeft - 1);
        secondLastTree = *(trees + treesLeft - 2);

        nodesP->left = lastTree;
        nodesP->right = secondLastTree;
        nodesP->weight = lastTree->weight + secondLastTree->weight;
        nodesP->nmemb = lastTree->nmemb + secondLastTree->nmemb;
        nodesP->bytes = calloc(lastTree->nmemb + secondLastTree->nmemb, sizeof(char));
        // str* functions use \0 terminated string -> Might not work if array contains \0 (not at
        // the end)
        // strcat(nodesP->bytes, lastTree->bytes);
        // strcat(nodesP->bytes, secondLastTree->bytes);
        memcpy(nodesP->bytes, lastTree->bytes, lastTree->nmemb);
        memcpy(nodesP->bytes + lastTree->nmemb, secondLastTree->bytes, secondLastTree->nmemb);
        trees[treesLeft - 2] = nodesP;

        treesLeft--;
        nodesP++;
    }

    // write tree to file
    // https://stackoverflow.com/a/759766/15833045
    //...

    // write data to file
    // left is 0, right is 1
    rewind(input);
    unsigned char bitPosition = 1;
    unsigned char buffer[BUFSIZ];
    node* currNode = *trees;
    currChar = fgetc(input);

    for (i = 0; currChar != EOF; i++) {
        // Fill byte of buffer
        while (bitPosition <= 8) {
            // Set 0 if going left, set 1 if going right
            if (memchr(currNode->left->bytes, currChar, currNode->nmemb)) {
                buffer[i] &= ~(1 << (8 - bitPosition));
                currNode = currNode->left;
            } else {
                buffer[i] |= (1 << (8 - bitPosition));
                currNode = currNode->right;
            }

            // If leaf, reset currNode and get next byte
            if (!currNode->left) {
                currNode = *trees;
                currChar = fgetc(input);
                if (currChar == EOF) {
                    break;
                }
            }
            bitPosition++;
        }
        bitPosition = 1;

        if (i == BUFSIZ - 1) {
            i = 0;
            fwrite(buffer, sizeof(char), BUFSIZ, output);
        }
    }

    fwrite(buffer, sizeof(char), i, output);
}

main.c

#include "huffman.h"
#include <stdio.h>

int main(int argc, char* argv[])
{
    FILE* inputP = fopen(argv[1], "rb");
    FILE* outputP = fopen(argv[2], "w");

    if (!(inputP && outputP)) return 1;

    encode(inputP, outputP);

    fclose(inputP);
    fclose(outputP);
    return 0;
}

Makefile

CC = gcc
CFLAGS = -g
OBJ = main.o huffman.o

huffman: $(OBJ)
    $(CC) $(OBJ) -o $@

%.o: %.c
    $(CC) $(CFLAGS) -c $<


clean:
    rm *.o

GDB调试输出(不同随机文件生成的core dump略有差异)

Reading symbols from huffman...
[New LWP 9843]
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/usr/lib/libthread_db.so.1".
Core was generated by `huffman a b'.
Program terminated with signal SIGSEGV, Segmentation fault.
#0  __memchr_avx2 () at ../sysdeps/x86_64/multiarch/memchr-avx2.S:82
82              VPCMPEQ (%rdi), %ymm0, %ymm1

huffman.h

#ifndef HUFFMAN_H
#define HUFFMAN_H

#include <stdio.h>

typedef struct node node;

// static int compareFrequency(const void* a, const void* b);
void encode(FILE* input, FILE* output);

#endif

内容的提问来源于stack exchange,提问作者TheGlibber

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.23 10:56:00