第3回ライブラリの利用(4月28日)

今日のポイント

■ ヘッダファイル

ヘッダファイルとは,定数の宣言,構造体の宣言,関数の宣言などをまと めたファイルである.

次に示すヘッダファイルは, 「ネームカードの構造体」,「ネームカードを管理する構造体」,それから, 「これら構造体のメモリ確保の関数(nc_folder_create)」, 「名前を登録する関数(nc_folder_entry_name)」, 「ネームカードの管理状態を表示する関数(nc_folder_dump)」 をまとめている.
/*
 * name_card_folder.h
 */
#ifndef __NAME_CARD_FOLDER_H__
#define __NAME_CARD_FOLDER_H__

typedef struct {
  char *name;    // 名前を格納する場所
} NameCard;

typedef struct {
  NameCard *cards;
  int max_name_length;  // NameCard の name 変数で確保したサイズ
  int max_cards;        // NameCard の確保したサイズ
  int num;              // NameCard の使用可能なインデックス番号 
} NCFolder;

// ネームカードを管理するフォルダー
//   第1引数は,ネームカードの枚数の上限.
//   第2引数は,ネームカードの名前の文字列の上限.
//   返り値は,フォルダー
NCFolder *nc_folder_create( int, int );

// フォルダーのネームカードに名前を書き込みをする
//   第1引数は,ネームカードフォルダー
//   第2引数は,書き込む「名前」
//   返り値は,ない
void nc_folder_entry_name( NCFolder *, char * );

// フォルダーの中身を標準エラー出力に表示する
//   第1引数は,ネームカードフォルダー
void nc_folder_dump( NCFolder * );
#endif

■ ソースファイル

ヘッダファイルに対応したソースファイルを作る.
/*
 * name_card_folder.c
 */

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

/* ネームカードフォルダを作る */
NCFolder *nc_folder_create( int num, int len ){
  NCFolder *ans;
  int i;

  ans = ? NCFolder のメモリ確保
  ? ans が NULL ならばエラー終了.サブルーチンの中から終了するには,exit(数); を実行すればよい.
  ans->cards = ? NameCard を num の数だけ確保
  ? もし ans->cards が NULL ならばエラー終了
  ans->max_name_length = len;
  ans->max_cards = num;
  ans->num = 0;
  for( i = 0; i < ????; i += 1 ){
    ans->? cards の i 番目の name(文字列)のメモリ領域を確保.サイズは len
    ? 確保に失敗したときの処理をする(エラー終了)
  }
  ? ネームカードフォルダーのポインタを返す.
}

/* ネームカードフォルダに新しい名前 name を追加登録する */
void nc_folder_entry_name( NCFolder *ncf, char *name ){
  ? もし,フォルダの num が,max_cards 以上ならば,エラー終了
  ? もし,name が max_name_length - 1 以上ならば,エラー終了
  ? フォルダの num 番目のカードに,name を登録(文字列コピー)
  ? フォルダの num を 1 つ増やす
}

/* ネームカードフォルダーの内容を表示(ダンプ)する */
void nc_folder_dump( NCFolder *ncf ){
  int i;

  for( i = 0; i < ncf -> max_cards; i += 1 ){
    fprintf( stderr, "%d: %s\n", i, ncf->cards[i].name );
  }
}

■ メインファイル(メインのソースファイル)

上記のヘッダファイルとソースファイルから,ネームカードを管理するための ライブラリが作成できる. ライブラリだけでは,プログラムは動かない. ここでは,ライブラリを使用するメインファイルを作る.
/* list0301.c */

#include <stdio.h>
#include ?
#include "name_card_folder.h"
#define MAX_CARDS 10
#define MAX_NAME_LEN 20

int main(){
  NCFolder *ncf;
  int i;
  char buff[BUFSIZ];

  ncf = nc_folder_create( MAX_CARDS, MAX_NAME_LEN );
  for( i = 0; i < MAX_CARDS; i += 1 ){
    if( fgets( buff, BUFSIZ, stdin ) == NULL )
      break;
    ? 行末が \n ならば削除する
    ?(文字列の長さは,次の関数内で処理するのでここでは気にしなくてよい)
    nc_folder_entry_name( ncf, buff );
  }
  nc_folder_dump( ncf );

  return 0;
}

■ Makefile とコンパイル

分割コンパイル・その1(手間のかかる方法)

ライブラリのソースファイルとメインファイルは別々にコンパイルをした後で,統合する. 次の手順でコンパイルをすればよい.

$ gcc -Wall -c name_card_folder.c
$ gcc -Wall -o list0301 list0301.c name_card_folder.o

分割コンパイル・その2(Makefileを使う方法)

しかし,いつも手で上記の手順を入力することは面倒くさい. そこで,Makefile にコンパイルの手順を定義する.Makefile は次のように書く.
CC = gcc
CFLAGS = -Wall

name_card_folder.o: name_card_folder.c name_card_folder.h
	$(CC) $(CFLAGS) -c name_card_folder.c

list0301: list0301.c name_card_folder.o name_card_folder.h
	$(CC) $(CFLAGS) -o list0301 list0301.c name_card_folder.o

clean:
	$(RM) *.o core.*

Makefile を使ったコンパイルの実行は次のように,コマンドラインで操 作すればよい.

$ make list0301

ちなみに,不要なファイルを削除するには,以下を実行すればよい.

$ make clean

□ 宿題ではないが絶対にしておくこと


(c) Masato TOKUHISA (2006, April, 26.)