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 ファイル名を二つ指定して下さい。 $