Download : sample-001.c ( SJIS 版 )
/* * 2014/12/05 sample-001.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; } /* * */
C:\usr\c>sample-001 整数値(%d) : 123, 文字(%c) : 'a', 文字列(%s) : "xyz", 浮動小数点数(%f) : \ 1.230000 C:\usr\c>
#include <stdio.h> int main(int ac, char *av[] ) { printf ( "sizeof ( char ) = %d\n", sizeof ( char ) ); printf ( "sizeof ( int ) = %d\n", sizeof ( int ) ); printf ( "sizeof ( double ) = %d\n", sizeof ( double ) ); return 0; }
#include <stdio.h> void print_char ( char ch ) { printf ( "ch = %d\n", ch ); } int main(int ac, char *av[] ) { print_char ( 'A' ); /* 'A' という文字を表す ASCII Code を指定 */ /* 'A' は、65 という(ちいさな)整数値になる */ /* 「小さい整数」とは「0 から 255 の 1 byte 表現できる整数」 */ /* あるいは -128 から 127 の範囲 (符号つきの場合) */ print_char ( 65 ); /* 実際に整数を指定しても、同じ */ print_char ( 1000 ); /* 小くなければ .. ? */ print_char ( 1000 % 256 ); /* 小くなければ .. => 余分な部分はきりすてられる */ printf ( "%d\n", 256 * 10 + 20 ); print_char ( 256 * 10 + 20 ); print_char ( 2580 ); /* 2580 = 256 * 10 + 20 */ /* 2580 / 256 = 10 .. 20 */ /* 余りの 20 しかでない */ /* 2580 = 256 * 10 + 20 +-----------+ | 20 | 0 〜 255 +-----------+ | 10 | 0 〜 255 +-----------+ -> 256 * 10 + 20 ( 256 進数法での表現 ) セルが一つ 0 〜 255 = 256 - 1 ( 256 = 2^8 通り ) セルが二つ 0 〜 655xx ( 2^16 通り ) セルが四つ 0 〜 2^32-1 ( 2^32 通り ) -> int の表現できる場合のかず - 2^(32-1) 〜 2^(32-1) - 1 */ return 0; }
#include <stdio.h> void print_char ( char ch ) { printf ( "ch = %d\n", ch ); } int main(int ac, char *av[] ) { int i; char ch; ch = 0; for ( i = 0; i < 1000; i++ ) { /* i を 0 から 999 まで 1000 回実行 */ printf ( "%d : ", i ); /* +-----------+ i +-- | 0 | 0 = 0 + 0 * 256^1 | +-----------+ + 0 * 256^2 +-- | 0 | + 0 * 256^3 | +-----------+ +-- | 0 | | +-----------+ +-- | 0 | +-----------+ +-----------+ ch -- | 0 | 256 = 0 + 0 * 256^1 +-----------+ + 1 * 256^2 | 1 | + 0 * 256^3 +-----------+ | 0 | +-----------+ | 0 | +-----------+ */ /* 127 : ch = 127 128 : ch = -128 セル char int 128 -128 -128 129 -127 -127 -2 -2 255 -1 -1 0 0 0 1 1 1 .. .. 127 127 127 */ print_char ( ch ); ch = ch + 1; /* ch を 1 ずつ増やす */ /* 最初は 0 : 0, 1 : 1, ... */ } return 0; }
#include <stdio.h> void print_int ( int ch ) { printf ( "ch = %d\n", ch ); } int main(int ac, char *av[] ) { print_int ( 'A' ); print_int ( 65 ); /* 実際に整数を指定しても、同じ */ print_int ( 1000 ); /* 小くなければ .. ? */ print_int ( 2580 ); /* 2580 = 256 * 10 + 20 */ return 0; }
/* */ #include <stdio.h> typedef struct { char bytes[sizeof(int)]; /* sizeof int = 4 */ } Int; void print_int ( Int ch ) { int i; for ( i = 0; i < sizeof ( int ); i++ ) { printf ( "%d\n", ch.bytes[i] ); } }
#include <stdio.h> int main(int ac, char *av[] ) { print_int ( 'A' ); print_int ( 65 ); /* 実際に整数を指定しても、同じ */ /* +-----------+ 65 | 65 | +-----------+ | 0 | +-----------+ | 0 | +-----------+ | 0 | +-----------+ */ print_int ( 1000 ); /* 小くなければ .. ? */ print_int ( 2580 ); /* 2580 = 256 * 10 + 20 */ /* +-----------+ 2580 | 20 | +-----------+ | 10 | +-----------+ | 0 | +-----------+ | 0 | +-----------+ */ /* int 型の変数は、sizeof ( int ) のサイズのセルを占有する */ return 0; }
#include <stdio.h> int main(int ac, char *av[] ) { int ivar; char cvar; /* & は、変数の前につけて、その変数のアドレス(ポインター)値を取る */ printf ( "&ivar = %x\n", &ivar ); /* ivar のアドレス値 */ printf ( "&cvar = %x\n", &cvar ); /* cvar のアドレス値 */ return 0; }
#include <stdio.h> void set_char_var ( char *p ) { /* アドレス値(ポインター値)を利用して、変数の値が変更できる */ *p = 1; /* 引数でしていされた char 型変数へのポインターを使って */ /* その変数の値を書き換える */ } void set_int_var ( int *p ) { *p = 1; /* 引数でしていされた int 型変数へのポインターを使って */ /* その変数の値を書き換える */ } int main(int ac, char *av[] ) { int ivar = 2; char cvar = 2; /* char 型変数の場合 (先週) */ printf ( "前 : cvar = %d\n", cvar ); set_char_var ( &cvar ); printf ( "後 : cvar = %d\n", cvar ); /* int 型変数の場合 (今週) */ printf ( "前 : ivar = %d\n", ivar ); set_int_var ( &ivar ); printf ( "後 : ivar = %d\n", ivar ); return 0; }
#include <stdio.h> void print_char_array ( char ch[3] ) { int i; for ( i = 0; i < 3; i++ ) { printf ( "ch[%d] = %d\n", i, ch[i] ); } } int main(int ac, char *av[] ) { char cArray[3] = { 1, 2, 3 }; /* +-----------+ | 1 | cArray[0] +-----------+ | 2 | cArray[1] +-----------+ | 3 | cArray[2] +-----------+ */ print_char_array ( cArray ); return 0; }
#include <stdio.h> void print_char_array_address ( char ch[3] ) { int i; printf ( "関数 : ch = %x\n", ch ); for ( i = 0; i < 3; i++ ) { printf ( "&ch[%d] = %x\n", i, &ch[i] ); /* アドレスの表示 */ } /* 配列名は、その配列の先頭の要素のアドレス(ポインター値)をもっている => 普通、 関数には、実引数に変数名を指定して、 関数内で、仮引数変数の値を書き換えても、元には影響しない 一方、 関数には、実引数に配列名を指定して、 関数内で、仮引数変数を利用して、配列の要素の値を書き換えると 元の配列の要素の値が書き変わる */ } int main(int ac, char *av[] ) { char cArray[3] = { 1, 2, 3 }; /* +-----------+ | 1 | cArray[0] +-----------+ | 2 | cArray[1] +-----------+ | 3 | cArray[2] +-----------+ */ printf ( "main : ch = %x\n", cArray ); print_char_array_address ( cArray ); return 0; }
#include <stdio.h> void print_int_array_address ( int in[3] ) { int i; printf ( "関数 : in = %x\n", in ); for ( i = 0; i < 3; i++ ) { printf ( "&in[%d] = %x\n", i, &in[i] ); /* アドレスの表示 */ } } int main(int ac, int *av[] ) { int iArray[3] = { 1, 2, 3 }; /* +-----------+ bfd9e7e4 | 1 | iArray[0] +-----------+ | 0 | +-----------+ | 0 | +-----------+ | 0 | +-----------+ bfd9e7e4+4 | 2 | iArray[1] =bfd9e7e8 +-----------+ &iArray[1] の値は &iArray[0] より sizeof (int) だけ大 | 0 | +-----------+ | 0 | +-----------+ | 0 | +-----------+ | 3 | iArray[2] +-----------+ | 0 | +-----------+ | 0 | +-----------+ | 0 | +-----------+ */ printf ( "main : in = %x\n", iArray ); print_int_array_address ( iArray ); return 0; }
#include <stdio.h> void print_int_array_address ( int in[3] ) { int i; printf ( "関数 : in = %x\n", in ); printf ( "in + 1 = %x\n", in + 1 ); printf ( "&in[1] = %x\n", &in[1] ); /* &in[1] = in + 1 = &in[0] + 1 */ } int main(int ac, int *av[] ) { int iArray[3] = { 1, 2, 3 }; /* +-----------+ bfd9e7e4 | 1 | iArray[0] +-----------+ | 0 | +-----------+ | 0 | +-----------+ | 0 | +-----------+ bfd9e7e4+4 | 2 | iArray[1] =bfd9e7e8 +-----------+ &iArray[1] の値は &iArray[0] より sizeof (int) だけ大 | 0 | +-----------+ | 0 | +-----------+ | 0 | +-----------+ | 3 | iArray[2] +-----------+ | 0 | +-----------+ | 0 | +-----------+ | 0 | +-----------+ */ printf ( "main : in = %x\n", iArray ); print_int_array_address ( iArray ); return 0; }
/* * 20141212-01-QQQQ.c * ポインターを利用して、整数変数の値を正値にする */ #include <stdio.h> /* * to_positive_int_variable ( int *iVarPtr ) * 指定された整数型の変数の値を正値になるようにする * int *iVarPtr : 整数型変数へのポインター値 */ void to_positive_int_variable ( int *iVarPtr ) { /* int *iVarPtr <=> int iVarPtr[1] */ /* iVarPtr[0] を操作できる iVarPtr は配列と同じだから */ if ( *iVarPtr < 0 ) { /* もし、整数型変数の値が負ならば.. */ /* その値の符号を変更して、元の変数に代入する */ /* *iVarPtr ( = iVarPtr[0] ) の値を変更する必要がある */ *iVarPtr = - *iVarPtr; /* この結果 (当然) *iVarPtr の値が変化 */ /* するが、これは、関数の呼び元の値も変る */ } /* そうでなければ、何もしない.. */ } /* * */ int main ( int argc, char *argv[] ) { int pVar = 5; /* 正の値を持つ整数型変数 */ int nVar = -2; /* 負の値を持つ整数型変数 */ int zVar = 0; /* 零の値を持つ整数型変数 */ /* pVar */ printf ( "前 : pVar = %d\n", pVar ); to_positive_int_variable ( &pVar ); /* 引数はポインター値を指定する */ /* 引数には、値を変更させたい変数の「ポインター値」を指定するする必要が あるので「&pVar」とする必要がある 引数でわたしているのは pVar の「ポインター値」 関数は、その「ポインター値」を利用して pVar を間接的に操作する */ printf ( "後 : pVar = %d\n", pVar ); /* nVar */ printf ( "前 : nVar = %d\n", nVar ); to_positive_int_variable ( &nVar ); /* ^この「&」が重要 */ printf ( "後 : nVar = %d\n", nVar ); /* zVar */ printf ( "前 : zVar = %d\n", zVar ); /* ** この部分を完成させなさい */ printf ( "後 : zVar = %d\n", zVar ); return 0; } /* * */
/* 構造体とポインターの関係 */ #include <stdio.h> typedef struct { int x; int y; } Point2D; /* * void to_origin ( Point2D *pPtr ); * 引数で指定された Point2D 型の変数のポインター値を利用して * その変数の値を、原点 ( 0, 0 ) に移す関数 */ void to_origin ( Point2D *pPtr ) { (*pPtr).x = 0; /* x 座標を 0 にする */ (*pPtr).y = 0; /* y 座標を 0 にする */ /* 注意 : *pPtr.x は *(pPtr.x) と同じで、これは (*pPtr).x と異る */ /* main 側で &pa を渡すので、ここでは (*(&pa)).x = 0 (*(&pa)).y = 0 とおなじで、これは pa.x = 0 pa.y = 0 とおなじ、すなわち、元 main の変数の値を (0,0) した */ } void print_point_2d ( Point2D point ) { /* Point2D の値がわかればよいので * は不要 */ /* ※ * と & をつかっても良い (may) */ printf ( "(%d, %d)", point.x, point.y ); } int main(int ac, char *av[] ) { Point2D pa = { 1, 2 }; /* pa = (1,2) にする */ printf ( "pa = " ); print_point_2d ( pa ); /* ここでは値をわたしている */ /* & はいらない */ printf ( "です\n" ); printf ( "これを原点に移動させると\n" ); to_origin ( &pa ); /* 変数の値を変更するのでポインターを渡す */ /* & を利用 */ printf ( "pa = " ); print_point_2d ( pa ); /* ここでは値をわたしている */ /* & はいらない */ printf ( "となります\n" ); return 0; }
/* 構造体とポインターの関係(2) */ #include <stdio.h> typedef struct { int x; int y; } Point2D; /* * void to_origin ( Point2D *pPtr ); * 引数で指定された Point2D 型の変数のポインター値を利用して * その変数の値を、原点 ( 0, 0 ) に移す関数 */ void to_origin ( Point2D *pPtr ) { pPtr -> x = 0; /* x 座標を 0 にする */ pPtr -> y = 0; /* y 座標を 0 にする */ /* 注意 : 「pPtr -> x」 は 「(*pPtr).x 」と同じ */ /* main 側で &pa を渡すので、ここでは (*(&pa)).x = 0 (*(&pa)).y = 0 とおなじで、これは pa.x = 0 pa.y = 0 とおなじ、すなわち、元 main の変数の値を (0,0) した */ } void print_point_2d ( Point2D point ) { /* Point2D の値がわかればよいので * は不要 */ /* ※ * と & をつかっても良い (may) */ printf ( "(%d, %d)", point.x, point.y ); } int main(int ac, char *av[] ) { Point2D pa = { 1, 2 }; /* pa = (1,2) にする */ printf ( "pa = " ); print_point_2d ( pa ); /* ここでは値をわたしている */ /* & はいらない */ printf ( "です\n" ); printf ( "これを原点に移動させると\n" ); to_origin ( &pa ); /* 変数の値を変更するのでポインターを渡す */ /* & を利用 */ printf ( "pa = " ); print_point_2d ( pa ); /* ここでは値をわたしている */ /* & はいらない */ printf ( "となります\n" ); return 0; }
/* * 20141212-02-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 型の変数へのポインターの値 * * * Y ^ * | * | * | P(x,y) <- 元の点 * | | * ----+-----+-----> X * | | * | P'(x,-y) <- X 軸に対称点( y 座標の符号反転する) * | * | * */ void move_to_x_axis_symmetry ( Point2D *p2dPtr ) { /* x 軸に対称なので、y 座標の符号だけを変更 */ p2dPtr -> y = - p2dPtr -> y; /* y 座標の符号を逆転 */ /* その点の y 座標 ( (*p2dPtr).y == p2dPtr -> 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 ); /* 引数は初めからポインター値 */ /* '&' はいらない (すでにポインタ値になっている) */ move_to_y_axis_symmetry ( p2dPtr ); /* 今度は y 座標 */ /* 同様に '&' はいらない (すでにポインタ値になっている) */ } /* * print_point2d ( Point2D p2dVar ) * 指定された Point2D 型の値を表示する * Point2D p2dVar; Point2D 型の値 */ void print_point2d ( Point2D p2dVar ) { /* x, y 座標をそれぞれ出力するだけ */ printf ( "(%d, %d)", p2dVar.x, p2dVar.y ); } /* ポインター版もつくれる */ void print_point2d_by_pointer ( 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 ); */ print_point2d_by_pointer ( &Pa ); printf ( "になります\n" ); printf ( "Pb = " ); print_point2d ( Pb ); printf ( "\nを、原点対称な位置に移動すると.." ); move_to_origin_symmetry ( /*ここ*/ ); /* Pb を利用した何か */ print_point2d ( Pb ); printf ( "になります\n" ); return 0; } /* * */
#include <stdio.h> typedef struct { char bytes[sizeof(int)]; } Int; void print_low_byte ( Int v ) { /* 引数は Int 型 sturct { int bytes[4] } */ /* +-----------+ +-----------+ int | 20 | Int.bytes[0] | 20 | +-----------+ +-----------+ | 10 | Int.bytes[1] | 10 | +-----------+ +-----------+ | 0 | Int.bytes[2] | 0 | +-----------+ +-----------+ | 0 | Int.bytes[3] | 0 | +-----------+ +-----------+ */ printf ( "low byte = %d\n", v.bytes[0] ); }
#include <stdio.h> int main(int ac, char *av[] ) { print_low_byte ( 10 * 256 + 20 ); /* 引数は int 型 */ /* +-----------+ int | 20 | = 10 * 256 + 20 +-----------+ | 10 | +-----------+ | 0 | +-----------+ | 0 | +-----------+ */ return 0; } /* 変数 ( メモリのセル集まり.. ) は、それ自身は、型情報をもっていない それをどようにつかうかは、その内容を「どの型として利用するか」できまる */
#include <stdio.h> #include <stdio.h> typedef struct { char bytes[sizeof(int)]; } Int; void print_low_byte ( Int v ) { /* 引数は Int 型 sturct { int bytes[4] } */ /* +-----------+ +-----------+ int | 20 | Int.bytes[0] | 20 | +-----------+ +-----------+ | 10 | Int.bytes[1] | 10 | +-----------+ +-----------+ | 0 | Int.bytes[2] | 0 | +-----------+ +-----------+ | 0 | Int.bytes[3] | 0 | +-----------+ +-----------+ */ printf ( "low byte = %d\n", v.bytes[0] ); } int main(int ac, char *av[] ) { print_low_byte ( 10 * 256 + 20 ); /* 引数は int 型 */ /* +-----------+ int | 20 | = 10 * 256 + 20 +-----------+ | 10 | +-----------+ | 0 | +-----------+ | 0 | +-----------+ */ return 0; } /* 変数 ( メモリのセル集まり.. ) は、それ自身は、型情報をもっていない それをどようにつかうかは、その内容を「どの型として利用するか」できまる */
#include <stdio.h> #include <stdio.h> void print_low_byte_with_pointer ( int *v ) { /* 引数は (int *) 型 */ printf ( "value = %d\n", *v ); /* *(&iVar) == iVar = 10 * 256 + 20 */ } int main(int ac, char *av[] ) { int iVar = 10 * 256 + 20; print_low_byte_with_pointer ( &iVar ); /* 引数は (int*) 型 */ return 0; } /* 変数 ( メモリのセル集まり.. ) は、それ自身は、型情報をもっていない それをどようにつかうかは、その内容を「どの型として利用するか」できまる */
#include <stdio.h> #include <stdio.h> void print_low_byte_with_pointer ( int *v ) { /* 引数は (int *) 型 */ printf ( "value = %d\n", *(char *)v ); /* +-----------+ iVar| 20 | ← v は、ココのアドレス + int 型という情報 +-----------+ もし、単に *v とすれば、4 byte 分を利用する | 10 | ところが、(char *) 型であれば 1 byte 分しか +-----------+ 利用しない | 0 | +-----------+ | 0 | +-----------+ */ /* キャスト v は int * 型の値 ( int へのポインター型 ) だが、 それをむりやり char * 型 ( char へのポインター型 ) に強制できる */ } int main(int ac, char *av[] ) { int iVar = 10 * 256 + 20; /* +-----------+ iVar| 20 | ← &iVar は、ココのアドレス + int 型という情報 +-----------+ | 10 | +-----------+ | 0 | +-----------+ | 0 | +-----------+ */ print_low_byte_with_pointer ( &iVar ); /* 引数は (int*) 型 */ return 0; } /* 変数 ( メモリのセル集まり.. ) は、それ自身は、型情報をもっていない それをどようにつかうかは、その内容を「どの型として利用するか」できまる */
#include <stdio.h> /* 階乗の計算 */ int fac ( int n ) { if ( n <= 0 ) { return 1; } else { return fac ( n - 1 ) * n; } } int main(int ac, char *av[] ) { printf ( "4! = %d\n", fac ( 4 ) ); /* 4! = 4 * 3 * 2 * 1 = 24 fac ( 4 ) n ← 4; fac( n ) n ← 4; if ( n <= 0 ) { return 1; } else { return fac ( n - 1 ) * n; } if ( 4 <= 0 ) { return 1; } else { return fac ( 4 - 1 ) * 4; } return fac ( 4 - 1 ) * 4; return fac ( 3 ) * 4; 4 をかけるというはおいておいて .. fac(3 )は... */ return 0; }
#include <stdio.h> /* 階乗の計算 */ int fac ( int n ) { printf ( "*(%x) = %d\n", &n, n ); /* n のアドレス値を表示してみる */ if ( n <= 0 ) { return 1; } else { return fac ( n - 1 ) * n; } } int main(int ac, char *av[] ) { printf ( "4! = %d\n", fac ( 4 ) ); /* 4! = 4 * 3 * 2 * 1 = 24 fac ( 4 ) n ← 4; fac( n ) n ← 4; if ( n <= 0 ) { return 1; } else { return fac ( n - 1 ) * n; } if ( 4 <= 0 ) { return 1; } else { return fac ( 4 - 1 ) * 4; } return fac ( 4 - 1 ) * 4; return fac ( 3 ) * 4; 4 をかけるというはおいておいて .. fac(3 )は... */ return 0; }
sizeof ( 型名 ) 引数は、「型名」あるいは、「型」を表す表現 その型が、幾つのセルをしめるかを byte 数でしめしてくれる 前回は char 型変数 <-> メモリモデルの一つセル & をつける <-> セルのアドレスを取り出す & をつけたものに * をつける <-> セルそのものをさす ※ & をつけたものは、「値」なので、計算に利用したり、 関数に引数として渡す事や、関数の値として返す事ができる 今回は、int 型に関しても ( int が 4 つ分のセルを占有する以外は、 ) char 型と同じ 同様に、 double 型 / 構造体なども同じように説明できる == ポインター値 アドレスと型の二つの情報を持つ値 アドレス : メモリモデルにおける、番地(address)同じもの 型 : それがしめるセルサイズと、それが表す情報の操作方法 -> char, int, double, struct { int x; int y; }, etc.. と同じ !! ポインター値は、アドレス値 *だけ* をもっているわけではない !! アドレス値は、整数値として表示できるが、型情報は表示できない == 関数の引数 (など、変数) は、 スタックにとられている 「スタック」=「棚」 上に、どんどん、物を載せる事ができて、 必要ならば、上にあるものを下して使う使う事ができる ------------------ スタック (最初は空っぽ) +-------+ | 1 | <- 一つ物を載せた (push) +-------+ ------------------ スタック (内容が 1 つのもの) +-------+ | 4 | <- 沢山のっている +-------+ +-------+ | 3 | +-------+ +-------+ | 2 | +-------+ +-------+ | 1 | +-------+ ------------------ とりだすときは、最後にいれたものだけがとりだせる +-------+ | 4 | <- 取り出せるのは、さいごにつんだもの (pop) +-------+ +-------+ | 3 | +-------+ +-------+ | 2 | +-------+ +-------+ | 1 | +-------+ ------------------ fac(int n) { +----------+ n-> | 4 | +----------+ ------------ fac ( n - 1 ) = fac ( 4 - 1 ) = fac ( 3 ) +----------+ n-> | 3 | +----------+ +----------+ | 4 | +----------+ ------------ } main() { fac (4) ------------ } 1. 再帰呼出し メモリモデルで説明ができる 2. 引数が「スタック」とよばれる「メモリ」に保存されている -> printf の秘密につながる
Download : 20141212-01.c ( SJIS 版 )
/* * 20141212-01-QQQQ.c * ポインターを利用して、整数変数の値を正値にする */ #include <stdio.h> /* * to_positive_int_variable ( int *iVarPtr ) * 指定された整数型の変数の値を正値になるようにする * int *iVarPtr : 整数型変数へのポインター値 */ void to_positive_int_variable ( int *iVarPtr ) { if ( *iVarPtr < 0 ) { /* もし、整数型変数の値が負ならば.. */ /* その値の符号を変更して、元の変数に代入する */ /* ** この部分を完成させなさい */ } /* そうでなければ、何もしない.. */ } /* * */ int main ( int argc, char *argv[] ) { int pVar = 5; /* 正の値を持つ整数型変数 */ int nVar = -2; /* 負の値を持つ整数型変数 */ int zVar = 0; /* 零の値を持つ整数型変数 */ /* pVar */ printf ( "前 : pVar = %d\n", pVar ); to_positive_int_variable ( &pVar ); /* 引数はポインター値を指定する */ printf ( "後 : pVar = %d\n", pVar ); /* nVar */ printf ( "前 : nVar = %d\n", nVar ); /* ** この部分を完成させなさい */ printf ( "後 : nVar = %d\n", nVar ); /* zVar */ printf ( "前 : zVar = %d\n", zVar ); /* ** この部分を完成させなさい */ printf ( "後 : zVar = %d\n", zVar ); return 0; } /* * */
C:\usr\c\> 20141212-01-QQQQ 前 : pVar = 5 後 : pVar = 5 前 : nVar = -2 後 : nVar = 2 前 : zVar = 0 後 : zVar = 0 C:\usr\c\>
Download : 20141212-02.c ( SJIS 版 )
/* * 20141212-02-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; } /* * */
C:\usr\c\> 20141212-02-QQQQ Pa = (-1, 3) を、x 軸対称な位置に移動すると..(-1, -3)になります Pb = (2, 5) を、原点対称な位置に移動すると..(-2, -5)になります C:\usr\c\>
Download : 20141212-03.c ( SJIS 版 )
/* * 20141212-03-QQQQ.c * Point2D 型に対応した myprintf を拡張して作る */ #include <stdio.h> #include "s_print.h" #include "s_input.h" /* * */ typedef struct { int x; int y; } Point2D; /* * 複数の引数を書式指定して文字列の中に埋め込む */ void myprintf ( char *msg, ... ) { int i; char *pvalue = (char *)&msg + sizeof( char * ); Point2D pv; for ( i = 0; msg[i] != EOS; i++ ) { if ( msg[i] == '%' ) { /* 文字列の中に '%' があったら特別処理する */ i++; /* 次の文字をみる */ switch ( msg[i] ) { case 'd': /* 10 進数 */ s_print_int ( *((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 'D': /* Point2D 型 */ pv = *((Point2D *)pvalue); /* pv の値を (%d,%d) の形式で出力する */ /* ** この部分を完成させなさい */ /* 次のデータを処理するために pvalue 変更 */ /* ** この部分を完成させなさい */ break; case '%': /* '%' が重なったら.. */ s_print_char ( '%' ); /* '%' を出力 */ break; default: /* その他 : よくわからないので読み飛ばす.. */ break; } } else { /* そうでなけれ .. */ s_print_char ( msg[i] ); /* そのままその文字を出力 */ } } } /* * 色々な数値の出力 */ int main ( void ) { Point2D pv; pv.x = 2; pv.y = -3; /* * データの出力 (Output) */ myprintf ( "整数値(%%d) : %d, 文字(%%c) : '%c', 文字列(%%s) : \"%s\", 浮動小数点数(%%f) : %f, \ 点の座標 (%%D) : %D\n", 123, (int)'a', "xyz" , 1.23, pv ); /* * */ return 0; } /* * */
C:\usr\c\> 20141212-03-QQQQ 整数値(%d) : 123, 文字(%c) : 'a', 文字列(%s) : "xyz", 浮動小数点数(%f) : \ 1.230000, 点の座標 (%D) : (2,-3) C:\usr\c\>