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 $