Powered by SmartDoc

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

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

目次

講義資料

当日の OHP 資料

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

Download : sample-001.c

sample-001.c
/*
 * 2019/12/20 sample-001.c
 */

/*
 *
 */

#include <stdio.h>

/*
 *
 */

int main ( int ac, char *av[] ) {
	int stack[1000];	/* 配列サイズは適当(充分に大きく取ってあると *仮定*) */
	int stack_pointer;	/* sp */
	int data;			/* データ */

		/*
				「スタック」の設計方針
					「スタック」は、「下から上に積む」物として考える
					「スタックポインター(sp)」は、「次にデータを積む場所」と考える

						...
					+-----------+
				2	|			|
					+-----------+
				1	|			|
					+-----------+
				0	|			|		<- sp (stack_pointer) == 0
			--------+-----------+-----
		*/

		/*
		 * スタックを利用する前に、スタックの初期化を行う
		 */

	stack_pointer = 0;	/* 初期状態では、空 (sp == 0) */

		/*
		 * 最初にスタックに 100 を積む : push ( 100 )
		 */

	data = 100;							/* データは 100 */
	printf ( "スタックに %d を積みます。\n", data );

	stack [ stack_pointer ] = data;		/* 現在の場所にデータを保存 */
	stack_pointer = stack_pointer + 1;	/* スタックポインターは「次」を指す */

		/*
				データは「現在の場所」に保存され、
				sp が「次の場所」を指すようになる

						...
					+-----------+
				2	|			|
					+-----------+
				1	|			|		<- sp (stack_pointer) == 1
					+-----------+
				0	|	100		|		スタックトップには 100 が入る
			--------+-----------+-----
		*/

		/*
		 * スタックに 123 を積む : push ( 123 )
		 */

	data = 123;
	printf ( "スタックに %d を積みます。\n", data );

	stack [ stack_pointer ] = data;
	stack_pointer = stack_pointer + 1;

		/*

						...
					+-----------+
				2	|			|		<- sp (stack_pointer) == 2
					+-----------+
				1	|	123		|		スタックトップには 123 が入る
					+-----------+
				0	|	100		|
			--------+-----------+-----
		*/

		/*
		 * スタックに 999 を積む  : push ( 999 )
		 */

	data = 999;
	printf ( "スタックに %d を積みます。\n", data );

	stack [ stack_pointer ] = data;
	stack_pointer = stack_pointer + 1;

		/*

						...
					+-----------+
				3	|			|		<- sp (stack_pointer) == 3
					+-----------+
				2	|	999		|		スタックトップには 999 が入る
					+-----------+
				1	|	123		|
					+-----------+
				0	|	100		|
			--------+-----------+-----
		*/

		/*
		 * スタックからデータを取り出す : pop() => 999 が取り出される
		 */

	stack_pointer = stack_pointer - 1;	/* まず、sp を戻す */
	data = stack [ stack_pointer ];		/* sp が指す先がスタックトップ */

	printf ( "スタックから取出したデータは %d です。\n", data );

		/*

						...
					+-----------+
				3	|			|
					+-----------+
				2	|	999		|		<- sp (stack_pointer) == 2
					+-----------+
				1	|	123		|
					+-----------+
				0	|	100		|
			--------+-----------+-----
		*/

		/*
		 * もう一度スタックからデータを取り出す : pop() => 123 が取り出される
		 */

	stack_pointer = stack_pointer - 1;
	data = stack [ stack_pointer ];

	printf ( "スタックから取出したデータは %d です。\n", data );

		/*

						...
					+-----------+
				3	|			|
					+-----------+
				2	|	999		|		<- データは消えない(が、利用禁止 !!)
					+-----------+
				1	|	123		|		<- sp (stack_pointer) == 1
					+-----------+
				0	|	100		|
			--------+-----------+-----
		*/

		/*
		 * スタックに 876 を積む  : push ( 876 )
		 */

	data = 876;
	printf ( "スタックに %d を積みます。\n", data );

	stack [ stack_pointer ] = data;
	stack_pointer = stack_pointer + 1;

		/*

						...
					+-----------+
				3	|			|
					+-----------+
				2	|	999		|		<- sp (stack_pointer) == 2
					+-----------+
				1	|	876		|		<- データは上書き(領域は再利用)
					+-----------+
				0	|	100		|
			--------+-----------+-----
		*/

		/*
		 * スタックからデータを取り出す : pop() => 876 が取り出される
		 */

	stack_pointer = stack_pointer - 1;
	data = stack [ stack_pointer ];

	printf ( "スタックから取出したデータは %d です。\n", data );

		/*

						...
					+-----------+
				3	|			|
					+-----------+
				2	|	999		|
					+-----------+
				1	|	876		|		<- sp (stack_pointer) == 1
					+-----------+
				0	|	100		|
			--------+-----------+-----
		*/

		/*
		 * スタックからデータを取り出す : pop() => 100 が取り出される
		 */

	stack_pointer = stack_pointer - 1;
	data = stack [ stack_pointer ];

	printf ( "スタックから取出したデータは %d です。\n", data );

		/*

						...
					+-----------+
				3	|			|
					+-----------+
				2	|	999		|
					+-----------+
				1	|	876		|
					+-----------+
				0	|	100		|		<- sp (stack_pointer) == 0 : 空っぽ
			--------+-----------+-----
		*/

	return 0;
}

