C言語アルゴリズム-メモリ領域確保とリスト構造
前へ 目次へ 次へ 

4 メモリ領域確保とリスト構造

4.1 メモリ領域の確保

 多くのデータを格納するためには、配列を使用するのが便利である。しかし、配列は宣言するときにその大きさが決まってしまう。例えば、テキストファイルの単語の種類(単語の大小文字は区別しない)を調べるため単語を格納する場合を考えてみる。

 次の例文の単語の種類は、6個である。

ptr[count] = (char *)malloc(strlen(token) + 1);   /* 文字数 +1バイト */

 1つの単語は、せいぜい10文字程度であり、1つのプログラムファイルに現れる単語の種類は 100〜200個程度である。最大20文字の単語を200個格納するために配列を宣言すると次のようになる。

char tango[200][20];

 これでは、20文字×200個=4000バイトのメモリが必要である。しかし、最初のptrを格納すると20文字格納できる変数であるから残り16バイト(20-3-1)は未使用になり、メモリの無駄づかいである。単語の長さに合わせて必要なときに必要なだけ領域が確保できれば、メモリを節約できる。
 この、「必要なときに必要なだけ領域(値を格納するためのメモリ)を確保する」関数がmallocである。また、関数mallocで確保したデータ領域は、関数freeで解放することもできる。

#include <stdlib.h>
void *malloc(int size);

sizeバイトの領域を確保し、その先頭アドレスを返す。

#include <stdlib.h>
void free(void *ptr);

ptrで指定された領域を開放する。
ptrはmallocで確保した領域の先頭アドレスを指定する。


指定したテキストファイルを読み込み、単語(英字で始まる語)の種類を調べる。[malloc1.c]

 単語の取り出しには、関数get_tokenを利用する。
 MS-Cでコンパイルする場合は、オプション/F 1000を付けなければ実行できないので注意すること。
    > cl /F 1000 malloc1.c sub.c

 1:
 2:
 3:
 4:
 5:
 6:
 7:
 8:
 9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include "gettoken.h"

#define WORDMAX 1000
void str_upper(char *);    /* 文字列を大文字に変換 */

void main(void)
{
    FILE    *fp;            /* 入力ファイル */
    char    fname[30];      /* ファイル名 */
    char    *ptr[WORDMAX];  /* 単語分 */
    int     count = 0;      /* 単語数 */
    int     i;              /* 作業用 */

    printf("ファイル名 : ");
    gets(fname);                /* ファイル名入力 */
    if((fp = fopen(fname, "r")) == NULL){   /* オープン */
        printf("ファイルがオープンできない\n");
        exit(1);
    }
    while((fgets(gt_line, STR_MAX, fp)) != NULL){
        while(1){
            get_token();            /* トークン取得 */
            if(*token == '\0')      /* 1行分終了 */
                break;
            str_upper(token);       /* 大文字変換 */
            if(isalpha(*token)){    /* 先頭英字(単語) */
                for(i = 0; i < count; ++i){     /* 単語検索 */
                    if(!strcmp(token, ptr[i])){     /* 登録済み */
                        *token = '\0';
                        break;
                    }
                }
                if(*token != '\0'){ /* 未登録 */     /* ↓文字数 +1バイト確保 */
                    ptr[count] = (char *)malloc(strlen(token) + 1);
                    if(ptr[count] == NULL){
                        printf("データ領域が確保できない\n");
                        exit(1);
                    }
                    strcpy(ptr[count], token);  /* 確保した領域に単語コピー */
                    ++count;                    /* 単語数カウント */
                }
            }
            if(count >= WORDMAX){
                printf("単語格納用の領域を超えた\n");
                exit(1);
            }
        }
    }
    fclose(fp);             /* クローズ */
    for(i=0;i < count; ++i)
        printf("(%s) ",ptr[i]);     /* 全単語表示 */
    printf("\n%d種類\n", count - 1);
}
void str_upper(char *pb)
{
    while(*pb != '\0'){             /* ヌル文字まで繰り返す */
        if(*pb >= 'a' && *pb <= 'z')    /* 小文字のとき */
            *pb = *pb -0x20;                /* 大文字に変換 */
        ++pb;                           /* ポインタを1つ進める */
    }
}
実行結果
ファイル名 : gettoken.c
(INCLUDE) (STDIO) (H) (STDLIB) (STRING) (CTYPE) (DEFINE) (STR) (MAX) (EUC) (SJIS
1) (SJIS2) (IFDEF) (UNIX) (KANJI1) (KANJI2) (ELSE) (ENDIF) (CHAR) (GT) (LINE) (G
ET) (TOKEN) (VOID) (UNGET) (INT) (ISKANJI) (UNSIGNED) (P) (PTK) (WHILE) (IF) (RE
TURN) (DO) (PRINTF) (EXIT) (ISALPHA) (ISALNUM) (ISDIGIT) (TOUPPER) (STRCPY) (T)
(WORK) (STRCAT) (TYPE) (CODE) (RET) (SWITCH) (CASE) (XA1) (XFE) (BREAK) (X81) (X
9F) (XE0) (XFC) (X40) (X7E) (X80) (DEFAULT) (TEST) (MAIN) (FILE) (BF) (GETS) (FO
PEN) (NULL) (FGETS) (FCLOSE)
68種類

 実行例の(XA1)や(XFE)等は、0xa1, 0xfeのような16進数データである。関数gettokenで単語を取り出すと、0xa1は0とxa1に区切られてしまうためである。

 《処理の概要》

 データ  ptr[count] = (char *)malloc(strlen(token) + 1);   /* 文字数 +1バイト */

  1.  英単語の先頭アドレスを格納する領域を宣言する。(13行目) (char *ptr[1000];)
    fig
  2. get_token()で単語(token)を取り出し(26行目)、大文字に変換する(29行目)。
  3. 英単語かどうかチェックする(30行目)。 英単語である→(4.)、英単語ではない→(7.)
  4. すでに登録されているかチェックする(32行目)。 未登録→(5.)、登録済み→(7.)
  5. 英単語を格納する領域を確保し、先頭アドレスをptrのcount番目に代入する(38行目)。
    fig
  6. 確保した領域に単語(token)をコピーし(43行目)、countを1加算する(44行目)。
  7. すべての単語が終わるまで(2.)に戻る。


前へ 目次へ 次へ 
Copyright © 2001 Hiroshi Masuda 

 

 

inserted by FC2 system