0

テキストファイルを圧縮するハフマン符号化プログラムを作成しようとしています。完了すると、プログラムはreturnステートメントで終了するか、読み取っていたファイルを閉じようとすると終了します。メモリリークがあると思いますが、見つかりません。それらを見つけることができたら、私に知らせてください(そしてそれらを修正する方法をいただければ幸いです!)。

(注:small1.txtは標準のテキストファイルです)

これがメインプログラムです

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#define ASCII 255

struct link {
 int freq;
 char ch[ASCII];
 struct link* right;
 struct link* left;
};

typedef struct link node;
typedef char * string;
FILE * ofp;
FILE * ifp;
int writebit(unsigned char);
void sort(node *[], int);
node* create(char[], int);
void sright(node *[], int);
void Assign_Code(node*, int[], int, string *);
void Delete_Tree(node *);

int main(int argc, char *argv[]) {
 //Hard-coded variables
 //Counters

 int a, b, c = 0;
 //Arrays
 char *key = (char*) malloc(ASCII * sizeof(char*));
 int *value = (int*) malloc(ASCII * sizeof(int*));

 //File pointers

 FILE *fp = fopen(argv[1], "r");
 if (fp == NULL) {
  fprintf(stderr, "can't open %s\n", argv[1]);
  return 0;
 }
 //Nodes
 node* ptr;//, *head;
 node* array[ASCII];

 //
 int u, carray[ASCII];
 char str[ASCII];

 //Variables
 char car = 0;
 int inList = 0;
 int placeinList = -1;
 int numofKeys;

 if (argc < 2) {
  printf("Usage: huff <.txt file> \n");
  return 0;
 }

 for (a = 0; a < ASCII; a++) {
  key[a] = -1;
  value[a] = 0;
 }

 car = fgetc(fp);
 while (!feof(fp)) {
  for (a = 0; a < ASCII; a++) {
   if (key[a] == car) {
    inList = 1;
    placeinList = a;
   }
  }
  if (inList) {
   //increment value array
   value[placeinList]++;
   inList = 0;
  } else {
   for (b = 0; b < ASCII; b++) {
    if (key[b] == -1) {
     key[b] = car;
     break;
    }
   }
  }
  car = fgetc(fp);
 }
 fclose(fp);
 c = 0;
 for (a = 0; a < ASCII; a++) {
  if (key[a] != -1) {
   array[c] = create(&key[a], value[a]);
   numofKeys = c;
   c++;
  }
 }

 string code_string[numofKeys];

 while (numofKeys > 1) {
  sort(array, numofKeys);
  u = array[0]->freq + array[1]->freq;
  strcpy(str, array[0]->ch);
  strcat(str, array[1]->ch);
  ptr = create(str, u);
  ptr->right = array[1];
  ptr->left = array[0];
  array[0] = ptr;
  sright(array, numofKeys);
  numofKeys--;
 }

 Assign_Code(array[0], carray, 0, code_string);

 ofp = fopen("small1.txt.huff", "w");

 ifp = fopen("small1.txt", "r");

 car = fgetc(ifp);
 while (!feof(ifp)) {
  for (a = 0; a < ASCII; a++) {
   if (key[a] == car) {
    for (b = 0; b < strlen(code_string[a]); b++) {
     if (code_string[a][b] == 48) {
      writebit(0);
     } else if (code_string[a][b] == 49) {
      writebit(1);
     }
    }
   }
  }
  car = fgetc(ifp);
 }
 writebit(255);
 fclose(ofp);
 ifp = fopen("small1.txt", "r");
 fclose(ifp);
 free(key);
 //free(value);
 //free(code_string);
 printf("here1\n");
 return 0;
}

int writebit(unsigned char bitval) {

 static unsigned char bitstogo = 8;
 static unsigned char x = 0;

 if ((bitval == 0) || (bitval == 1)) {
  if (bitstogo == 0) {
   fputc(x, ofp);
   x = 0;
   bitstogo = 8;
  }
  x = (x << 1) | bitval;
  bitstogo--;
 } else {
  x = (x << bitstogo);
  fputc(x, ofp);
 }

 return 0;
}
void Assign_Code(node* tree, int c[], int n, string * s) {
 int i;
 static int cnt = 0;
 string buf = malloc(ASCII);
 if ((tree->left == NULL) && (tree->right == NULL)) {
  for (i = 0; i < n; i++) {
   sprintf(buf, "%s%d", buf, c[i]);
  }
  s[cnt] = buf;
  cnt++;
 } else {
  c[n] = 1;
  n++;
  Assign_Code(tree->left, c, n, s);
  c[n - 1] = 0;
  Assign_Code(tree->right, c, n, s);
 }
}
node* create(char a[], int x) {
 node* ptr;
 ptr = (node *) malloc(sizeof(node));
 ptr->freq = x;
 strcpy(ptr->ch, a);
 ptr->right = ptr->left = NULL;
 return (ptr);
}

void sort(node* a[], int n) {
 int i, j;
 node* temp;
 for (i = 0; i < n - 1; i++)
  for (j = i; j < n; j++)
   if (a[i]->freq > a[j]->freq) {
    temp = a[i];
    a[i] = a[j];
    a[j] = temp;
   }
}