/*
 *
 */
sample-001.c の実行結果
$ ./sample-001.exe
スタックに 100 を積みます。
スタックに 123 を積みます。
スタックに 999 を積みます。
スタックから取出したデータは 999 です。
スタックから取出したデータは 123 です。
スタックに 876 を積みます。
スタックから取出したデータは 876 です。
スタックから取出したデータは 100 です。
$ 

Download : sample-002.c

sample-002.c
/*
 * 2019/12/20 sample-002.c
 */

/*
 *
 */

#include <stdio.h>

/*
 *
 */

#include "int_stack.h"

/*
 *
 */

void push_and_print ( IntStack *isp, int data ) {

	printf ( "スタックに %d を積みます。\n", data );

	push_int_stack ( isp, data );	/* data を push */

}

/*
 *
 */

int pop_and_print ( IntStack *isp ) {
	int data;

	data = pop_int_stack ( isp );	/* data を pop */

	printf ( "スタックから取出したデータは %d です。\n", data );
	
	return data;
}

/*
 *
 */

int main ( int ac, char *av[] ) {
	IntStack stack;		/* スタックの宣言 */

		/*
		 * スタックを利用する前に、スタックの初期化を行う
		 */

	clear_int_stack ( &stack );	/* ポインターを渡す */

		/*
				「スタック」の設計方針
					「スタック」は、「下から上に積む」物として考える
					「スタックポインター(sp)」は、「次にデータを積む場所」と考える

						...
					+-----------+
				2	|			|
					+-----------+
				1	|			|
					+-----------+
				0	|			|		<- sp (stack_pointer) == 0
			--------+-----------+-----
		*/

		/*
		 * 最初にスタックに 100 を積む : push ( 100 )
		 */

	push_and_print ( &stack, 100 );

		/*
				データは「現在の場所」に保存され、
				sp が「次の場所」を指すようになる

						...
					+-----------+
				2	|			|
					+-----------+
				1	|			|		<- sp (stack_pointer) == 1
					+-----------+
				0	|	100		|		スタックトップには 100 が入る
			--------+-----------+-----
		*/

		/*
		 * スタックに 123 を積む : push ( 123 )
		 */

	push_and_print ( &stack, 123 );

		/*

						...
					+-----------+
				2	|			|		<- sp (stack_pointer) == 2
					+-----------+
				1	|	123		|		スタックトップには 123 が入る
					+-----------+
				0	|	100		|
			--------+-----------+-----
		*/

		/*
		 * スタックに 999 を積む  : push ( 999 )
		 */

	push_and_print ( &stack, 999 );

		/*

						...
					+-----------+
				3	|			|		<- sp (stack_pointer) == 3
					+-----------+
				2	|	999		|		スタックトップには 999 が入る
					+-----------+
				1	|	123		|
					+-----------+
				0	|	100		|
			--------+-----------+-----
		*/

		/*
		 * スタックからデータを取り出す : pop() => 999 が取り出される
		 */

	pop_and_print ( &stack );

		/*

						...
					+-----------+
				3	|			|
					+-----------+
				2	|	999		|		<- sp (stack_pointer) == 2
					+-----------+
				1	|	123		|
					+-----------+
				0	|	100		|
			--------+-----------+-----
		*/

		/*
		 * もう一度スタックからデータを取り出す : pop() => 123 が取り出される
		 */

	pop_and_print ( &stack );

		/*

						...
					+-----------+
				3	|			|
					+-----------+
				2	|	999		|		<- データは消えない(が、利用禁止 !!)
					+-----------+
				1	|	123		|		<- sp (stack_pointer) == 1
					+-----------+
				0	|	100		|
			--------+-----------+-----
		*/

		/*
		 * スタックに 876 を積む  : push ( 876 )
		 */

	push_and_print ( &stack, 876 );

		/*

						...
					+-----------+
				3	|			|
					+-----------+
				2	|	999		|		<- sp (stack_pointer) == 2
					+-----------+
				1	|	876		|		<- データは上書き(領域は再利用)
					+-----------+
				0	|	100		|
			--------+-----------+-----
		*/

		/*
		 * スタックからデータを取り出す : pop() => 876 が取り出される
		 */

	pop_and_print ( &stack );

		/*

						...
					+-----------+
				3	|			|
					+-----------+
				2	|	999		|
					+-----------+
				1	|	876		|		<- sp (stack_pointer) == 1
					+-----------+
				0	|	100		|
			--------+-----------+-----
		*/

		/*
		 * スタックからデータを取り出す : pop() => 100 が取り出される
		 */

	pop_and_print ( &stack );

		/*

						...
					+-----------+
				3	|			|
					+-----------+
				2	|	999		|
					+-----------+
				1	|	876		|
					+-----------+
				0	|	100		|		<- sp (stack_pointer) == 0 : 空っぽ
			--------+-----------+-----
		*/

	return 0;
}

