Download : sample-001.c
/* * 2019/11/15 sample-001.c */ /* * 複素数型の定義と計算 * * 利用方法 * コンパイル * cc -c sample-001.c * リンク * cc -o sample-001.exe sample-001.c * 実行 * ./sample-001.exe */ #include <stdio.h> /* * 複素数型を表す Complex の定義 * * 複素数 z は、二つの実数 x, y を用いて * z = x + yi ( i は虚数単位 ) * と表現できる。 * C 言語では実数を表すのに浮動小数点数型の double を用いる * 型名 ( Complex ) を大文字で始めるのは「ソフトウェア概論ルール」 */ typedef struct { double real; /* 実部 */ double imaginary; /* 虚部 */ } Complex; /* 複素数型 */ /* * Complex make_Complex ( double x, double y ) * Complex 型の数を作り、返す * x, y -> z = x + yi */ Complex make_Complex ( double x, double y ) { Complex newComplex; /* 新しく作られる複素数 */ newComplex.real = x; /* 新しく作られた複素数の実部は x */ newComplex.imaginary = y; /* 新しく作られた複素数の実部は y */ return newComplex; /* 新しく作られる複素数を値として返す */ } /* * double real_part ( Complex z ) * Complex 型の数の実部を返す * z = x + yi -> x */ double real_part ( Complex z ) { return z.real; } /* * double imaginary_part ( Complex z ) * Complex 型の数の実部を返す * z = x + yi -> x */ double imaginary_part ( Complex z ) { return z.imaginary; } /* * print_Complex ( Complex z ) * Complex 型の数の出力 * z = x + y i だが、y < 0 の時は z = x - (-y) i となるように工夫 */ void print_Complex ( Complex z ) { if ( z.imaginary > 0 ) { printf ( "%f + %f i", z.real, z.imaginary ); } else { printf ( "%f - %f i", z.real, - z.imaginary ); } } /* * Complex add_Complex ( Complex z1, Complex z2 ) * Complex 型の数の足し算 * z1 = x1 + y1 i * z2 = x2 + y2 i * -> z1 + z2 = ( x1 + x2 ) + ( y1 + y2 ) i */ Complex add_Complex ( Complex z1, Complex z2 ) { Complex result; /* 複素数の和の実部は、実部の和 */ result.real = z1.real + z2.real; /* 複素数の和の虚部は、虚部の和 */ result.imaginary = z1.imaginary + z2.imaginary; return result; } /* * main */ int main( int argc, char *argv[] ) { Complex z1 = make_Complex ( 2.0, 3.0 ); /* z1 = 2 + 3i */ Complex z2 = make_Complex ( -1.0, 5.0 ); /* z2 = -1 + 5i */ Complex z3; printf ( " z1 ( = " ); print_Complex ( z1 ); printf ( " ) と、" ); printf ( " z2 ( = " ); print_Complex ( z2 ); printf ( " ) の和は 、\n" ); z3 = add_Complex ( z1, z2 ); /* z3 <- z1 + z2 */ print_Complex ( z3 ); printf ( " です。\n" ); return 0; }
$ ./sample-001.exe z1 ( = 2.000000 + 3.000000 i ) と、 z2 ( = -1.000000 + 5.000000 i ) の和は 、 1.000000 + 8.000000 i です。 $
Download : sample-002.c
/* * 2019/11/15 sample-002.c */ /* * 二次元行列型の定義と計算 * * 利用方法 * コンパイル * cc -c sample-002.c * リンク * cc -o sample-002.exe sample-002.c * 実行 * ./sample-002.exe */ #include <stdio.h> /* * */ #define DIMENSION 2 /* 二次元 */ /* * 行列 A は、2 x 2 = 4 の要素をもっている * * A = ( 1 2 ) = ( a[0][0] a[0][1] ) * 3 4 a[1][0] a[1][1] * */ typedef struct { double a[DIMENSION][DIMENSION]; /* 二次元の行列の要素は 2 x 2 */ } Matrix2D; /* Matrix2D 型の宣言 */ /* * Matrix2D make_Matrix2D ( double a, double b, double c, double d ) * 「行列」を作成する * * A = ( a b ) = ( a[0][0], a[0][1] ) * ( c d ) ( a[1][0], a[1][1] ) */ Matrix2D make_Matrix2D ( double a, double b, double c, double d ) { Matrix2D newMatrix2D; /* 新しい行列 */ newMatrix2D.a[0][0] = a; newMatrix2D.a[0][1] = b; newMatrix2D.a[1][0] = c; newMatrix2D.a[1][1] = d; return newMatrix2D; } /* * void print_Matrix2D ( Matrix2D ary ); * 「行列」を表示する (表示の都合上、常に独立した行に出力する) * Matrix2D ary; 二次元行列 */ void print_Matrix2D ( Matrix2D ary ) { int r; /* 行 ( row ) */ int c; /* 列 ( colomun ) */ for ( r = 0; r < DIMENSION; r++ ) { printf ( "(" ); for ( c = 0; c < DIMENSION; c++ ) { printf ( " %10.5f", ary.a[r][c] ); /* * [注意] %10.5f は %f と同じく浮動小数点数を出力するが * 「全体の桁数は 10 桁、小数点数以下は 5 桁にする」 * という「表示上の指定」も加わっている * 詳しくは google で「printf 書式」で検索 */ } printf ( " )\n" ); } } /* * Matrix2D add_Matrix2D ( Matrix2D a1, Matrix2D a2 ); * 「行列」の和 * * ( a b ) + ( e f ) = ( a + e b + f ) * ( c d ) ( g h ) ( c + g g + h ) */ Matrix2D add_Matrix2D ( Matrix2D a1, Matrix2D a2 ) { Matrix2D result; /* 計算結果 */ int r; /* 行 ( row ) */ int c; /* 列 ( colomun ) */ for ( r = 0; r < DIMENSION; r++ ) { for ( c = 0; c < DIMENSION; c++ ) { result.a[r][c] = a1.a[r][c] + a2.a[r][c]; } } return result; } /* * main */ int main( int argc, char *argv[] ) { /* a1 = ( 1 2 ) ( 3 -1 ) a2 = ( -3 1 ) ( 1 -2 ) */ Matrix2D a1 = make_Matrix2D ( 1.0, 2.0, 3.0, -1.0 ); Matrix2D a2 = make_Matrix2D ( -3.0, 1.0, 1.0, -2.0 ); /* 行列 a1 と行列 a2 の和を計算して出力する */ print_Matrix2D ( a1 ); printf ( " と、 \n" ); print_Matrix2D ( a2 ); printf ( " との、和は \n" ); print_Matrix2D ( a ); printf ( " です。\n" ); return 0; }
$ ./sample-002.exe z1 ( = 2.000000 + 3.000000 i ) と、 z2 ( = -1.000000 + 5.000000 i ) の和は 、 1.000000 + 8.000000 i です。 $
/* * 課題 20191115-01 * * 20191115 20191115-01-QQQQ.c * * 複素数型の四則 */ #include <stdio.h> /* * 複素数型の定義と計算 * * 利用方法 * コンパイル * cc -Ic:\usr\c\include -o BASENAME.exe 20191115-01-QQQQ.c * 実行 * BASENAME */ #include <stdio.h> /* * 複素数型を表す Complex の定義 * * 複素数 z は、二つの実数 x, y を用いて * z = x + yi ( i は虚数単位 ) * と表現できる。 * C 言語では実数を表すのに浮動小数点数型の double を用いる * 型名 ( Complex ) を大文字で始めるのは「ソフトウェア概論ルール」 */ typedef struct { double real; /* 実部 */ double imaginary; /* 虚部 */ } Complex; /* 複素数型 */ /* * Complex make_Complex ( double x, double y ) * Complex 型の数を作り、返す * x, y -> z = x + yi * [コメント] あたしい型を作ったら、新しい型の要素を作る関数を設けた方がよい * 新しい型のデータを作る関数を構築子(コンストラクタ)/初期子(イニシャライザ)と呼ぶ * 特に、正規化が必要な場合は、このような関数が不可欠 */ Complex make_Complex ( double x, double y ) { Complex newComplex; /* 新しく作られる複素数 */ newComplex.real = x; /* 新しく作られた複素数の実部は x */ newComplex.imaginary = y; /* 新しく作られた複素数の実部は y */ return newComplex; /* 新しく作られる複素数を値として返す */ } /* * double real_part ( Complex z ) * Complex 型の数の実部を返す * z = x + yi -> x * 新しい型の部分を取り出す関数(アクセッサ)も、あると、(後で..)いろいろ便利 * # 新しい型を作るようになると、「一行関数」はざらにでてくる */ double real_part ( Complex z ) { return z.real; } /* * double imaginary_part ( Complex z ) * Complex 型の数の実部を返す * z = x + yi -> x */ double imaginary_part ( Complex z ) { return z.imaginary; } /* * print_Complex ( Complex z ) * Complex 型の数の出力 * z = x + y i だが、y < 0 の時は z = x - (-y) i となるように工夫 */ void print_Complex ( Complex z ) { if ( z.imaginary > 0.0 ) { printf ( "%f + %f i", z.real, z.imaginary ); } else { printf ( "%f - %f i", z.real, - z.imaginary ); } } /* * Complex add_Complex ( Complex z1, Complex z2 ) * Complex 型の数の足し算 * z1 = x1 + y1 i * z2 = x2 + y2 i * -> z1 + z2 = ( x1 + x2 ) + ( y1 + y2 ) i */ Complex add_Complex ( Complex z1, Complex z2 ) { Complex result; /* 複素数の和の実部は、実部の和 */ result.real = z1.real + z2.real; /* 複素数の和の虚部は、虚部の和 */ result.imaginary = z1.imaginary + z2.imaginary; return result; } /* * Complex sub_Complex ( Complex z1, Complex z2 ) * Complex 型の数の引き算 * z1 = x1 + y1 i * z2 = x2 + y2 i * -> z1 - z2 = ( x1 - x2 ) + ( y1 - y2 ) i */ Complex sub_Complex ( Complex z1, Complex z2 ) { Complex result; /* 複素数の差の実部は、実部の差 */ result.real = z1.real - z2.real; /* 複素数の差の虚部は、虚部の差 */ result.imaginary = z1.imaginary - z2.imaginary; return result; } /* * Complex mul_Complex ( Complex z1, Complex z2 ) * Complex 型の数のかけ算 * z1 = x1 + y1 i * z2 = x2 + y2 i * の時 * z1 * z2 = (x1 * x2 - y1 * y2) + (x1 * y2 + x2 * y1) i */ Complex mul_Complex ( Complex z1, Complex z2 ) { Complex result; result.real = z1.real * z2.real - z1.imaginary * z2.imaginary; result.imaginary = z1.real * z2.imaginary + z1.imaginary * z2.real; return result; } /* * Complex div_Complex ( Complex z1, Complex z2 ) * Complex 型の数の割り算 * z1 = x1 + y1 i * z2 = x2 + y2 i * の時 * z1 / z2 = ( x1 + y1 i ) / ( x2 + y2 i ) * = ( x1 + y1 i )( x2 - y2 i ) / ( x2 + y2 i )( x2 - y2 i ) * = ( x1 * x2 + y1 * y2) / ( x2^2 + y2^2 ) * + ( (- x1 * y2 + x2 * y1) / ( x2^2 + y2^2 ) ) i */ Complex div_Complex ( Complex z1, Complex z2 ) { Complex result; double denominator = z2.real * z2.real + z2.imaginary *z2.imaginary; /* 実部、虚部の割る数 |z2|^2 を予め計算しておく */ /* ** この部分を完成させなさい */ result.imaginary = ( - z1.real * z2.imaginary + z1.imaginary * z2.real ) / denominator; return result; } /* * print_result 演算結果を出力する */ void print_result ( Complex z1, Complex z2, char *operator, Complex z ) { print_Complex ( z1 ); printf ( " と、 " ); print_Complex ( z2 ); printf ( " との、%s は ", operator ); print_Complex ( z ); printf ( " です。\n" ); } /* * main */ int main( int argc, char *argv[] ) { Complex z1 = make_Complex ( 20.0, -15.0 ); /* z1 = 20 - 15i */ Complex z2 = make_Complex ( 1.0, 2.0 ); /* z2 = 1 + 2i */ /* 和の出力 */ print_result ( z1, z2, "和", add_Complex ( z1, z2 ) ); /* 差の出力 */ print_result ( z1, z2, "差", sub_Complex ( z1, z2 ) ); /* 積の出力 */ print_result ( z1, z2, "積", mul_Complex ( z1, z2 ) ); /* 商の出力 */ print_result ( z1, z2, "商", div_Complex ( z1, z2 ) ); return 0; }
/* * 課題 20191115-03 * * 20191115 20191115-03-QQQQ.c * * 整数型の配列を作り、それに 5 個のデータを入力し * その値を 5 倍にしたものと 2 分の 1 にした値を * それぞれ、画面に出力するプログラムを作りなさい * * ポイントは、入力した5つのデータを二回利用している * 五つのデータを、いったん変数に保存し、それをあとで利用したい * この時に、五つのデータは、同じ操作をするので、繰り返しで処理したい * => 配列と for の組み合わせで処理する */ #include <stdio.h> /* * */ #define DATA_SIZE 5 /* データのサイズ (個数) */ /* #define を利用して、以下、「DATA_SIZE」が現れたら、「5」に置き換える */ /* => 後で、5 個を 6 個にしたければ、ここを 6 するだけで済む */ /* * */ int main(int argc, char *argv[]) { int array[DATA_SIZE]; /* サイズが DATA_SIZE の整数型の配列 array の宣言 */ /* 配列の宣言では、配列のサイズはプログラム時点で確定している必要がある */ /* 固定の理由は、ここで、そのサイズの個数の要素を用意する必要 */ int i; /* 添字変数 i を宣言 */ for ( i = 0; i < DATA_SIZE; i++ ) { /* 配列 array に数値を読み込む */ /* プロンプト */ printf ( "%d 番目の整数値を入力してください : ", i + 1 ); /* 配列へのデータ入力 */ scanf ( "%d", &array[i] ); /* &array[i] -> &(array[i]) [ != (&array)[i] ] */ } /* 入力された個々の値を 5 倍した物を出力 */ for ( i = 0; i < DATA_SIZE; i++ ) { printf ( "%d\n", array[i] * 5 ); /* 5 倍 */ } /* 入力された個々の値を 1/2 した物を出力 */ for ( i = 0; i < DATA_SIZE; i++ ) { printf ( "%d\n", array[i] / 2 ); /* 2 分の 1 */ /* 整数割り算になるので、小数点以下は切り捨て */ } return 0; }
#include <stdio.h> /* 平面上の点を、直交座標系での二つの座標 ( x, y ) の組で、表現する x, y は実数値 */ typedef struct { double x; double y; } Point; /* 二つの座標によって、一つの点を表現 Point 型が導入される */ /* Point 型は構造体として宣言されているので.. 最低限、次の操作ができる */ /* タグ名を利用して、部分を操作(参照:値の取り出し/代入:値の設定)が可能 */ /* 全体の参照と代入が可能 */ void print_point ( Point pt ) { printf ( "( %f, %f )", pt.x, pt.y ); /* pt.x, pt.y => (C 言語的には..) 単に Point 型の二つの要素の一つずつ.. */ /* この printf によって、一つ目の ( x で参照される ) 要素が「 x 座標」という 意味付けが行わている (ことになる) */ /* もし、ここで、 printf ( "( %f, %f )", pt.x * cos( pt.y ), pt.x * sin( pt.y ) ); としたら、これは、一つ目の要素が、動径の長さで、二つ目の要素が角度を 保持しているとした、極座標形式で点を表す事を意味している */ /* データ型は、「形式(二つの実数値の組)」だけでは、意味が定まらない */ /* それを操作するプログラムと対にして、初めて「意味」が決まる */ /* int 型 / double 型 : 当然データの形式を表現している同時に、 四則などの計算機能が、対になって初めて、意味がある */ /* C 言語では、typedef で定義した新しい型(形式のみ)と、その意味付け (その新しい型を操作するプログラム[関数]の実装) の対応は、「恣意的 (プログラマはわかっているが、コンピュータにはその情報がない)」 # オブジェクト指向型言語では、「その意図(形式と意味)の一部」を # コンピュータに伝える表現方法(Class/Object 記述)がある # => これによって、プログラムの作成により、コンピュータが # サポートしてくれるようになる => 形式と意味対応は、プログラマが管理し、意識的に対応付けを行う必要がある # 高級でない.. */ } /* 新しく作ったデータ型に、「意味付け」を行うということは、 そのデータ型を処理する、関数を増やす */ /* 与えらえた点を、x 軸方向に、dx 、y 軸方向に dy だけ、平行移動した結果を 返す関数 */ Point shift_point ( Point base, double dx, double dy ) { /* base の値は、実引数で指定した Point 型の値のコピーとなる (代入されている) */ /* => この値を変更しても、関数の呼び出し元には影響しない */ base.x = base.x + dx; /* base.x += dx */ base.y = base.y + dy; /* base.y += dy */ return base; /* 平行移動した結果が返る */ } int main(int argc, char *argv[] ) { Point p1; /* Point 型の変数 p1 が宣言できる */ Point p2; Point p3; p1.x = 1.0; /* p1 の個々の要素 ( x と y ) は、ピリオドとタグ名を使って参照可能 */ p1.y = -2.0; /* p1 <- (1.0,-2.0) としたのと同じ */ printf ( "p1 = " ); print_point ( p1 ); /* p1 は Point 型の変数で、その値も Point 型の値になるが.. */ /* 他の単純型(あらかじめ用意されている型:int.double.etc..) と同様に 引数に指定できる */ printf ( "\n" ); /* p1 を p2 にコピーする二つの方法 */ /* 方法 1 : 要素毎に行う (従来と同じ) */ p2.x = p1.x; /* 個々に要素を指定してコピーをする */ p2.y = p1.y; /* 個々の要素の値を代入する時に、いろいろな変更が可能 */ printf ( "p2 = " ); print_point ( p2 ); printf ( "\n" ); /* 方法 2 : 丸ごとコピーもできる (新しい型のデータの変数は、「まとめ」て扱える */ p3 = p1; /* 新しい型の値は、関数の引数にそのまま利用し、 */ /* 関数の値にも指定できる */ printf ( "p3 = " ); print_point ( p3 ); printf ( "\n" ); p2 = shift_point ( p1, 10.0, 100.0 ); /* 平行移動した結果の値を代入 */ /* p1 を指定しているが、値がコピーされ p1 自身は影響を受けない */ printf ( "p1 " ); print_point ( p1 ); printf ( " を x 軸方向に 10, y 軸方向に 100 平行移動した p2 = " ); print_point ( p2 ); printf ( "\n" ); return 0; }
/* 分数(有理数)の実装 (Q = m/n : m が分子, n が分母 : 整数 (n>0)) */ #include <stdio.h> typedef struct { int m; /* 分子 */ int n; /* 分母 */ } Q; /* 二つ整数の組 ( m,n ) が、有理数 q の分母分子になるには、 m,n が素で、n>0 という条件がある 任意の整数の組 m,n に対して、 q=m/n=m'/n' の形で、m',n' を求める 関数(約分する)を考える # 与えれたデータを適切な形に整える作業を「正規化」と呼ぶ # 有理数においては、約分する事が「正規化」になっている */ int gcm ( int m, int n ) { int r; if ( m < 0 ) { m = -m; } if ( n < 0 ) { n = -n; } while ( n > 0 ) { /* ユークリッドの互除法 */ r = m % n; m = n; n = r; } return m; } Q nomarize_q ( int m, int n ) { int g; /* m と n の最大公約数 */ Q result; if ( n < 0 ) { m = -m; n = -n; } g = gcm ( m, n ); m /= g; /* m = m / g */ n /= g; result.m = m; result.n = n; return result; } /* q1 * q2 = (q1.m/q1.n) * (q2.m/q2.n) = (q1.m * q2.m)/(q1.m * q2.n) */ Q mul_q ( Q q1, Q q2 ) { return nomarize_q ( q1.m * q2.m, q1.n * q2.n ); } /* q1 + q2 = (q1.m/q1.n) + (q2.m/q2.n) = (q1.m*q2.n)/(q1.n*q2.n) + (q2.m*q1.n)/(q2.n*q1.n) = (q1.m * q2.n + q2.m * q1.n)/(q1.m * q2.n) */ Q add_q ( Q q1, Q q2 ) { return nomarize_q ( q1.m * q2.n + q2.m * q1.n, q1.n * q2.n ); } void print_q ( Q q ) { printf ( "%d/%d", q.m, q.n ); } int main(int argc, char *argv[]) { Q q1; Q q2; q1.m = 9; q1.n = 10; printf ( "q1 = " ); print_q ( q1 ); printf ( "\n" ); q2 = nomarize_q ( 6, -36 ); /* q2.m = 6; q2.n = -36; */ printf ( "q2 = " ); print_q ( q2 ); printf ( "\n" ); printf ( "q1 * q2 = " ); print_q ( mul_q ( q1, q2 ) ); printf ( "\n" ); printf ( "q1 + q2 = " ); print_q ( add_q ( q1, q2 ) ); printf ( "\n" ); return 0; }
#include <stdio.h> typedef struct { int x; double y; char z; } Foo; int main(int argc, char *argv[] ) { Foo foo; int a[10]; /* ??????v?f?? 10 ?????z?? a ??? */ /* ??X??v?f?? a[0] ?` a[9] ??Q???\ */ int i; foo.x = 1; /* ?u?\????v????????A?u????v?????p????? */ foo.y = 2.0; foo.z = '3'; /* ????\?????????f?[?^??????? */ /* ?????????????g????????? */ /* ??????????A????????\????????A ???????????A??????????s???鎖??? */ /* ?u?z??v????????A?u?J?????v?????p????? */ a[0] = 0; /* ?v?f?? 10 ????????? 10 ?s?????????????? */ a[1] = 10; a[2] = 20; /* ... */ a[9] = 90; /* ???????Ai ????v?f??? 10 * i ??l?????????? ?K????????????.. ?J?????????????? */ for ( i = 0; i < 10; i++ ) { a[i] = i * 10; } /* ?Y????(?u???P?b?g??????)?????????g???? */ for ( i = 0; i < 10; i++ ) { a[9-i] = i * 10; /* ???t????l???鎖???? */ } return 0; }
#include <stdio.h> int main(int argc, char *argv[]) { printf ( "%c\n", *"abc" ); /* 文字列の前に「*」を付けると、先頭の文字が取り出せる */ printf ( "%s\n", "abc"+1 ); /* 文字列に整数値を加えると、その数だけ先頭文字が欠ける */ /* 特に +1 すると、先頭文字がける */ printf ( "%c\n", *("abc"+1) ); /* *("abc"+1) => *("bc") => 'b' */ /* 配列では、「配列名[添え字]」=「*(配列名+添え字)」 文字列では、「文字列[添え字]」=「*(文字列+添え字)」 */ printf ( "%c\n", "abc"[1] ); /* *("abc"+1) => *("bc") => 'b' */ /* 実は、「文字列」は、「char 型配列(の形で実装されている)」だという事が判明 */ return 0; }
#include <stdio.h> int main(int argc, char *argv[]) { char string[100]; /* 100 個数の文字(コード)が入れられる配列 */ string[0] = 'a'; string[1] = 'b'; string[2] = 'c'; string[3] = '\0'; /* '\0' は EOS (End Of String) を表すコード */ /* 文字列 "abc" は、 'a', 'b', 'c', '\0' だった */ /* string[0-3] は、"abc" と同じ状態になっている */ /* => 文字列 "abc" と同じ扱いができる */ printf ( "%s\n", string ); /* string[0-4] のような書き方は「しない(できない)」*/ string[1] = '\0'; /* s[0], s[1], s[2], s[3], s[4], .. 代入前 'a', 'b', 'c', '\0', ???,... => "abc" 代入後 'a', '\0', 'c', '\0', ???,... => */ printf ( "%s\n", string ); /* "a" */ printf ( "%s\n", string + 2 ); /* もし string <-> "abc" */ /* string + 2 <-> "abc" + 2 */ /* <-> "c" */ /* char string[4]; +-string+----+ | +-[0]-----+| | | 'a' || | +---------+| | +-[1]-----+| | | '\0' || | +---------+| | +-[2]-----+| | | 'c' || | +---------+| | +-[3]-----+| | | '\0' || | +---------+| +------------+ string <-> 'a', '\0', 'c', '\0' printf ( "%s", string ) -> "a" printf ( "%s", string + 2 ) -> "c" +------------+ データのある場所 | +-[0]-----+| string --> | | 'a' || | | +---------+| | | +-[1]-----+| | +2 | | '\0' || | | +---------+| v | +-[2]-----+| string + 2 -> | | 'c' || | +---------+| | +-[3]-----+| | | '\0' || | +---------+| +------------+ "abc" ---> 'a' 'b' "abc"+2 ---> 'c' '\0' */ return 0; }
#include <stdio.h> void myprint ( char *str ) { /* 文字列を引数してもつ */ int i; for ( i = 0; str[i] != '\0'; i++ ) { printf ( "%c", str[i] ); /* str[i] は文字列 str の i 番目の要素 */ } } int main(int argc, char *argv[] ) { char s[10]; myprint ( "abc\n" ); myprint ( "123456789\n" ); s[0] = 'X'; s[1] = 'Y'; s[2] = '\0'; myprint ( s ); /* 配列の名前 <-> 文字列と同様に扱える事がわかる */ return 0; }
[前回] データ構造 [背景] 目的 => プログラムを作成 そのプログラムを実行する事が、 何等かの形で、現実と対応して、現実に影響を与える 例 : プログラム上の単なる足し算 <-> 現実の銀行口座の振り込み 現実の情報と、コンピュータ内のデータ(数値)の対応関係が重要になる 現実の世界 : 複雑 例 : 平面上の点 ( 一つの数値ではなく、二つの数値で表現する事が、「自然」 ) コンピュータ(C 言語のあらかじめ与えられている数値の形式) : 有限個 ( char, int, double ) 対応関係を作るには、なんらかの工夫 例 : C 言語では「二つの数値を組み合わせる」という工夫をする => あらかじめ用意されているデータ型に対し、それとは異なる新しい型を導入できる typedef : 新しい型を C 言語に導入する仕組み 組み合わせ : struct (構造体) を使う 新しい型が導入されても、それは、あくまでも「形式」だけ <= これに、「意味」を付加する必要がある # いろいろな人が、自分で新しいデータ型をつくって、 # 独自の意味付けを行っている # => それを他の人(3 カ月後の自分..)に利用できるようするには # その「意味付け(新しいデータ型の形式と、それを操作する関数):API」 # を、資料(Document 化)しておく必要がある p-002.c : 有理数型を作った 形式 ( 分母と分子の対 ) だけ、決めても、「有理数」としてはふるまわない それを正規化したり、計算機能を追加して初めて、「有理数型」として「意味」を持つ [今日の話] 構造体 : いろいろな型を有限個数組み合わせて、新しい型を作る仕組み 配列 : 同じものを不特定多数組み合わせて、新しい型(?)を作る仕組み !!! 配列の要素の参照には「(計算)式」が使える => すごく便利 構文 配列宣言 : 型名 配列名[配列のサイズ]; cf. int a[10]; /* 整数の要素を 10 個もつ配列 a を宣言 */ /* 個々の要素は a[0] ? a[9] で参照可能 */ 意味: 配列名 [ 添え字 ]; 添え字の部分には(普通は非負の)整数値が与えられると、 その整数番目の要素が操作できる 配列の利用 要素の参照の時に、 配列名[添え字] <-> *(配列名 + 添え字) さらに、「添え字」が 0 の時は、 配列名[0] = *(配列名 + 0) = *(配列名) = *配列名 !!! 前期に、何かの前に「*」を付ける 配列の文字列の関係 C 言語では、「文字列」は、「char 型配列」の形で実現されている [] を利用して、要素 (char 型 : 文字)が取り出せる * は、その [] の特別な記法 ( "abc"[0] <-> *("abc"+0) <-> *("abc") <-> *"abc" +1 することは、配列の先頭の要素の場所を指している値を一つ増やしている => この結果、その値は、配列の次の要素を指す事になり、結果として、 先頭の文字が欠けた文字列と同じものを表す事になる # データのある場所を指す値 : ポインター値と呼ぶ 「配列名」は、配列の要素の先頭の要素のある場所を値としてもっている # 「配列名」は先頭の要素を指すポインター値 # +1 すると、その指す値は、配列の次の要素を指す事になる
課題プログラム内の「/*名前:ここ*/」の部分を書き換え「/*この部分を完成させなさい*/」の部分にプログラムを追加して、プログラムを完成させます。
Download : 20191115-01.c
/* * 課題 20191115-01 * * 20191115 20191115-01-QQQQ.c * * 複素数型の四則 */ #include <stdio.h> /* * 複素数型の定義と計算 * * 利用方法 * コンパイル * cc -Ic:\usr\c\include -o BASENAME.exe 20191115-01-QQQQ.c * 実行 * BASENAME */ #include <stdio.h> /* * 複素数型を表す Complex の定義 * * 複素数 z は、二つの実数 x, y を用いて * z = x + yi ( i は虚数単位 ) * と表現できる。 * C 言語では実数を表すのに浮動小数点数型の double を用いる * 型名 ( Complex ) を大文字で始めるのは「ソフトウェア概論ルール」 */ typedef struct { double real; /* 実部 */ double imaginary; /* 虚部 */ } Complex; /* 複素数型 */ /* * Complex make_Complex ( double x, double y ) * Complex 型の数を作り、返す * x, y -> z = x + yi */ Complex make_Complex ( double x, double y ) { Complex newComplex; /* 新しく作られる複素数 */ newComplex.real = x; /* 新しく作られた複素数の実部は x */ newComplex.imaginary = y; /* 新しく作られた複素数の実部は y */ return newComplex; /* 新しく作られる複素数を値として返す */ } /* * double real_part ( Complex z ) * Complex 型の数の実部を返す * z = x + yi -> x */ double real_part ( Complex z ) { return z.real; } /* * double imaginary_part ( Complex z ) * Complex 型の数の実部を返す * z = x + yi -> x */ double imaginary_part ( Complex z ) { return z.imaginary; } /* * print_Complex ( Complex z ) * Complex 型の数の出力 * z = x + y i だが、y < 0 の時は z = x - (-y) i となるように工夫 */ void print_Complex ( Complex z ) { if ( z.imaginary > 0.0 ) { printf ( "%f + %f i", z.real, z.imaginary ); } else { printf ( "%f - %f i", z.real, - z.imaginary ); } } /* * Complex add_Complex ( Complex z1, Complex z2 ) * Complex 型の数の足し算 * z1 = x1 + y1 i * z2 = x2 + y2 i * -> z1 + z2 = ( x1 + x2 ) + ( y1 + y2 ) i */ Complex add_Complex ( Complex z1, Complex z2 ) { Complex result; /* 複素数の和の実部は、実部の和 */ result.real = z1.real + z2.real; /* 複素数の和の虚部は、虚部の和 */ result.imaginary = z1.imaginary + z2.imaginary; return result; } /* * Complex sub_Complex ( Complex z1, Complex z2 ) * Complex 型の数の引き算 * z1 = x1 + y1 i * z2 = x2 + y2 i * -> z1 - z2 = ( x1 - x2 ) + ( y1 - y2 ) i */ Complex sub_Complex ( Complex z1, Complex z2 ) { Complex result; /* 複素数の差の実部は、実部の差 */ result.real = z1.real - z2.real; /* 複素数の差の虚部は、虚部の差 */ /* ** この部分を完成させなさい */ return result; } /* * Complex mul_Complex ( Complex z1, Complex z2 ) * Complex 型の数のかけ算 * z1 = x1 + y1 i * z2 = x2 + y2 i * の時 * z1 * z2 = (x1 * x2 - y1 * y2) + (x1 * y2 + x2 * y1) i */ Complex mul_Complex ( Complex z1, Complex z2 ) { Complex result; result.real = z1.real * z2.real - z1.imaginary * z2.imaginary; /* ** この部分を完成させなさい */ return result; } /* * Complex div_Complex ( Complex z1, Complex z2 ) * Complex 型の数の割り算 * z1 = x1 + y1 i * z2 = x2 + y2 i * の時 * z1 / z2 = ( x1 + y1 i ) / ( x2 + y2 i ) * = ( x1 + y1 i )( x2 - y2 i ) / ( x2 + y2 i )( x2 - y2 i ) * = ( x1 * x2 + y1 * y2) / ( x2^2 + y2^2 ) * + ( (- x1 * y2 + x2 * y1) / ( x2^2 + y2^2 ) ) i */ Complex div_Complex ( Complex z1, Complex z2 ) { Complex result; double denominator = z2.real * z2.real + z2.imaginary *z2.imaginary; /* 実部、虚部の割る数 |z2|^2 を予め計算しておく */ /* ** この部分を完成させなさい */ result.imaginary = ( - z1.real * z2.imaginary + z1.imaginary * z2.real ) / denominator; return result; } /* * print_result 演算結果を出力する */ void print_result ( Complex z1, Complex z2, char *operator, Complex z ) { print_Complex ( z1 ); printf ( " と、 " ); print_Complex ( z2 ); printf ( " との、%s は ", operator ); print_Complex ( z ); printf ( " です。\n" ); } /* * main */ int main( int argc, char *argv[] ) { Complex z1 = make_Complex ( 20.0, -15.0 ); /* z1 = 20 - 15i */ Complex z2 = make_Complex ( 1.0, 2.0 ); /* z2 = 1 + 2i */ /* 和の出力 */ print_result ( z1, z2, "和", add_Complex ( z1, z2 ) ); /* 差の出力 */ /* ** この部分を完成させなさい */ /* 積の出力 */ /* ** この部分を完成させなさい */ /* 商の出力 */ print_result ( z1, z2, "商", div_Complex ( z1, z2 ) ); return 0; }
$ ./20191115-01-QQQQ.exe 20.000000 - 15.000000 i と、 1.000000 + 2.000000 i との、和 は 21.000000 - 13.000000 i です。 20.000000 - 15.000000 i と、 1.000000 + 2.000000 i との、差 は 19.000000 - 17.000000 i です。 20.000000 - 15.000000 i と、 1.000000 + 2.000000 i との、積 は 50.000000 + 25.000000 i です。 20.000000 - 15.000000 i と、 1.000000 + 2.000000 i との、商 は -2.000000 - 11.000000 i です。 $
Download : 20191115-02.c
/* * 課題 20191115-02 * * 20191115 20191115-02-QQQQ.c * * 二次元行列型の定義と計算 */ #include <stdio.h> /* * */ #define DIMENSION 2 /* 二次元 */ /* * 行列 A は、2 x 2 = 4 の要素をもっている * * A = ( 1 2 ) = ( a[0][0] a[0][1] ) * 3 4 a[1][0] a[1][1] * */ typedef struct { double a[DIMENSION][DIMENSION]; /* 二次元の行列の要素は 2 x 2 */ } Matrix2D; /* Matrix2D 型の宣言 */ /* * Matrix2D make_Matrix2D ( double a, double b, double c, double d ) * 「行列」を作成する * * A = ( a b ) = ( a[0][0], a[0][1] ) * ( c d ) ( a[1][0], a[1][1] ) */ Matrix2D make_Matrix2D ( double a, double b, double c, double d ) { Matrix2D newMatrix2D; /* 新しい行列 */ newMatrix2D.a[0][0] = a; newMatrix2D.a[0][1] = b; newMatrix2D.a[1][0] = c; newMatrix2D.a[1][1] = d; return newMatrix2D; } /* * void print_Matrix2D ( Matrix2D ary ); * 「行列」を表示する (表示の都合上、常に独立した行に出力する) * Matrix2D ary; 二次元行列 */ void print_Matrix2D ( Matrix2D ary ) { int r; /* 行 ( row ) */ int c; /* 列 ( colomun ) */ for ( r = 0; r < DIMENSION; r++ ) { printf ( "(" ); for ( c = 0; c < DIMENSION; c++ ) { printf ( " %10.5f", ary.a[r][c] ); /* * [注意] %10.5f は %f と同じく浮動小数点数を出力するが * 「全体の桁数は 10 桁、小数点数以下は 5 桁にする」 * という「表示上の指定」も加わっている * 詳しくは google で「printf 書式」で検索 */ } printf ( " )\n" ); } } /* * Matrix2D add_Matrix2D ( Matrix2D a1, Matrix2D a2 ); * 「行列」の和 * * ( a b ) + ( e f ) = ( a + e b + f ) * ( c d ) ( g h ) ( c + g g + h ) */ Matrix2D add_Matrix2D ( Matrix2D a1, Matrix2D a2 ) { Matrix2D result; /* 計算結果 */ int r; /* 行 ( row ) */ int c; /* 列 ( colomun ) */ for ( r = 0; r < DIMENSION; r++ ) { for ( c = 0; c < DIMENSION; c++ ) { result.a[r][c] = a1.a[r][c] + a2.a[r][c]; } } return result; } /* * Matrix2D sub_Matrix2D ( Matrix2D a1, Matrix2D a2 ); * 「行列」の差 * * ( a b ) - ( e f ) = ( a - e b - f ) * ( c d ) ( g h ) ( c - g g - h ) */ Matrix2D sub_Matrix2D ( Matrix2D a1, Matrix2D a2 ) { Matrix2D result; /* 計算結果 */ int r; /* 行 ( row ) */ int c; /* 列 ( colomun ) */ for ( r = 0; r < DIMENSION; r++ ) { for ( c = 0; c < DIMENSION; c++ ) { /* ** この部分を完成させなさい */ } } return result; } /* * Matrix2D mul_Matrix2D ( Matrix2D a1, Matrix2D a2 ); * 「行列」の積 * * ( a b ) ( e f ) = ( a * e + b * g a * f + b * h ) * ( c d ) ( g h ) ( c * e + d * g c * f + d * h ) */ Matrix2D mul_Matrix2D ( Matrix2D a1, Matrix2D a2 ) { Matrix2D result; /* 計算結果 */ int r; /* 行 ( row ) */ int c; /* 列 ( colomun ) */ int i; for ( r = 0; r < DIMENSION; r++ ) { for ( c = 0; c < DIMENSION; c++ ) { double products = 0.0; /* a1 の r 行と a2 の c 列の内積の結果 */ /* a1 の r 行と a2 の c 列の内積を計算する */ /* ** この部分を完成させなさい */ result.a[r][c] = products; } } return result; } /* * print_result 演算結果を出力する */ void print_result ( Matrix2D a1, Matrix2D a2, char *operator, Matrix2D a ) { printf ( "%s の計算\n", operator ); print_Matrix2D ( a1 ); printf ( " と、 \n" ); print_Matrix2D ( a2 ); printf ( " との、%s は \n", operator ); print_Matrix2D ( a ); printf ( " です。\n\n" ); } /* * main */ int main( int argc, char *argv[] ) { /* a1 = ( 1 2 ) ( 3 -1 ) a2 = ( -3 1 ) ( 1 -2 ) */ Matrix2D a1 = make_Matrix2D ( 1.0, 2.0, 3.0, -1.0 ); Matrix2D a2 = make_Matrix2D ( -3.0, 1.0, 1.0, -2.0 ); /* 和の出力 */ print_result ( a1, a2, "和", add_Matrix2D ( a1, a2 ) ); /* 差の出力 */ /* ** この部分を完成させなさい */ /* 積の出力 */ /* ** この部分を完成させなさい */ return 0; }
123 987 456
$ ./20191115-02-QQQQ.exe 和 の計算 ( 1.00000 2.00000 ) ( 3.00000 -1.00000 ) と、 ( -3.00000 1.00000 ) ( 1.00000 -2.00000 ) との、和 は ( -2.00000 3.00000 ) ( 4.00000 -3.00000 ) です。 差 の計算 ( 1.00000 2.00000 ) ( 3.00000 -1.00000 ) と、 ( -3.00000 1.00000 ) ( 1.00000 -2.00000 ) との、差 は ( 4.00000 1.00000 ) ( 2.00000 1.00000 ) です。 積 の計算 ( 1.00000 2.00000 ) ( 3.00000 -1.00000 ) と、 ( -3.00000 1.00000 ) ( 1.00000 -2.00000 ) との、積 は ( -1.00000 -3.00000 ) ( -10.00000 5.00000 ) です。 $
Download : 20191115-03.c
/* * 課題 20191115-03 * * 20191115 20191115-03-QQQQ.c * * 整数型の配列を作り、それに 5 個のデータを入力し * その値を 5 倍にしたものと 2 分の 1 にした値を * それぞれ、画面に出力するプログラムを作りなさい * */ #include <stdio.h> /* * */ #define DATA_SIZE 5 /* データのサイズ (個数) */ /* * */ int main(int argc, char *argv[]) { int array[DATA_SIZE]; /* サイズが DATA_SIZE の整数型の配列 array の宣言 */ int i; /* 添字変数 i を宣言 */ for ( i = 0; i < DATA_SIZE; i++ ) { /* 配列 array に数値を読み込む */ /* プロンプト */ printf ( "%d 番目の整数値を入力してください : ", i + 1 ); /* 配列へのデータ入力 */ scanf ( "%d", &array[i] ); } /* 入力された個々の値を 5 倍した物を出力 */ for ( i = 0; i < DATA_SIZE; i++ ) { printf ( "%d\n", /* p:ここ */ ); /* 5 倍 */ } /* 入力された個々の値を 1/2 した物を出力 */ for ( i = 0; i < DATA_SIZE; i++ ) { printf ( "%d\n", /* q:ここ */ ); /* 2 分の 1 */ /* 整数割り算になるので、小数点以下は切り捨て */ } return 0; }
3 8 13 2 4
$ ./20191115-03-QQQQ.exe $