Powered by SmartDoc

ソフトウェア概論A/B (2019/12/20)
Ver. 1.0

2019年12月20日
栗野 俊一
kurino@math.cst.nihon-u.ac.jp
http://edu-gw2.math.cst.nihon-u.ac.jp/~kurino/2019/soft/soft.html
ソフトウェア概論 A/B2019年12月20日 の資料

目次

講義資料

当日の OHP 資料

講義で利用するサンプルプログラム

Download : sample-001.c

sample-001.c
/*
 * 2019/12/20 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.c の実行結果
$ ./sample-001.exe
sub1 : &i = 0x7fffbaefabcc
sub1 : i = 123456
sub2 : &j = 0x7fffbaefabcc
j = 123456
$ 

Download : sample-002.c

sample-002.c
/*
 * 2019/12/20 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.c の実行結果
$ ./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

sample-003.c
/*
 * 2019/12/20 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.c の実行結果
$ ./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

sample-004.c
/*
 * 2019/12/20 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.c の実行結果
$ ./sample-004.exe
$ 

講議中に作成したプログラム

本日の課題

課題プログラム内の「/*名前:ここ*/」の部分を書き換え「/*この部分を完成させなさい*/」の部分にプログラムを追加して、プログラムを完成させます。

課題 20191220-01 : 整数値のキュー(queue)

Download : 20191220-01.c

20191220-01.c
/*
 * 20191220-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;
}
20191220-01.c の実行結果
$ ./20191220-01-QQQQ.exe
[]
[ 1 ]
[ 1, 2, 3 ]
1
[ 2, 3 ]
$ 

課題 20191220-02 : 二つのファイルを比較して最初に異る場所を表示する

Download : 20191220-02.c

20191220-02.c
/*
 * 20191220-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;
}
20191220-02.c の実行結果
$ ./20191220-02-QQQQ.exe
ファイル名を二つ指定して下さい。
$