/*
 *
 */
sample-002.c の実行結果
$ ./sample-002.exe
スタックに 100 を積みます。
スタックに 123 を積みます。
スタックに 999 を積みます。
スタックから取出したデータは 999 です。
スタックから取出したデータは 123 です。
スタックに 876 を積みます。
スタックから取出したデータは 876 です。
スタックから取出したデータは 100 です。
$ 

Download : sample-003.c

sample-003.c
/*
 * 2019/12/20 sample-003.c
 */

/*
 *
 */

#include <stdio.h>
#include "s_print.h"
#include "s_input.h"

/*
 *
 */

#define	EOS	'\0'

/*
 * 複数の整数を書式指定して文字列の中に埋め込む
 */

void print_with_format ( char *msg, ... ) {
  int i;
  char *pvalue = (char *)&msg + sizeof( char * );

  for ( i = 0; msg[i] != EOS; i++ ) {
	if ( msg[i] == '%' ) {		/* 文字列の中に '%' があったら特別処理する */
	  i++; 						/* 次の文字をみる */
	  switch ( msg[i] ) {
	  case 'd':					/* 10 進数 */
		s_print_int ( *((int *)pvalue) );			// s_print_int ( *pvalue++ );
		pvalue += sizeof ( int );
		break;
	  case 'o':					/* 8 進数 */
		s_print_int ( *((int *)pvalue) );
		pvalue += sizeof ( int );
		break;
	  case 'x':					/* 16 進数 */
		s_print_int ( *((int *)pvalue) );
		pvalue += sizeof ( int );
		break;
	  case 'c':					/* 文字 */
		s_print_char ( *((char *)pvalue) );
		pvalue += sizeof ( int );	/* char は自動的に int にされる */
		break;
	  case 's':					/* 文字列 */
		s_print_string ( *((char **)pvalue) );
		pvalue += sizeof ( char * );
		break;
	  case 'f':					/* 浮動小数点数 */
		s_print_double ( *((double *)pvalue) );
		pvalue += sizeof ( double );
		break;
	  case '%':					/* '%' が重なったら.. */
		s_print_char ( '%' );	/* '%' を出力 */
		break;
	  default:					/* その他 : よくわからないので読み飛ばす.. */
		  break;
	  }
	} else {	/* そうでなけれ .. */
	  s_print_char ( msg[i] );	/* そのままその文字を出力 */
	}
  }
}

