Download : sample-001.c
/* * 2020/12/18 sample-001.c * * 局所変数の領域 */ #include <stdio.h> /* * sub1 */ void sub1(void) { int i; /* 関数 sub1 の中だけで有効 */ i = 123456; printf ( "sub1 : &i = %p\n", &i ); printf ( "sub1 : i = %d\n", i ); } /* * sub2 */ void sub2(void) { int j; /* 関数 sub2 の中だけで有効 */ /* 「当然(?)」 sub1 の i とは無関係 */ printf ( "sub2 : &j = %p\n", &j ); printf ( "j = %d\n", j ); } /* * main 関数 */ int main ( void ) { sub1(); sub2(); return 0; }
$ ./sample-001.exe sub1 : &i = 0x7ffc294baf1c sub1 : i = 123456 sub2 : &j = 0x7ffc294baf1c j = 123456 $
Download : sample-002.c
/* * 2020/12/18 sample-002.c * * ブロック内の局所変数 * 関数の本体もブロック * 関数の引数は、(例外的に..) 関数の本体と同じ扱い */ #include <stdio.h> /* * sub */ void sub(int arg) { int var = arg; printf ( "Level 1-1: arg = %d, var = %d\n", arg, var ); { /* 新しいブロック */ int var = 1; printf ( "Level 2-1: arg = %d, var = %d\n", arg, var ); arg = arg + 100; var = var + 100; printf ( "Level 2-2: arg = %d, var = %d\n", arg, var ); } printf ( "Level 1-2: arg = %d, var = %d\n", arg, var ); { int var = 2; printf ( "Level 2-3: arg = %d, var = %d\n", arg, var ); { int var = 10000; int arg = 20000; printf ( "Level 3-1: arg = %d, var = %d\n", arg, var ); var = var + 100000; arg = arg + 100000; printf ( "Level 3-2: arg = %d, var = %d\n", arg, var ); } printf ( "Level 2-4: arg = %d, var = %d\n", arg, var ); } printf ( "Level 1-3: arg = %d, var = %d\n", arg, var ); } /* * main 関数 */ int main ( void ) { sub(1234); return 0; }
$ ./sample-002.exe Level 1-1: arg = 1234, var = 1234 Level 2-1: arg = 1234, var = 1 Level 2-2: arg = 1334, var = 101 Level 1-2: arg = 1334, var = 1234 Level 2-3: arg = 1334, var = 2 Level 3-1: arg = 20000, var = 10000 Level 3-2: arg = 120000, var = 110000 Level 2-4: arg = 1334, var = 2 Level 1-3: arg = 1334, var = 1234 $
Download : sample-003.c
/* * 2020/12/18 sample-003.c * * 大域変数 */ #include <stdio.h> /* * 大域変数の宣言(定義) */ int gvar = 1234; /* 大域変数の宣言(定義) */ /* * sub1 */ void sub1(void) { printf ( "Sub1 1 : gvar = %d\n", gvar ); gvar = gvar + 10; printf ( "Sub1 1 : gvar = %d\n", gvar ); } /* * sub2 */ void sub2(void) { printf ( "Sub1 2 : gvar = %d\n", gvar ); gvar = gvar + 100; printf ( "Sub1 2 : gvar = %d\n", gvar ); } /* * main 関数 */ int main ( void ) { printf ( "Main 1 : gvar = %d\n", gvar ); gvar = gvar + 1; printf ( "Main 2 : gvar = %d\n", gvar ); sub1(); printf ( "Main 3 : gvar = %d\n", gvar ); sub2(); printf ( "Main 4 : gvar = %d\n", gvar ); return 0; }
$ ./sample-003.exe Main 1 : gvar = 1234 Main 2 : gvar = 1235 Sub1 1 : gvar = 1235 Sub1 1 : gvar = 1245 Main 3 : gvar = 1245 Sub1 2 : gvar = 1245 Sub1 2 : gvar = 1345 Main 4 : gvar = 1345 $
Download : sample-004.c
/* * 2020/12/18 sample-004.c * * ファイルにデータを書き出すプログラム */ /* * ファイルの内容の読み書きは * いったん、ファイルポインターを入手し、 * ファイルポインター経由で、操作を行う必要がある。 * 手順 * ファイルポインタを入手 * ファイルを開く(open) * ファイル操作(情報の R/W) * ファイルポインターを指定して、r/w の関数を呼ぶ * ファイルの利用が終わったら、ファイルポインターを開放 * ファイルの閉じる(close) */ /* ファイル abc.dat に、"abc" + 改行の 4 文字(4 byte) の 情報を書き込む */ #include <stdio.h> int main(int argc, char *argv[]) { FILE *fp; /* これから開く、ファイルへのファイルポインターを保存する */ /* 「*」がついているので、「ポインター型」である事が解る */ /* 幾つのファイルを開くは、予め解からない */ /* 動的なデータ型 -> 裏で alloc がされている */ fp = fopen( "abc.dat", "w" ); /* ライブラリ関数 fopen を利用して、 ファイル "abc.dat" に対するファイルポインターを入手 */ /* ファイルがない場合は、自動的に作られる */ /* すでにファイルがある場合は、以前のファイルの内容は消される */ /* 何等かの理由で、ファイルのオープンに失敗した場合は、 NULL が値として帰る */ if ( fp != NULL ) { /* 無事オープンできたらデータを書き込む */ /* データを書き込む */ fprintf ( fp, "abc\n" ); /* ライブラリ関数 fprintf を利用して、 最初の引数に指定したファイルポインターに 対応するファイルに、print する */ fclose ( fp ); /* ライブラリィ関数 fclose を用いて、ファイルをクローズ */ /* 後処理を行い、(C 言語のレベルでは..) 記録を完了させる */ /* (free の時と同じように..) いったん close した、 ファイルポインタは使えなくなる もし、同じファイルを操作したければ、 もういちど、ファイルをオープンして、 別のファイルポインタを入手する必要がある */ } else { printf ( "ファイル(abc.dat)のオープンに失敗しました\n" ); } return 0; }
$ ./sample-004.exe $
/* * 20201211-01-QQQQ.c * 同じ数値(自然対数の底 : e ネピアの数 ) の値を求める * e = \sum_{i=0}^{\infty} \frac{1}{i!} * 1 + 1/1! + 1/2! + .. */ #include <stdio.h> #include <math.h> /* * */ #define EPS (1e-300) /* 1/i! の下限 */ /* どこまで計算するか : ε>0 */ /* ポイント : double では表現できるが float では 表現できない(くらい、細かい../正確な..)精度 */ /* * float 型での計算 */ float fexp(void) { int i; float fe = 1.0; /* fe = 1 + \sum_{i=1}^n 1/(i!) */ float fs = 1.0; /* fs = 1/(i!), 1/(0!) = 1/1 = 1 */ for ( i = 1; ; i++ ) { /* 1/(i!) = (1/((i-1)!))/i */ /* fs には 1/(i-1)! が入っているので、 1/i! を計算するには、fs を i で割ればよい */ fs = fs / (float)i; /* (float) でキャストしているが、昇格するので不要 */ fe = fe + fs; if ( (double)fs < EPS ) { return fe; } } } /* * double 型での計算 */ double dexp() { int i; double ds = 1.0; double de = 1.0; for ( i = 1; ; i++ ) { ds = ds / (double)i; de = de + ds; if ( (double)ds < EPS ) { return de; } } } /* * main */ int main ( void ) { /* fexp の結果 */ printf ( "fexp = %40.35f\n", fexp() ); /* dexp の結果 */ printf ( "dexp = %40.35f\n", dexp() ); /* math.h の中の定数 */ printf ( "M_E = %40.35f\n", M_E ); return 0; } /* double と float では、計算結果の精度が異なる double の方が精度が良い */
/* * DATE-02-QQQQ.c * * 動的なメモリの確保 * キーボードより正の整数を幾つか入力して、その要素が入った配列を返す * 0 以下の整数が入力されたら、終了とする * 配列のサイズは、正の整数の個数 + 1 とし、最後の要素には 0 を入れる */ /* * 利用方法 * コンパイル * cc -o BASENAME.exe FILENAME * 実行 * ./BASENAME.exe */ #include <stdio.h> #include <malloc.h> /* calloc/free を利用するので必要 */ /* * read_n_integers * 入力された整数値が入った配列の先頭の要素へポインタ値 * # 番地(アドレス値)が返ってくる * [ポイント] * 1. 配列のサイズがわからないので、最後 0 が入っている * 2. 再帰を使って、入力が終わるまでメモリ確保しない */ int *read_n_integers( int size ) { int num; /* キーボードから入力された数値を保存する */ int *value; /* 確保された配列の先頭要素へのポインタ変数 */ printf ( "正の整数値を入力してください(0 以下だと入力を終了します):" ); scanf ( "%d", &num ); if ( num <= 0 ) { /* 入力が全部終ったので、配列を作成する */ /* 配列のサイズは、引数で指定された個数 + 1 となる */ if ( ( value = (int *)calloc ( size + 1, sizeof ( int ) ) ) != NULL ) { /* 1. calloc ( m, n ) '=, malloc ( m * n ) calloc 確保したメモリを 0 クリアする 2. calloc/malloc が返す値の型は (void *) : メモリだが何の型かわからない => キャストを利用して型変換をしている 3. 代入文 ( v = e; ) は、実は、「代入式」だった 「代入式」の「値」は、「代入の右辺」になる # a = b = 1; # a = (b=1); # b=1; a=1; 4. NULL ( ヌルポインタ ) # 実態 : ((void *)0) : どの(有効な)メモリも指さないポインタ値である事が保証されている値 # => NULL と比較して一致したら、それは無効な値 # calloc ( 動的なメモリ確保 ) # 確保できない可能性(の確率)が高い # => alloc (malloc/calloc/..) の時には、 # 必ず、確保できているかを確認する # !! alloc は確保に失敗すると NULL を返す規則 # !! 関数返り値を NULL と比較する事により、 # !! 確保できたかどうかがわかる */ /* 動的メモリは取り出せるとは限らないので、結果をチェック */ value[ size ] = 0; /* 最後の要素として 0 を代入 */ } /* else {} */ /* NULL が帰った場合は、そのまま、値として返す */ } else { /* 入力が終っていないので、更に、値を読むために再帰呼び出し */ if ( ( value = read_n_integers( size + 1 ) ) != NULL ) { /* 結果が NULL でなければ、配列が作られている */ /* size 番目の要素を配列に記録 */ value[size] = num; } /* else {} */ /* NULL が帰った場合は、そのまま、値として返す */ } /* いずれの場合でも value を返す */ return value; } /* 3, 1, -1 read_n_integers ( 0 ) size <- 0; num <- 3 read_n_integers ( 0 + 1 ) size <- 1; num <- 1; read_n_integers ( 1 + 1 ) size <- 2; num <- -1 value = { ?, ?, ? } : calloc value[size] = 0 value[2] = 0 => value = { ?, ?, 0 } value = { ?, ?, 0 } value[size] = num value[1] = 1; => value = { ?, 1, 0 } value = { ?, 1, 0 } value[size] = num value[0] = 3; => value = { 3, 1, 0 } value = { 3, 1, 0 } return { 3, 1, 0 } * main */ int main ( int argc, char *argv[] ) { int *array; /* n 個数の要素をもつ配列の先頭をもつ */ int i; /* read_n_integers を呼び出して、n 個の整数値を入力する */ /* 引数には、入力済のデータ数を指定するので、最初は 0 を指定する */ if ( ( array = read_n_integers( 0 ) ) != NULL ) { /* read_n_integers は、NULL を返す可能性がある */ /* 入力された要素を画面に出力 */ for ( i = 0; array[i] > 0; i++ ) { printf ( "%d th data = %d\n", i, array[i] ); } /* 1. alloc する領域は alloc してからでないと使えない 2. alloc に失敗する場合があるので注意 (NULLと比較) 3. alloc の結果(ポインタ値)は保存しておく */ /* malloc/calloc で確保したメモリは、必ず free で解放する */ /* 4. alloc した領域は、利用終了時に free する必要がある この時 alloc の結果(ポインタ値)が必要 */ free ( array ); /* メモリを解放する */ /* 5. alloc した領域は、解放した後は使えない !! */ } /* else {} */ /* NULL の場合はエラーなので、何もしない */ return 0; }
/* * 20201218-01-QQQQ.c * 整数のキュー(queue) * * queue : キュー (待ち行列) * => データの並び * 配列 * リスト * 先頭の要素の追加と削除 * <=> キュー * 先頭の要素の削除/最後の要素の追加 * 実装の方針: * LinkedList を転用して、 * ただし、最後の要素の追加ができるようにする * 最後の要素の追加を効率よくするために * 最後の要素へのポインタ値も保存する * */ #include <stdio.h> #include <malloc.h> /* malloc を利用する場合 */ /* * 整数のキュー(queue) * * 利用方法 * コンパイル * cc -o BASENAME.exe FILENAME * 実行 * ./BASENAME.exe */ /* * Queue は、List の最後に追加し、先頭の要素を削除する事で実現 * * 整数 queue : [ 1, 2, .., 10 ] の表現 * * ICell * +-------+ +-------+ +-------+ * next +-> | *-----> | *-----> ... --> | NULL | * value | | | | | +-> | | * | +-------+ +-------+ ... | +-------+ * | | 1 | | 2 | | | 10 | * | +-------+ +-------+ | +-------+ * IQueue | | * +-----------+ | | * head | *-------+ | * +-----------+ | * tail | *---------------------------------------+ * +-----------+ * * 空な整数 queue : [] の表現 * 空な整数 queue は、head/tail 共に NULL とする * * IQueue * +-----------+ * head | NULL | * +-----------+ * tail | NULL | * +-----------+ * */ typedef struct icell { struct icell *next; /* 次のセルへのポインター */ int value; /* このセルが持つ値(整数値) */ } ICell; typedef struct { ICell *head; /* リストの先頭の要素へのポインター */ ICell *tail; /* リストの最後の要素へのポインター */ } IQueue; /* */ #define NORMAL (0) /* 処理に成功した場合の値 */ #define ERROR (-1) /* 処理に失敗した場合の値 */ /* */ #define FALSE (0) /* 論理値 (偽値) */ #define TRUE (!FALSE) /* 論理値 (真値) */ /* * alloc_icell * 指定した整数値(data) を保持する icell を作成する * return 値 * 作成された icell へのポインター値 * alloc に失敗した場合は NULL 値が返る */ ICell *alloc_icell ( int data ) { ICell *result = (ICell *)malloc(sizeof(ICell)); if ( result != NULL ) { /* 次の要素 (next) は NULL にする */ result -> next = NULL; /* 保持する値 (value) は 指定された値 (data) にする */ result -> value = data; } return result; } /* * free_icell * 指定した整数値を保持する icell (icp) を開放する * return 値 * 開放に成功したかどうか ( 成功 -> NORMAL / 失敗 -> ERROR ) */ int free_icell ( ICell *icp ) { int result = ERROR; if ( icp != NULL ) { free ( icp ); result = NORMAL; } return result; } /* * alloc_iqueue * 空っぽな整数 queeue (iqueue) を作成する * return 値 * 作成された iqueue へのポインター値 * alloc に失敗した場合は NULL 値が返る */ IQueue *alloc_iqueue ( void ) { IQueue *result = (IQueue *)malloc(sizeof(IQueue)); if ( result != NULL ) { /* 空な整数 queue は、先頭も最後も NULL にする */ result -> head = NULL; result -> tail = NULL; } return result; } /* * is_empty * 指定した整数 queue が空かどうかを判定する * return 値 * 空の場合 TRUE * 空でない場合 TRUE 以外 */ int is_empty ( IQueue *iqp ) { if ( iqp != NULL ) { if ( iqp -> head == NULL ) { return TRUE; } } return FALSE; } /* * enque * 指定された 整数 queue (iqp) に指定された整数値 (data) を追加する * return 値 * 追加できた場合 NORMAL * それ以外 ERROR */ int enque ( IQueue *iqp, int data ) { int result = ERROR; /* 途中で、何か上手く行かなければ ERROR */ if ( iqp != NULL ) { ICell *icp = alloc_icell ( data ); /* 新しい ICell を作成 */ if ( icp != NULL ) { /* 作成できたら */ if ( is_empty ( iqp ) ) { /* 空っぽの場合 */ iqp -> head = icp; /* 先頭も新しい要素 */ /* ipq -> head, ipq -> tail はともに icp */ } else { iqp -> tail -> next = icp; /* 最後の要素の次に新しい要素を追加 */ /* 最後 ICell の次に、icp の指すセルをいれる */ } /* 最後の要素は、追加した要素 */ iqp -> tail = icp; result = NORMAL; /* 追加に成功 (全て OK) */ } } /* else {} */ /* iqp が NULL だと ERROR になる */ return result; } /* * dequeue * 指定された 整数 queue (iqp) から、要素を取り出す * return 値 * 取り出せた場合 取り出した値 * 取り出せない場合 ERROR * <<注意>> * 整数 queue の中に ERROR があると、 * 取り出しに成功したか失敗したか区別できない (不適切な設計 !!) */ int deque ( IQueue *iqp ) { int result = ERROR; if ( iqp != NULL ) { if ( !is_empty ( iqp ) ) { /* queue が空でない */ ICell *top = iqp -> head; /* 先頭の要素(ICell)を取り出す */ result = top -> value; /* 返す値は、先頭にある */ /* top -> value が運悪く ERROR と同じ場合 */ /* 取り出しに失敗しか、成功したか区別できない */ /* なので ERROR には、top -> value に入らない 値を設定する必要がある */ /* 今回の場合は、気にしない */ if ( ( iqp -> head = top -> next) == NULL ) { /* 新しい先頭は、先頭の次の要素 */ /* queue が空になった場合は tail も NULL にする */ iqp -> tail = NULL; } free ( top ); /* 先頭の要素は開放 */ } } return result; } /* * free_que * 指定された 整数 queue (iqp) を全て開放する */ void free_que ( IQueue *iqp ) { if ( iqp != NULL ) { ICell *icp = iqp -> head; /* 先頭の要素を、次の処理対象に */ ICell *next; /* 次の要素へのポインタ値を保持 */ while ( icp != NULL ) { /* 未だ、要素があれば.. */ next = icp -> next; /* 次のために、次の要素へのポインタ値をバックアップ */ free_icell ( icp ); /* 注目している要素を開放 */ /* 次の要素を、次の処理対象にする */ icp = next; } free ( iqp ); /* queue そのものを開放 */ } } /* * print_que * 指定された整数 que の内容を表示する */ void print_que ( IQueue *iqp ) { ICell *icp = iqp -> head; printf ( "[" ); for ( icp = iqp -> head; icp != NULL; icp = icp -> next ) { /* 現在着目している要素 (icp) が保持する値を表示 */ printf ( "%d", icp -> value ); if ( icp -> next != NULL ) { /* 次の要素があれば .. */ printf ( "," ); /* 「,」を出力 */ } else { printf ( " " ); /* 「 」を出力 */ } } printf ( "]" ); } /* * main */ int main ( int argc, char *argv[] ) { IQueue *iqp = alloc_iqueue(); /* 空の queue の作成 */ print_que ( iqp ); putchar ( '\n' ); /* 空の queue [] */ enque ( iqp, 1 ); /* que に 1 を追加 */ print_que ( iqp ); putchar ( '\n' ); /* 要素が 1 つの queue [ 1 ] */ enque ( iqp, 2 ); enque ( iqp, 3 ); /* que に 2, 3 を追加 */ print_que ( iqp ); putchar ( '\n' ); /* 要素が 3 つの queue [ 1, 2, 3 ] */ printf ( "%d\n", deque ( iqp ) ); /* 先頭の要素を削除 */ print_que ( iqp ); putchar ( '\n' ); /* 要素が 2 つの queue [ 2, 3 ] */ free_que ( iqp ); /* que の開放 */ return 0; }
#include <stdio.h> int main(void) { /* 離散型 : 演算が異なる / サイズの違いは表現する値の範囲の違い */ printf ( "sizeof(char) = %lu\n", sizeof(char) ); /* %lu : unsigned log */ printf ( "sizeof(int) = %lu\n", sizeof(int) ); printf ( "sizeof(short) = %lu\n", sizeof(short) ); printf ( "sizeof(long) = %lu\n", sizeof(long) ); /* 離散型に関しては、unsigned もあるが、 サイズが変わらず、表す範囲が異なる signed ( n byte ) : -2^(n-1) ? 2^(n-1) - 1 unsigned ( n byte ) : 0 ? 2^n - 1 */ /* 連続型 : サイズの違いは表現する値の範囲と精度(どれだけ細かい数値が表現できるか ) の違い */ printf ( "sizeof(double) = %lu\n", sizeof(double) ); printf ( "sizeof(float) = %lu\n", sizeof(float) ); return 0; } /* 基本は int double で考えればよい ( 型の昇格があるので、混在してもきにしない ) # char 型は、文字を扱う場合に必要なので、 # int とは別に扱う その型のデータがメモリ内に占めるサイズは、 メモリ効率(小さいほど良い)と、その適用範囲(大きい良い) トレードオフ => 配列や(今日予定の)ファイルに保存する時など、 大量のデータを保存する時に配慮する必要がある */
#include <stdio.h> int main(void) { int i; char nc = 1; unsigned char nuc = 1; int ni = 1; long nl = 1L; /* 1L は long 型 1 */ unsigned int nui = 1; unsigned long nul = 1L; /* 1L は long 型 1 */ for ( i = 0; i < 40; i++ ) { printf ( "i\t=\t%d\n", i ); printf ( "\tnc\t=\t%d\n", nc ); printf ( "\tnuc\t=\t%u\n", nuc ); printf ( "\tni\t=\t%d\n", ni ); printf ( "\tnl\t=\t%ld\n", nl ); printf ( "\tnui\t=\t%u\n", nui ); printf ( "\tnul\t=\t%lu\n", nul ); nc = nc * 2; nuc = nuc * 2; ni = ni * 2; nl = nl * 2; nui = nui * 2; nul = nul * 2; } return 0; }
#include <stdio.h> int main(void) { printf ( "%f\n", 123 ); /* 123 という整数値 ( 値:123, 型:int ) を %f : double 型して出力する -> 不適切な型 コンパイル時 : コンパイラが型の情報から ワーニングを出している 実行時: 特にエラーとは扱わない 整数型の 123 を表すビットパターンと 浮動小数点数型の何かを表すビットパターン は、区別できない => エラーにもしない、それなりに動く(通常は期待した値にならない) */ return 0; }
#include <stdio.h> #include <malloc.h> /* 整数のリスト 数が、順にならんだもの 例: 4, 1, 8 => [ 4, 1, 8 ] リストの実現方法 Linked List list +-----+ +-------+ +-------+ +-------+ | *------> | *------>| *------>| *------> NULL +-----+ +-------+ +-------+ +-------+ | 4 | | 1 | | 8 | +-------+ +-------+ +-------+ 複数の (目的とする要素の値と、「次」を示すポインタ値の対を 保持する..) Cell の並びで List を表現する表現方法 */ typedef struct icell { struct icell *next; /* (ICell *) 型: 次のセルを指す */ int value; /* リスト要素となる値を保持 */ } ICell; /* 整数値を保持するリストのセル */ /* * */ ICell *makeICell ( int v ) { ICell *newICell; if ( ( newICell = (ICell *)malloc( sizeof( ICell ) ) ) != NULL ) { newICell -> next = NULL; /* (*newICell).next = NULL; */ newICell -> value = v; /* (*newICell).value = v; */ } /* else {} */ /* もし、malloc が NULL を返したら、そのまま NULL 返す */ return newICell; } /* リストの要素の先頭に、新しい要素を追加する */ ICell *insertIList ( ICell *list, int v ) { ICell *newICell; if ( ( newICell = makeICell ( v ) ) != NULL ) { if ( list != NULL ) { newICell -> next = list; } } return newICell; } /* リストの要素の先頭に、新しい要素を追加する list:[4 1 8] +-----+ +-------+ +-------+ +-------+ | *------> | *------>| *------>| *------> NULL +-----+ +-------+ +-------+ +-------+ | 4 | | 1 | | 8 | +-------+ +-------+ +-------+ +-------------------+ | | list:[1 8] | v +-----+ | +-------+ +-------+ +-------+ | *----+ | *------>| *------>| *------> NULL +-----+ +-------+ +-------+ +-------+ | 4 | | 1 | | 8 | +-------+ +-------+ +-------+ topICell ^ +-----+ | | *------------+ +-----+ !! list = list -> next; とすると、 !! 先頭のセルが解放できなくなる */ ICell *removeIList ( ICell *list ) { ICell *topICell; if ( list == NULL ) { /* 空っぽなので、削除できない */ /* 本来は ERROR なのだが、今回は、 NULL をそのまま返す (手抜き実装) */ } else { /* NULL でない場合は、先頭の要素を削除 */ topICell = list; /* 先頭のセルを取り出す */ list = topICell -> next; /* 削除した後のリスト */ /* topICell がさす領域は解放すべきなの.. */ free ( topICell ); } return list; /* 削除後のポインタ値 */ } /* 使い終わった 整数リストを、すべて解放すr */ void freeIList ( ICell *list ) { while ( list != NULL ) { list = removeIList ( list ); } } /* リストの表示 */ void printIList ( ICell *list ) { printf ( "[" ); while ( list != NULL ) { printf ( " %d", list -> value ); list = list -> next; } printf ( " ]\n" ); } int main(void) { ICell *list = NULL; /* list = [ ] */ int i; printIList ( list ); list = insertIList ( list, 8 ); /* 本当は NULL かどうかチェックが必要 */ list = insertIList ( list, 1 ); list = insertIList ( list, 4 ); printIList ( list ); /* [ 8 1 4 ] */ list = removeIList ( list ); /* [ 1 4 ] */ printIList ( list ); /* [ 1 4 ] */ list = insertIList ( list, 10 ); /* [ 10 1 4 ] */ printIList ( list ); /* [ 10 1 4 ] */ for ( i = 0; i < 10; i++ ) { list = insertIList ( list, i ); } printIList ( list ); /* alloc しているので、本当は free する必要があるが、 それは後回し */ /* プログラムが終了すると、すべての領域が自動的に 解放(free)されるので、実質、悪影響はないが、 「alloc したら free する」という習慣は必要 */ /* 後回しした、解放のコード */ freeIList ( list ); return 0; }
前回の内容 : データ構造 (7) 講義 色々な型と型変換 : 型のサイズ/型の昇格/キャスト char, int, double long int, short int, float, unsigned ... メモリを示すサイズが異なる => sizeof 動的メモリの確保 : malloc/free メモリ:連続して並んでいるセルの集まり C 言語の「変数」に対応している その変数がもっている値(データ)を保存する役割 # データは、値と型 # メモリそのものがもっているは「値」(に対応するビットパターン) # 型 : そのメモリに保存されている値を # どのように扱うか ? # # 型の情報は、データではなくコードにある # # コンパイル時は利用可能/実行時は型の情報はない # # 同じデータを不適切な型として利用してまう危険性がある メモリの確保しかた 変数宣言 ( をすると、メモリの一部が、確保される ) => コンパイル時に決める(柔軟性がない) 動的なメモリ確保 => 実行時 変数宣言 利点: 変数名で、メモリを操作(値の取り出しと代入) 後始末が自動的に行われる 欠点: コンパイル時に決める必要があるので、 サイズや個数が固定(柔軟性に欠ける) 動的なメモリ確保 利点: サイズや個数を、実行時に自由に決められる(柔軟性に富む) # malloc を使って確保 欠点: メモリに名前(変数名)がないので、 ポインタ値を保持 ポインタ値を保存する変数[ポインタ変数]に記憶させて、 それを介して、利用する必要がある 後始末を、自分でやる必要がある # free を使って解放する必要がある [今回の内容] 動的なデータ構造 実行時にサイズや構造が決まるようなデータを扱いたい => alloc を利用する事になる ! 前回は、配列サイズを動的.. NULL 型: (void *) : メモリなんだけど、何が入るかわからない型 => 必ず、「(void *) 以外の」ポインタ型にキャストして使う 値: (void *)0 : この値は、「有効でない事が保証されている」ポインタ値 => 「有効でない」事を表すための値 => これと比較して一致したら、「無効」と考える # NUL 文字 : '\0' => ちゃんとした値 ( 文字の値として有効 ) # 型 : char (int) # 値 : 0 ( '\0' ) # ASCII Code 表の 0 に対応する文字コード # => NULL と同様、「普通でない『文字』」として、 # 利用される ( 例: End Of String ) # # NULL ポインタ : ポインタ値だが、「『無効』なポインタ値」という意味 # => alloc が返す値をと比較する場合のみ利用する # # ポインタ値を返す関数で、「無効」を示す場合に使う # !! stdio.h の中で(#define で)定義されている # !! => いつでも(#include <stdio.h>があれば)、 # !! 「NULL」で参照可能 動的なデータ型の例 List データが並んでいるもの: cf. 配列との違い 含まれるデータの個数 要素への参照(一定時間内) List 動的的に変更可能(追加/削除) 先頭から順に(シーケンシャル参照) 配列 宣言時に決まる(固定) どこからでも(ランダム参照) LinkedList Cell をつないで、リストを作る 先頭への挿入と先頭の削除ができる 要素を参照する場合は、順番にポインタを手繰る必要がある !! Cell そのものは、単なる構造体 !! 複数の Cell の間に ( next の値を常に次要素の Cellにする事により..) !! List に相当するデータ構造がつくられた !! その 「List への相当」を実現するために、 !! insetICell/removeICell !! という関数で、実現をしている !! !! !! 基本型(int,double 等)は !! !! 型宣言するだけで、その型を実現するコードは、組み込み(自分で作る必要はない) !! !! ユーザ型(自分で構造体やポインタ型)は、 !! !! 型宣言して(値の集合を決める)だけではだめで、 !! !! その値を操作する関数を対で実装する事で、 !! !! 「型」としての役割を果たす
課題プログラム内の「/*名前:ここ*/」の部分を書き換え「/*この部分を完成させなさい*/」の部分にプログラムを追加して、プログラムを完成させます。
Download : 20201218-01.c
/* * 20201218-01-QQQQ.c * 整数のキュー(queue) */ #include <stdio.h> #include <malloc.h> /* malloc を利用する場合 */ /* * 整数のキュー(queue) * * 利用方法 * コンパイル * cc -o BASENAME.exe FILENAME * 実行 * ./BASENAME.exe */ /* * Queue は、List の最後に追加し、先頭の要素を削除する事で実現 * * 整数 queue : [ 1, 2, .., 10 ] の表現 * * ICell * +-------+ +-------+ +-------+ * next +-> | *-----> | *-----> ... --> | NULL | * value | | | | | +-> | | * | +-------+ +-------+ ... | +-------+ * | | 1 | | 2 | | | 10 | * | +-------+ +-------+ | +-------+ * IQueue | | * +-----------+ | | * head | *-------+ | * +-----------+ | * tail | *---------------------------------------+ * +-----------+ * * 空な整数 queue : [] の表現 * 空な整数 queue は、head/tail 共に NULL とする * * IQueue * +-----------+ * head | NULL | * +-----------+ * tail | NULL | * +-----------+ * */ typedef struct icell { struct icell *next; /* 次のセルへのポインター */ int value; /* このセルが持つ値(整数値) */ } ICell; typedef struct { ICell *head; /* リストの先頭の要素へのポインター */ ICell *tail; /* リストの最後の要素へのポインター */ } IQueue; /* */ #define NORMAL (0) /* 処理に成功した場合の値 */ #define ERROR (-1) /* 処理に失敗した場合の値 */ /* */ #define FALSE (0) /* 論理値 (偽値) */ #define TRUE (!FALSE) /* 論理値 (真値) */ /* * alloc_icell * 指定した整数値(data) を保持する icell を作成する * return 値 * 作成された icell へのポインター値 * alloc に失敗した場合は NULL 値が返る */ ICell *alloc_icell ( int data ) { ICell *result = (ICell *)malloc(sizeof(ICell)); if ( result != NULL ) { /* 次の要素 (next) は NULL にする */ result -> next = NULL; /* 保持する値 (value) は 指定された値 (data) にする */ /* ** この部分を完成させなさい */ } return result; } /* * free_icell * 指定した整数値を保持する icell (icp) を開放する * return 値 * 開放に成功したかどうか ( 成功 -> NORMAL / 失敗 -> ERROR ) */ int free_icell ( ICell *icp ) { int result = ERROR; if ( icp != NULL ) { free ( icp ); result = NORMAL; } return result; } /* * alloc_iqueue * 空っぽな整数 queeue (iqueue) を作成する * return 値 * 作成された iqueue へのポインター値 * alloc に失敗した場合は NULL 値が返る */ IQueue *alloc_iqueue ( void ) { IQueue *result = (IQueue *)malloc(sizeof(IQueue)); if ( result != NULL ) { /* 空な整数 queue は、先頭も最後も NULL にする */ /* ** この部分を完成させなさい */ } return result; } /* * is_empty * 指定した整数 queue が空かどうかを判定する * return 値 * 空の場合 TRUE * 空でない場合 TRUE 以外 */ int is_empty ( IQueue *iqp ) { if ( iqp != NULL ) { if ( iqp -> head == NULL ) { return TRUE; } } return FALSE; } /* * enque * 指定された 整数 queue (iqp) に指定された整数値 (data) を追加する * return 値 * 追加できた場合 NORMAL * それ以外 ERROR */ int enque ( IQueue *iqp, int data ) { int result = ERROR; /* 途中で、何か上手く行かなければ ERROR */ if ( iqp != NULL ) { ICell *icp = alloc_icell ( data ); /* 新しい ICell を作成 */ if ( icp != NULL ) { /* 作成できたら */ if ( is_empty ( iqp ) ) { /* 空っぽの場合 */ iqp -> head = icp; /* 先頭も新しい要素 */ } else { iqp -> tail -> next = icp; /* 最後の要素の次に新しい要素を追加 */ } /* 最後の要素は、追加した要素 */ /* ** この部分を完成させなさい */ result = NORMAL; /* 追加に成功 (全て OK) */ } } return result; } /* * dequeue * 指定された 整数 queue (iqp) から、要素を取り出す * return 値 * 取り出せた場合 取り出した値 * 取り出せない場合 ERROR * <<注意>> * 整数 queue の中に ERROR があると、 * 取り出しに成功したか失敗したか区別できない (不適切な設計 !!) */ int deque ( IQueue *iqp ) { int result = ERROR; if ( iqp != NULL ) { if ( !is_empty ( iqp ) ) { /* queue が空でない */ ICell *top = iqp -> head; /* 先頭の要素(ICell)を取り出す */ result = top -> value; /* 返す値は、先頭にある */ if ( ( iqp -> head = top -> next) == NULL ) { /* 新しい先頭は、先頭の次の要素 */ /* queue が空になった場合は tail も NULL にする */ /* ** この部分を完成させなさい */ } free ( top ); /* 先頭の要素は開放 */ } } return result; } /* * free_que * 指定された 整数 queue (iqp) を全て開放する */ void free_que ( IQueue *iqp ) { if ( iqp != NULL ) { ICell *icp = iqp -> head; /* 先頭の要素を、次の処理対象に */ ICell *next; /* 次の要素へのポインタ値を保持 */ while ( icp != NULL ) { /* 未だ、要素があれば.. */ next = icp -> next; /* 次のために、次の要素へのポインタ値をバックアップ */ free_icell ( icp ); /* 注目している要素を開放 */ /* 次の要素を、次の処理対象にする */ /* ** この部分を完成させなさい */ } free ( iqp ); /* queue そのものを開放 */ } } /* * print_que * 指定された整数 que の内容を表示する */ void print_que ( IQueue *iqp ) { ICell *icp = iqp -> head; printf ( "[" ); for ( icp = iqp -> head; icp != NULL; icp = icp -> next ) { /* 現在着目している要素 (icp) が保持する値を表示 */ /* ** この部分を完成させなさい */ if ( icp -> next != NULL ) { /* 次の要素があれば .. */ printf ( "," ); /* 「,」を出力 */ } else { printf ( " " ); /* 「 」を出力 */ } } printf ( "]" ); } /* * main */ int main ( int argc, char *argv[] ) { IQueue *iqp = alloc_iqueue(); /* 空の queue の作成 */ print_que ( iqp ); putchar ( '\n' ); /* 空の queue [] */ enque ( iqp, 1 ); /* que に 1 を追加 */ print_que ( iqp ); putchar ( '\n' ); /* 要素が 1 つの queue [ 1 ] */ enque ( iqp, 2 ); enque ( iqp, 3 ); /* que に 2, 3 を追加 */ print_que ( iqp ); putchar ( '\n' ); /* 要素が 3 つの queue [ 1, 2, 3 ] */ printf ( "%d\n", deque ( iqp ) ); /* 先頭の要素を削除 */ print_que ( iqp ); putchar ( '\n' ); /* 要素が 2 つの queue [ 2, 3 ] */ free_que ( iqp ); /* que の開放 */ return 0; }
$ ./20201218-01-QQQQ.exe [] [ 1 ] [ 1, 2, 3 ] 1 [ 2, 3 ] $
Download : 20201218-02.c
/* * 20201218-02-QQQQ.c * */ #include <stdio.h> /* * main */ int main ( int argc, char *argv[] ) { if ( argc != 3 ) { printf ( "ファイル名を二つ指定して下さい。\n" ); } else { FILE *fp1; if ( ( fp1 = fopen ( argv[1], "r" ) ) == NULL ) { printf ( "ファイル(%s)を開く事ができませんでした。\n", argv[1] ); } else { FILE *fp2; if ( ( fp2 = fopen ( argv[2], "r" ) ) == NULL ) { printf ( "ファイル(%s)を開く事ができませんでした。\n", argv[2] ); } else { int position; int ch1; int ch2; for ( position = 0; (ch1 = fgetc (fp1)) != EOF; position++ ) { ch2 = fgetc( fp2 ); if ( ch1 != ch2 ) { break; } } if ( ch1 == EOF ) { ch2 = fgetc( fp2 ); } if ( ch1 == ch2 ) { printf ( "二つのファイル(%s,%s)は同じ内容です。\n", argv[1], argv[2] ); } else if ( ch1 == EOF ) { /* ** この部分を完成させなさい */ } else if ( ch2 == EOF ) { printf ( "ファイル(%s) の方がサイズが大きいです。\n", argv[1] ); } else { printf ( "%d byte 目で %s は %c, %s は %c という違いがありました。\n", position, argv[1], ch1, argv[2], ch2 ); } fclose ( fp2 ); } fclose ( fp1 ); } } return 0; }
$ ./20201218-02-QQQQ.exe ファイル名を二つ指定して下さい。 $