void sright(node* a[], int n) {
 int i;
 for (i = 1; i < n - 1; i++)
  a[i] = a[i + 1];
}
4

5 に答える 5

3

プログラムが他の方法で有効な操作(関数からの復帰やファイルのクローズなど)でクラッシュしている場合は、メモリリークではなく、バッファオーバーフローの問題であることをほぼ保証します。

通常、メモリリークは、mallocが最終的に失敗することを意味し、他の操作が影響を受けることを意味するものではありません。スタック上のアイテムのバッファオーバーフロー(たとえば)は、その近くのスタック上の他のアイテム(ファイルハンドル変数やからのリターンアドレスなど)を破壊する可能性がありますmain

おそらく、最初の最善の策は、ファイルハンドルへの書き込みに条件付きブレークポイントを設定することです。これは、他の場所への呼び出しで発生するはずfopenです。呼び出しの終了後に書き込みを検出した場合fopenは、問題が発生した場所であるため、スタックと実行行を調べて理由を確認してください。


あなたの最初の問題(これは必ずしも唯一のものではありません)はここにあります:

c = 0;
for (a = 0; a < ASCII; a++) {
    if (key[a] != -1) {
        array[c] = create(&key[a], value[a]);
        numofKeys = c;  // DANGER,
        c++;            //   WILL ROBINSON !!
    }
}

string code_string[numofKeys];

インクリメントするにキーの数を設定したことがわかりますc。つまり、キーの数が実際に必要な数より1つ少ないため、の最後の要素にアクセスすると、実際には他の何かcode_stringにアクセスしていることになります(これは有効なポインターではない可能性があります)。

numofKeys = c;c++;周りを交換します。それを行うと、少なくともビット印刷に到達し、here1コアダンプなしで終了します。コードの残りの部分が正しいことを保証することはできませんが、これによりセグメンテーション違反が解決されるため、次の質問に他の何かが含まれる可能性があります(必要な場合)。

于 2010-12-01T04:40:50.133 に答える
1

問題が 1 つあります。

strcpy(str, array[0]->ch);
strcat(str, array[1]->ch);

chフィールドはsizestruct linkchar配列です255。終了していませんNUL。したがって、を使用してコピーすることはできませんstrcpy

また、次のものがあります。

ofp = fopen("small1.txt.huff", "w");
ifp = fopen("small1.txt", "r");

small1.txt.huff存在しない場合は作成されます。ただし、small1.txt作成されずにfopenが返される場合は、ファイルから読み取る前にの戻り値を確認するNULL必要があります。fopen

于 2010-12-01T04:51:09.760 に答える
0

数えるだけで、4つの別々のmalloc呼び出しがありますが、1つのfree呼び出しだけです。

sprintf私はまた、あなたの電話、そしてあなたが実際にどのように歌っているのかについても警戒しますmalloc

ただし、最終的な文字列がバイトsprintf(buf, "%s%d", buf, c[i])より長い場合は、バッファオーバーフローになる可能性があります。ASCII

デバッガーを使用して、セグメンテーション違反が発生している場所を確認し、そこからデバッグすることをお勧めします。

于 2010-12-01T04:42:32.527 に答える
0

プログラムをコンパイルし、そのソースをそのsmall1.txtファイルとして実行しました。ファイルが存在しないか、ファイルが存在する場合./huf small1.txt、プログラムがクラッシュするようにコマンドで指定すると、「開くことができません(null)」になります。

Program terminated with signal 11, Segmentation fault.
#0  0x08048e47 in sort (a=0xbfd79688, n=68) at huf.c:195
195    if (a[i]->freq > a[j]->freq) {
(gdb) backtrace
#0  0x08048e47 in sort (a=0xbfd79688, n=68) at huf.c:195
#1  0x080489ba in main (argc=2, argv=0xbfd79b64) at huf.c:99

これをgdbから取得するには、実行します

ulimit -c 100000000
./huf
gdb --core=./core ./huf

バックトレースと入力します

于 2010-12-01T04:48:26.877 に答える
0

コードにさまざまな問題があります。

1.- malloc (必須):

//Arrays
char *key = (char*) malloc(ASCII * sizeof(char));
int *value = (int*) malloc(ASCII * sizeof(int));

sizeof(char) == 1, sizeof(char *) == 4 or 8 (if 64 bits compiler is used).

2.- バッファ サイズ 255 (ASCII) は短すぎて、配列 [0]->ch + 配列 [1]->ch + '\0' の内容を受信できません。

3.- strcpy の代わりに strncpy を使用し、strcat の代わりに strncat を使用します。

4.- key は個々の文字の配列であるか、それとも null で終了する文字列ですか? (コード内でこの変数を両方の方法で使用しているため)。文字カウントループでは、この変数を個々の文字の配列として使用していますが、ノードの作成では、配列のポインターを渡し、null で終了する配列としてコピーしています。

5.-最後に、使用する前に常にパラメーターを確認してください。argv [1]を開こうとした後、argc < 2かどうかを確認しています。

于 2010-12-01T05:55:58.223 に答える