/*
 * 色々な数値の出力
 */

int main ( void ) {

  /*
   * データの出力 (Output)
   */

  print_with_format ( 
					 "整数値(%%d) : %d, 文字(%%c) : '%c', 文字列(%%s) : \"%s\", 浮動小数点数(%%f) : %f\n",
					 123, (int)'a', "xyz", 1.23 );

  /*
   * 
   */

  return 0;
}

/*
 *
 */
sample-003.c の実行結果
$ ./sample-003.exe
整数値(%d) : 1, 文字(%c) : '%', 文字列(%s) : "h`?D?", 浮動小数点数(%f) : 0.000000
$ 

Download : sample-004.c

sample-004.c
/*
 * 2019/12/20 sample-004.c
 */

#include <stdio.h>
#include <math.h>

/*
 * cf. http://www.asahi-net.or.jp/~uc3k-ymd/Lesson/Section01/section01_06.html
 */

//  10進→2進へ変換して出力
void dec2bin(double x, int bit[])
{
    // ビット演算は、整数型または文字型に対して使用しなければならないので、
    // 共用体を使って浮動小数点型のデータを文字型(整数型)として参照する
    union {
            double   d;  // 8バイト(64ビット)の領域
            unsigned char  c[8];
    } data;

    data.d = x; //double型で代入

    int i,j, k = 0;
    for(i=7; i >= 0; i--) {// 文字型で1バイトごとに取り出す
        unsigned char ch = data.c[i];
        for(j = 7; j >= 0; j--){ // 1バイト単位で2進数に変換を行う
		  if( (ch >> j) & 1 ) {
                bit[k] = 1;
          } else {
                bit[k] = 0;
		  }
		  // ビットを出力
		  putchar ( '0' + bit[k] );
		  k++;
        }
        putchar ( ' ' );  // 1バイトごとに区切りを入れる
    }
    putchar ( '\n' );
}

//  仮数部と指数部を2進→10進へ変換して出力する
void bin2dec(int bit[])
{
    int BIAS = 1023; // 指数バイアス
    // 指数部は0〜2047の値で表されている、この値から
    // BIASを引くことで、-1023〜1024の範囲で本当の値が求まる

    int    exponent=0;        // 指数部
    double fraction=0.0;      // 仮数部の小数点以下
    int sign  = 1 - 2*bit[0]; //符号ビット0:正、1:負なので
                              // 0 -> 1  1 -> -1 となる

    // 指数部を10進整数に変換
    int i;
    for (i=11; i>=1; i--)  {// 指数部は11ビット
        exponent += (int)(bit[i]*pow(2, 11-i));
	}

    // 仮数部を10進小数に変換
    for (i=12; i<=63; i++)   {// 仮数部は52ビット
        fraction += bit[i]*pow(2, 11-i);
	}

	printf ( "%f ×2^%d\n", sign * (1.0 + fraction), (exponent - BIAS) );

}

int main(void)
{
    int bit[64]; // ビットパターンを格納する
    double x;

    printf ( "実数値を入力して下さい: " );
    scanf ( "%lf", &x );

    printf ( "\ndouble型の入力データの内部表現(2進数表示)\n\n"
             " [指数部    ][ ---- 仮数部                                      ----- ]\n" );

    dec2bin(x, bit);//  10進→2進へ変換しbit[]に格納する

	printf ( "\n入力データ: %f = ", x );

    bin2dec(bit);//  仮数部と指数部の2進→10進へ変換

    return 0;
}
入力例
0.1
sample-004.c の実行結果
$ ./sample-004.exe < sample-004.in
実数値を入力して下さい: 0.100000

double型の入力データの内部表現(2進数表示)

 [指数部    ][ ---- 仮数部                                      ----- ]
00111111 10111001 10011001 10011001 10011001 10011001 10011001 10011010 

入力データ: 0.100000 = 1.600000 ×2^-4
$ 

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

本日の課題

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

課題 20191227-01 : ポインター演算子を利用して構造体を操作

Download : 20191227-01.c

20191227-01.c
/*
 * 20191227-01-QQQQ.c
 * ポインター演算子を利用して構造体を操作
 */

#include <stdio.h>

/*
 *	二次元の「点」を表す構造体型 Point2D の宣言
 */

typedef	struct {
	int x;
	int y;
} Point2D;

/*
 *	move_to_x_axis_symmetry ( Point2D *p2dPtr )
 *		指定された Point2D 型の変数へのポインターの値を x 軸に対象な点に移す
 *		 Point2D *p2dPtr : Point2D 型の変数へのポインターの値
 */

void move_to_x_axis_symmetry ( Point2D *p2dPtr ) {

	/* x 軸に対称なので、y 座標の符号だけを変更 */

	p2dPtr -> y = - p2dPtr -> y;	/* y 座標の符号を逆転 */

}

/*
 *	move_to_y_axis_symmetry ( Point2D *p2dPtr )
 *		指定された Point2D 型の変数へのポインターの値を y 軸に対象な点に移す
 *		Point2D *p2dPtr : Point2D 型の変数へのポインターの値
 */

void move_to_y_axis_symmetry ( Point2D *p2dPtr ) {

	/* y 軸に対称なので、x 座標の符号だけを変更 */


	/*
	**	 この部分を完成させなさい
	*/


}

/*
 *	move_to_origin_symmetry ( Point2D *p2dPtr )
 *		指定された Point2D 型の変数へのポインターの値を 原点に対象な点に移す
 *		Point2D *p2dPtr : Point2D 型の変数へのポインターの値
 */

void move_to_origin_symmetry ( Point2D *p2dPtr )	{
	/* 原点対称に移動するには、
	   x 軸対称に移動して、から y 軸対称に移動すればよい
	*/

	move_to_x_axis_symmetry ( p2dPtr );	/* 引数は初めからポインター値 */

	/*
	**	 この部分を完成させなさい
	*/


}

/*
 *	print_point2d ( Point2D p2dVar )
 *		指定された Point2D 型の値を表示する
 *		Point2D p2dVar; Point2D 型の値
 */

void print_point2d ( Point2D p2dVar ) {

	 /* x, y 座標をそれぞれ出力するだけ */
	 printf ( "(%d, %d)", p2dVar.x, p2dVar.y );

}

/*
 *
 */

int main ( void ) {
	Point2D Pa = { -1, 3 };	/* 座標 ( -1, 3 ) の点 Pa */
	Point2D Pb = { 2, 5 };	/* 座標 ( 2, 5 ) の点 Pb */

	printf ( "Pa = " );
	print_point2d ( Pa );				/* Point2D の値を指定 */
	printf ( "\nを、x 軸対称な位置に移動すると.." );
	move_to_x_axis_symmetry ( &Pa );	/* Point2D 型の変数のポインター値を指定 */
	print_point2d ( Pa );
	printf ( "になります\n" );

	printf ( "Pb = " );
	print_point2d ( Pb );
	printf ( "\nを、原点対称な位置に移動すると.." );

	/*
	**	 この部分を完成させなさい
	*/

	print_point2d ( Pb );
	printf ( "になります\n" );

    return 0;

}

/*
 *
 */
入力例
2.3
9.1
5.9
2.7
3.2
20191227-01.c の実行結果
$ ./20191227-01-QQQQ.exe
Pa = (-1, 3)
を、x 軸対称な位置に移動すると..(-1, -3)になります
Pb = (2, 5)
を、原点対称な位置に移動すると..(-2, -5)になります
$