Download : sample-006.c
/* * 2018/12/21 sample-006.c */ /* * メモリモデルの理解 (6) * * 利用方法 * コンパイル * cc -I ~/c/include -c sample-006.c * リンク * cc -o sample-006.exe sample-006.c * 実行 * ./sample-006.exe */ #include <stdio.h> #include "s_variable.h" /* memory モデルを理解するための関数定義 */ /* * main */ int main ( int argc, char *argv[] ) { /* * C 言語の文字列のメモリモデルによる理解 */ /* * 文字列はアドレスをもっている */ printf ( "文字列 \"abc\" のアドレスは 16 進数表現で %x です。\n", get_string_address( "abc" ) ); /* * 文字列の要素をアドレスを利用して参照 */ printf ( "文字列 \"abc\" の先頭の文字は %c です。\n", get_variable_value_at ( get_string_address( "abc" ) ) ); /* * 文字列の要素の二つ目以後を取り出す */ printf ( "文字列 \"abc\" の先頭の次の文字は %c です。\n", get_variable_value_at ( get_string_address( "abc" ) + 1 ) ); printf ( "文字列 \"abc\" の先頭の次の次の文字は %c です。\n", get_variable_value_at ( get_string_address( "abc" ) + 2 ) ); /* * */ return 0; } /* * */
$ ./sample-006.exe 文字列 "abc" のアドレスは 16 進数表現で 401480 です。 文字列 "abc" の先頭の文字は a です。 文字列 "abc" の先頭の次の文字は b です。 文字列 "abc" の先頭の次の次の文字は c です。 $
Download : sample-007.c
/* * 2018/12/21 sample-007.c */ /* * メモリモデルの理解 (7) * * 利用方法 * コンパイル * cc -I ~/c/include -c sample-007.c * リンク * cc -o sample-007.exe sample-007.c * 実行 * ./sample-007.exe */ #include <stdio.h> #include "s_variable.h" /* memory モデルを理解するための関数定義 */ /* * main */ int main ( int argc, char *argv[] ) { /* * C 言語の変数のメモリモデルによる理解 */ char cvar; /* char 型の変数 cvar の宣言 */ char dvar; /* char 型の変数 dvar の宣言 */ char evar; /* char 型の変数 evar の宣言 */ /* * 変数を並べてて宣言すると (偶然..) アドレスが連続していた.. */ printf ( "変数 cvar のアドレスは 16 進数表現で %x です。\n", get_variable_address( cvar ) ); printf ( "変数 dvar のアドレスは 16 進数表現で %x です。\n", get_variable_address( dvar ) ); printf ( "変数 evar のアドレスは 16 進数表現で %x です。\n", get_variable_address( evar ) ); /* * 変数をアドレスを利用して参照 */ cvar = 'c'; /* 変数 cvar に、値 'c' を代入 */ dvar = 'D'; /* 変数 dvar に、値 'D' を代入 */ evar = '\0'; /* 変数 evar に、値 '\0' を代入 */ printf ( "cvar の所から記録されている文字列は (%s) です。\n", get_variable_address( cvar ) ); /* * アドレス経由で、変数の内容を変更 */ set_variable_value_at ( get_variable_address( cvar ) + 1, 'x' ); /* 変数 cvar のアドレスの次のアドレスは dvar のアドレスなので.. */ printf ( "cvar に記録されている文字は %c です。\n", cvar ); /* 結果的に、dvar の内容が書き変わる */ printf ( "dvar に記録されている文字は %c です。\n", dvar ); /* * */ return 0; } /* * */
$ ./sample-007.exe 変数 cvar のアドレスは 16 進数表現で b766a30d です。 変数 dvar のアドレスは 16 進数表現で b766a30e です。 変数 evar のアドレスは 16 進数表現で b766a30f です。 cvar の所から記録されている文字列は (cD) です。 cvar に記録されている文字は c です。 dvar に記録されている文字は x です。 $
Download : sample-008.c
/* * 2018/12/21 sample-008.c */ /* * メモリモデルの理解 (8) * * 利用方法 * コンパイル * cc -I ~/c/include -c sample-008.c * リンク * cc -o sample-008.exe sample-008.c * 実行 * ./sample-008.exe */ #include <stdio.h> #include "s_variable.h" /* memory モデルを理解するための関数定義 */ /* * main */ int main ( int argc, char *argv[] ) { /* * C 言語の変数のメモリモデルによる理解 */ char carray[3]; /* char 型の一次元配列 carray の宣言 (サイズは 3) */ /* 意味的には char carry[0]; -- cvar char carry[1]; -- dvar char carry[2]; -- evar のように考えて良い (cf. sample-007.c) */ /* * 配列の要素のアドレスは連続している事が保証される */ printf ( "変数 carray[0] のアドレスは 16 進数表現で %x です。\n", get_variable_address( carray[0] ) ); printf ( "変数 carray[1] のアドレスは 16 進数表現で %x です。\n", get_variable_address( carray[1] ) ); printf ( "変数 carray[2] のアドレスは 16 進数表現で %x です。\n", get_variable_address( carray[2] ) ); /* * 変数をアドレスを利用して参照 */ carray[0] = 'c'; /* 変数 carray[0] に、値 'c' を代入 */ carray[1] = 'D'; /* 変数 carray[1] に、値 'D' を代入 */ carray[2] = '\0'; /* 変数 carray[2] に、値 '\0' を代入 */ printf ( "carray[0] の所から記録されている文字列は (%s) です。\n", get_variable_address( carray[0] ) ); /* * アドレス経由で、変数の内容を変更 */ set_variable_value_at ( get_variable_address( carray[0] ) + 1, 'x' ); /* 変数 carray[0] のアドレスの次のアドレスは carray[1] のアドレスなので.. */ printf ( "carray[0] に記録されている文字は %c です。\n", carray[0] ); /* 結果的に、carray[1] の内容が書き変わる */ printf ( "carray[1] に記録されている文字は %c です。\n", carray[1] ); /* * */ return 0; } /* * */
$ ./sample-008.exe 変数 carray[0] のアドレスは 16 進数表現で 9ec9bb0 です。 変数 carray[1] のアドレスは 16 進数表現で 9ec9bb1 です。 変数 carray[2] のアドレスは 16 進数表現で 9ec9bb2 です。 carray[0] の所から記録されている文字列は (cD) です。 carray[0] に記録されている文字は c です。 carray[1] に記録されている文字は x です。 $
Download : sample-009.c
/* * 2018/12/21 sample-009.c */ /* * メモリモデルの理解 (9) * * 利用方法 * コンパイル * cc -I ~/c/include -c sample-009.c * リンク * cc -o sample-009.exe sample-009.c * 実行 * ./sample-009.exe */ #include <stdio.h> #include "s_variable.h" /* memory モデルを理解するための関数定義 */ /* * main */ int main ( int argc, char *argv[] ) { /* * */ char carray[3]; /* char 型の一次元配列 carray の宣言 (サイズは 3) */ /* * 配列の要素のアドレスは連続している事が保証される */ carray[0] = 'c'; /* 変数 carray[0] に、値 'c' を代入 */ carray[1] = 'D'; /* 変数 carray[1] に、値 'D' を代入 */ carray[2] = '\0'; /* 変数 carray[2] に、値 '\0' を代入 */ printf ( "carray[0] の所から記録されている文字列は (%s) です。\n", get_variable_address( carray[0] ) ); /* * 配列名は、文字列と同じように扱える */ printf ( "carray が表現している文字列は (%s) です。\n", carray ); /* * 文字列の一部を変更する事ができる */ carray[1] = 'U'; /* ニ文字目を 'U' に変更 */ printf ( "carray が表現している文字列は (%s) です。\n", carray ); carray[0] = 'p'; /* 一字目を 'p' に変更 */ printf ( "carray が表現している文字列は (%s) です。\n", carray ); /* * */ return 0; } /* * */
$ ./sample-009.exe carray[0] の所から記録されている文字列は (cD) です。 carray が表現している文字列は (cD) です。 carray が表現している文字列は (cU) です。 carray が表現している文字列は (pU) です。 $
Download : sample-010.c
/* * 2018/12/21 sample-010.c */ /* * 文字配列と文字列 * * 利用方法 * コンパイル * cc -c sample-010.c * リンク * cc -o sample-010.exe sample-010.c * 実行 * ./sample-010.exe */ #include <stdio.h> /* * main */ int main ( int argc, char *argv[] ) { /* * 文字配列の初期化 */ char carray[3] = "AB"; /* carray[0] = 'A'; carray[1] = 'B'; carray[2] = '\0'; */ printf ( "carray[0] は %c です。\n", carray[0] ); printf ( "carray[1] は %c です。\n", carray[1] ); /* * */ return 0; } /* * */
$ ./sample-010.exe carray[0] は A です。 carray[1] は B です。 $
Download : sample-011.c
/* * 2018/12/21 sample-011.c */ /* * アドレス演算子「&」と間接演算子「*」 * * 利用方法 * コンパイル * cc -c sample-011.c * リンク * cc -o sample-011.exe sample-011.c * 実行 * ./sample-011.exe */ #include <stdio.h> /* * main */ int main ( int argc, char *argv[] ) { /* * アドレス演算子「&」と間接演算子「*」 */ char carray[3] = "AB"; /* * 添字による参照 */ printf ( "carry[0] = %c\n", carry[0] ); printf ( "carry[1] = %c\n", carry[1] ); /* * 間接演算子による参照 */ printf ( "*carry = %c\n", *carry ); printf ( "*(carry+1) = %c\n", *(carry+1) ); /* * address の比較 */ s_print_string ( "&carry[0] = %x\n", &carry[0] ); s_print_string ( "carry = %x\n", carry ); /* * 「&」と「*」は逆演算子 */ s_print_string ( "carry = %x\n", carry ); s_print_string ( "&*carry = %x\n", &*carry ); s_print_string ( "carry[0] = %c\n", carry[0] ); s_print_string ( "*&carry[0] = %c\n", *&carry[0] ); /* * */ return 0; } /* * */
$ ./sample-011.exe carray[0] は A です。 carray[1] は B です。 $
Download : sample-012.c
/* * 2018/12/21 sample-012.c */ /* * 二次元配列とメモリモデル (1) * * 利用方法 * コンパイル * cc -c sample-012.c * リンク * cc -o sample-012.exe sample-012.c * 実行 * ./sample-012.exe */ #include <stdio.h> /* * ◯×ゲームのボード (一次元版) * * y * 0 1 2 (y,t) * +-----+-----+-----+ +-----+ * 0 |(0,0)|(0,1)|(0,2)| |(0,0)| 0 = 0*3+0 = t*3+y * +-----+-----+-----+ +-----+ * t 1 |(1,0)|(1,1)|(1,2)| |(0,1)| 1 = 0*3+1 = t*3+y * +-----+-----+-----+ +-----+ * 2 |(2,0)|(2,1)|(2,2)| |(0,2)| 2 = 0*3+2 = t*3+y * +-----+-----+-----+ +-----+ * |(1,0)| 3 = 1*3+0 = t*3+y * +-----+ * |(1,1)| 4 = 1*3+1 = t*3+y * +-----+ * |(1,2)| 5 = 1*3+2 = t*3+y * +-----+ * |(2,0)| 6 = 2*3+0 = t*3+y * +-----+ * |(2,1)| 7 = 2*3+1 = t*3+y * +-----+ * |(2,2)| 8 = 2*3+2 = x*3+y * +-----+ * */ #define BOARD_SIZE 3 /* ボードのサイズ */ #define SENTE_MARK 'o' /* 先手は 'o' (マル) */ #define GOTE_MARK 'x' /* 後手は 'x' (バツ) */ /* * main */ int main ( int argc, char *argv[] ) { /* * */ char board[BOARD_SIZE*BOARD_SIZE]; /* サイズは 3 × 3 */ int t; /* 縱 */ int y; /* 横 */ /* * ある局面 * * oxx * xoo * oox */ board[0*BOARD_SIZE+0] = 'o'; /* (0,0) */ board[0*BOARD_SIZE+1] = 'x'; /* (0,1) */ board[0*BOARD_SIZE+2] = 'x'; /* (0,2) */ board[1*BOARD_SIZE+0] = 'x'; /* (1,0) */ board[1*BOARD_SIZE+1] = 'o'; /* (1,1) */ board[1*BOARD_SIZE+2] = 'o'; /* (1,2) */ board[2*BOARD_SIZE+0] = 'o'; /* (2,0) */ board[2*BOARD_SIZE+1] = 'x'; /* (2,1) */ board[2*BOARD_SIZE+2] = 'x'; /* (2,2) */ /* * */ t = 0; while ( t < BOARD_SIZE ) { y = 0; while ( y < BOARD_SIZE ) { printf ( "%c", board[t*BOARD_SIZE+y] ); y = y + 1; } printf ( "\n" ); t = t + 1; } /* * */ return 0; } /* * */
$ ./sample-012.exe oxx xoo oxx $
Download : sample-013.c
/* * 2018/12/21 sample-013.c */ /* * 二次元配列とメモリモデル (2) * * 利用方法 * コンパイル * cc -c sample-013.c * リンク * cc -o sample-013.exe sample-013.c * 実行 * ./sample-013.exe */ #include <stdio.h> /* * ◯×ゲームのボード (一次元版) * * y * 0 1 2 (y,t) * +-----+-----+-----+ +-----+ * 0 |(0,0)|(0,1)|(0,2)| |(0,0)| 0 = 0*3+0 = t*3+y * +-----+-----+-----+ +-----+ * t 1 |(1,0)|(1,1)|(1,2)| |(0,1)| 1 = 0*3+1 = t*3+y * +-----+-----+-----+ +-----+ * 2 |(2,0)|(2,1)|(2,2)| |(0,2)| 2 = 0*3+2 = t*3+y * +-----+-----+-----+ +-----+ * |(1,0)| 3 = 1*3+0 = t*3+y * +-----+ * |(1,1)| 4 = 1*3+1 = t*3+y * +-----+ * |(1,2)| 5 = 1*3+2 = t*3+y * +-----+ * |(2,0)| 6 = 2*3+0 = t*3+y * +-----+ * |(2,1)| 7 = 2*3+1 = t*3+y * +-----+ * |(2,2)| 8 = 2*3+2 = x*3+y * +-----+ * */ #define BOARD_SIZE 3 /* ボードのサイズ */ #define SENTE_MARK 'o' /* 先手は 'o' (マル) */ #define GOTE_MARK 'x' /* 後手は 'x' (バツ) */ /* * 二次元の座標を一次元に変換する関数 */ int index2d ( int t, int y ) { return t * BOARD_SIZE + y; } /* * main */ int main ( int argc, char *argv[] ) { /* * */ char board[BOARD_SIZE*BOARD_SIZE]; /* サイズは 3 × 3 */ int t; /* 縱 */ int y; /* 横 */ /* * ある局面 * * oxx * xoo * oox */ board[index2d(0,0)] = 'o'; /* (0,0) */ board[index2d(0,1)] = 'x'; /* (0,1) */ board[index2d(0,2)] = 'x'; /* (0,2) */ board[index2d(1,0)] = 'x'; /* (1,0) */ board[index2d(1,1)] = 'o'; /* (1,1) */ board[index2d(1,2)] = 'o'; /* (1,2) */ board[index2d(2,0)] = 'o'; /* (2,0) */ board[index2d(2,1)] = 'x'; /* (2,1) */ board[index2d(2,2)] = 'x'; /* (2,2) */ /* * */ t = 0; while ( t < BOARD_SIZE ) { y = 0; while ( y < BOARD_SIZE ) { printf ( "%c", board[index2d(t,y)] ); y = y + 1; } printf ( "\n" ); t = t + 1; } /* * */ return 0; } /* * */
$ ./sample-013.exe oxx xoo oxx $
Download : sample-014.c
/* * 2018/12/21 sample-014.c */ /* * 二次元配列とメモリモデル (3) * * 利用方法 * コンパイル * cc -c sample-014.c * リンク * cc -o sample-014.exe sample-014.c * 実行 * ./sample-014.exe */ #include <stdio.h> /* * ◯×ゲームのボード (二次元版) * * y * 0 1 2 * +-----+-----+-----+ * 0 |(0,0)|(0,1)|(0,2)| * +-----+-----+-----+ * t 1 |(1,0)|(1,1)|(1,2)| * +-----+-----+-----+ * 2 |(2,0)|(2,1)|(2,2)| * +-----+-----+-----+ * */ #define BOARD_SIZE 3 /* ボードのサイズ */ #define SENTE_MARK 'o' /* 先手は 'o' (マル) */ #define GOTE_MARK 'x' /* 後手は 'x' (バツ) */ /* * main */ int main ( int argc, char *argv[] ) { /* * */ char board[BOARD_SIZE][BOARD_SIZE]; /* サイズは 3 × 3 */ int t; /* 縱 */ int y; /* 横 */ /* * ある局面 * * oxx * xoo * oox */ board[0][0] = 'o'; /* (0,0) */ board[0][1] = 'x'; /* (0,1) */ board[0][2] = 'x'; /* (0,2) */ board[1][0] = 'x'; /* (1,0) */ board[1][1] = 'o'; /* (1,1) */ board[1][2] = 'o'; /* (1,2) */ board[2][0] = 'o'; /* (2,0) */ board[2][1] = 'x'; /* (2,1) */ board[2][2] = 'x'; /* (2,2) */ /* * */ t = 0; while ( t < BOARD_SIZE ) { y = 0; while ( y < BOARD_SIZE ) { printf ( "%c", board[t][y] ); y = y + 1; } printf ( "\n" ); t = t + 1; } /* * */ return 0; } /* * */
$ ./sample-014.exe oxx xoo oxx $
Download : sample-015.c
/* * 2018/12/21 sample-015.c */ /* * 二次元配列とメモリモデル (3) * * 利用方法 * コンパイル * cc -c sample-015.c * リンク * cc -o sample-015.exe sample-015.c * 実行 * ./sample-015.exe */ #include <stdio.h> /* * ◯×ゲームのボード (一次元版) * * y * 0 1 2 (y,t) * +-----+-----+-----+ +-----+ * 0 |(0,0)|(0,1)|(0,2)| |(0,0)| 0 = 0*3+0 = t*3+y * +-----+-----+-----+ +-----+ * t 1 |(1,0)|(1,1)|(1,2)| |(0,1)| 1 = 0*3+1 = t*3+y * +-----+-----+-----+ +-----+ * 2 |(2,0)|(2,1)|(2,2)| |(0,2)| 2 = 0*3+2 = t*3+y * +-----+-----+-----+ +-----+ * |(1,0)| 3 = 1*3+0 = t*3+y * +-----+ * |(1,1)| 4 = 1*3+1 = t*3+y * +-----+ * |(1,2)| 5 = 1*3+2 = t*3+y * +-----+ * |(2,0)| 6 = 2*3+0 = t*3+y * +-----+ * |(2,1)| 7 = 2*3+1 = t*3+y * +-----+ * |(2,2)| 8 = 2*3+2 = x*3+y * +-----+ * */ #define BOARD_SIZE 3 /* ボードのサイズ */ #define SENTE_MARK 'o' /* 先手は 'o' (マル) */ #define GOTE_MARK 'x' /* 後手は 'x' (バツ) */ /* * main */ int main ( int argc, char *argv[] ) { /* * */ char board[BOARD_SIZE][BOARD_SIZE]; /* サイズは 3 × 3 */ int t; /* 縱 */ int y; /* 横 */ /* * */ printf ( "sizeof ( board[0][0] ) = %d\n", sizeof ( board[0][0] ) ); printf ( "sizeof ( board[0] ) = %d\n", sizeof ( board[0] ) ); printf ( "\n" ); for ( t = 0; t < BOARD_SIZE; t++ ) { printf ( "board[%d]=%x\n", t, &board[t] ); for ( y = 0; y < BOARD_SIZE; y++ ) { /* アドレスの表示 */ printf ( "\t(%d,%d)=%x\n", t, y, &board[t][y] ); } printf ( "\n" ); } /* * */ return 0; } /* * */
$ ./sample-015.exe sizeof ( board[0][0] ) = 1 sizeof ( board[0] ) = 3 board[0]=ad04ed70 (0,0)=ad04ed70 (0,1)=ad04ed71 (0,2)=ad04ed72 board[1]=ad04ed73 (1,0)=ad04ed73 (1,1)=ad04ed74 (1,2)=ad04ed75 board[2]=ad04ed76 (2,0)=ad04ed76 (2,1)=ad04ed77 (2,2)=ad04ed78 $
Download : sample-016.c
/* * 2018/12/21 sample-016.c */ /* * ニ次元配列とメモリモデル (4) * * 利用方法 * コンパイル * cc -c sample-016.c * リンク * cc -o sample-016.exe sample-016.c * 実行 * ./sample-016.exe */ #include <stdio.h> /* * 三次の行列 * * A 列 * 0 1 2 * +-----+-----+-----+ * 0 | a00 | a01 | a02 | * +-----+-----+-----+ * 行 1 | a10 | a11 | a12 | * +-----+-----+-----+ * 2 | a20 | a21 | a22 | * +-----+-----+-----+ * */ #define DIM 3 /* 行列の次元 */ /* * void print_array ( int array[DIM][DIM] ) * array を出力する */ void print_array ( int array[DIM][DIM] ) { int row; /* 行 */ int col; /* 列 */ for ( row = 0; row < DIM; row++ ) { printf ( "%c%c ", " | "[row], "/ \\"[row] ); for ( col = 0; col < DIM; col++ ) { printf ( "%d ", array[row][col] ); } printf ( "%c%c\n", "\\ /"[row], " | "[row] ); } } /* * void add_array ( int c[DIM][DIM], int a[DIM][DIM], int b[DIM][DIM] ) * c = a + b */ void add_array ( int c[DIM][DIM], int a[DIM][DIM], int b[DIM][DIM] ) { int row; /* 行 */ int col; /* 列 */ for ( row = 0; row < DIM; row++ ) { for ( col = 0; col < DIM; col++ ) { c[row][col] = a[row][col] + b[row][col]; } } } /* * main */ int main ( int argc, char *argv[] ) { /* * */ int a[DIM][DIM] = { {1, 2, 3}, {2, 3, 4}, {3, 4, 5} }; /* 配列の初期化 */ int b[DIM][DIM] = { {1, 1, 1}, {3, 2, 1}, {4, 2, 0} }; int c[DIM][DIM]; /* a + b の結果を入れる行列 */ /* * */ printf ( "行列 a\n" ); print_array ( a ); printf ( "と、\n" ); printf ( "行列 b\n" ); print_array ( b ); printf ( "の和は\n" ); add_array ( c, a, b ); /* c = a + b */ print_array ( c ); printf ( "になります。\n" ); /* * */ return 0; } /* * */
$ ./sample-016.exe 行列 a / 1 2 3 \ | 2 3 4 | \ 3 4 5 / と、 行列 b / 1 1 1 \ | 3 2 1 | \ 4 2 0 / の和は / 2 3 4 \ | 5 5 5 | \ 7 6 5 / になります。 $
Download : sample-017-01.c
/* * 2018/12/14 sample-017-01.c */ #include <stdio.h> /* * 利用方法 * コンパイル * cc -c sample-017-01.c */ typedef struct { unsigned char intAry[ sizeof ( int ) ]; } Int; void print_int_as_char_list ( Int ia ) { int i; for ( i = 0; i < sizeof ( int ); i++ ) { printf ( "%d ", ia.intAry[i] ); } printf ( "\n" ); }
Download : sample-017.c
/* * 2018/12/21 sample-017.c */ /* * 二次元配列とメモリモデル (5) * * 利用方法 * コンパイル * cc -c sample-017.c * リンク * cc -o sample-017.exe sample-017.c * 実行 * ./sample-017.exe */ #include <stdio.h> #if 0 /* * 利用方法 * コンパイル * cc -I ~/c/include -c sample-017.c * cc -I ~/c/include -c sample-017-01.c * cc -o sample-017.exe sample-017.o sample-017-01.o * 実行 * ./sample-017.exe */ #endif /* * 整数とメモリモデル * * +---+---+---+---+ +---+ * int | | == char | | * +---+---+---+---+ +---+ * | | * +---+ * | | * +---+ * | | * +---+ * * */ int make_int ( int b1, int b2, int b3, int b4 ) { return b1 + 256 * ( b2 + 256 * ( b3 + 256 * b4 ) ); } /* * main */ int main ( int argc, char *argv[] ) { /* * */ int i; printf ( "sizeof( int ) = %d\n", sizeof ( int ) ); i = make_int ( 1, 0, 0, 0 ); printf ( "i = make_int ( 1, 0, 0, 0 ) = %d\n", i ); print_int_as_char_list ( i ); i = make_int ( 2, 3, 0, 0 ); printf ( "i = make_int ( 2, 3, 0, 0 ) = %d\n", i ); print_int_as_char_list ( i ); i = make_int ( 3, 4, 5, 0 ); printf ( "i = make_int ( 3, 4, 5, 0 ) = %d\n", i ); print_int_as_char_list ( i ); i = make_int ( 4, 5, 6, 7 ); printf ( "i = make_int ( 4, 5, 6, 7 ) = %d\n", i ); print_int_as_char_list ( i ); /* * */ printf ( "----\n" ); /* * */ i = 255; printf ( "i = %d\n", i ); print_int_as_char_list ( i ); i = 256; printf ( "i = %d\n", i ); print_int_as_char_list ( i ); /* * */ return 0; } /* * */
$ ./sample-017.exe sizeof( int ) = 4 i = make_int ( 1, 0, 0, 0 ) = 1 1 0 0 0 i = make_int ( 2, 3, 0, 0 ) = 770 2 3 0 0 i = make_int ( 3, 4, 5, 0 ) = 328707 3 4 5 0 i = make_int ( 4, 5, 6, 7 ) = 117835012 4 5 6 7 ---- i = 255 255 0 0 0 i = 256 0 1 0 0 $
/* * 20181221-01-QQQQ.c * union の応用 (整数型変数のメモリ構造の出力) */ #include <stdio.h> /* * IntCharArray */ /* 共用体を利用した IntCharArray 型の宣言 */ typedef union { int iv; /* 整数部 */ char cv[sizeof(int)]; /* 文字(byte)配列部 */ } IntCharArray; /* [union] IntCharArray : int と char[4] を同じ領域で保持する int +-----------+ char[4] +-----------+ | | | | +-----------+ +-----------+ | | | | +-----------+ +-----------+ | | | | +-----------+ +-----------+ | | | | +-----------+ +-----------+ 一つの整数を、4 つの char に分解できる */ /* * printIntCharArray */ void printIntCharArray ( IntCharArray x ) { int i; /* 整数値として出力 */ printf ( "整数値として出力 : %10d\n", x.iv ); /* 書式指定で、%+型を表す文字の間に整数値を指定すると、 固定の表示幅で出力してくれる */ /* 文字(byte)配列として出力 */ printf ( "配列として出力 : " ); for ( i = 0; i < sizeof ( int ); i++ ) { printf ( " %2d", x.cv[i] ); } printf ( "\n" ); } /* * main * 2^b ( b = 0 〜 sizeof(int)*8 - 1 ) を整数値と文字(byte)配列として出力 */ int main ( void ) { int b; /* 2 の指数 */ int v = 1; /* 2 の k 乗の整数値 */ IntCharArray ica; /* 整数と文字配列の共用体 */ for ( b = 0; b < sizeof(int) * 8; b++ ) { /* ica に v の値を整数値として代入 */ ica.iv = v; /* printIntCharArray を呼出して、内容を出力する */ printIntCharArray ( ica ); v = v * 2; } return 0; }
#include <stdio.h> typedef struct { char c0; char c1; char c2; } Char3; void printChar3 ( Char3 str ) { printf ( "変更前 : %c%c%c\n", str.c0, str.c1, str.c2 ); str.c1 = 'X'; printf ( "変更後 : %c%c%c\n", str.c0, str.c1, str.c2 ); } int main(int argc, char *argv[]) { Char3 str = { 'a', 'b', 'c' }; /* 構造体も初期化可能 */ printf ( "呼び出し前 : %c%c%c\n", str.c0, str.c1, str.c2 ); printChar3 ( str ); printf ( "呼び出し後 : %c%c%c\n", str.c0, str.c1, str.c2 ); /* 構造体型の変数の場合、関数の引数に指定する事が可能で 関数内で、値を変更する事もできるが、その値の変更は、 呼び出し元には、影響しない => 関数を呼び出すときに、値がコピーされ、関数仮引数変数に その値が代入されるため、コピーされた値を書き換えても、 元の変数の値には影響しない */ return 0; }
#include <stdio.h> void printChar ( char str[] ) { printf ( "変更前 : %s\n", str ); str[1] = 'X'; printf ( "変更前 : %s\n", str ); } int main(int argc, char *argv[]) { char str[] = "abc"; printf ( "呼び出し前 : %s\n", str ); printChar ( str ); /* 配列「名」が引数として与えられる */ printf ( "呼び出し後 : %s\n", str ); /* 配列の要素が、関数の呼び出しの前と後で変化してしまっている */ return 0; }
#include <stdio.h> /* sizeof 演算子 型名を引数に指定すると、そのサイズ(byte 数)を教えてくれる !! C 言語では、型のサイズは、システムによって異なる(事がある) !! sizeof(int) == 4 などと仮定してはいけない !! => 他の言語では見られない !! ただし、最新の C 言語では sizeof(char) == 1 と決めている */ int main(int argc, char *argv[] ) { printf ( "sizeof (char) = %d\n", sizeof ( char ) ); /* ここは「1」と出るはず */ printf ( "sizeof (int) = %d\n", sizeof ( int ) ); printf ( "sizeof (double) = %d\n", sizeof ( double ) ); /* システムによって結果が異なるかも => ググってみる */ printf ( "sizeof (char *) = %d\n", sizeof ( char *) ); /* 「文字列」のサイズ ?? => なぜか、「固定」... */ return 0; }
#include <stdio.h> typedef struct{ char a[4]; /* サイズ 4 の配列 */ char b[4]; /* サイズ 4 の配列 */ int c; /* サイズ 1 の配列 */ char d[4]; /* サイズ 4 の配列 */ } Array; int main(int argc, char *argv[]) { Array v; printf ( "ポインター値の表示 : %p\n", v.a ); /* 「%p」は、ポインター値を 16 進数で表示する書式指定 */ printf ( "ポインター値の表示 : %p\n", v.b ); printf ( "ポインター値の表示 : %p\n", v.d ); /* どうやら、メモリの番地を表示しているようにみえる */ return 0; }
#include <stdio.h> /* 配列名は、番地を表現しているように見える */ void testfunc ( char *str ) { printf ( "文字列 : %s\n", str ); printf ( "ポインター値 : %p\n", str ); printf ( "文字列 : %s\n", str + 1 ); printf ( "ポインター値 : %p\n", str + 1 ); /* 配列名を引数に指定すると、渡されるのは、 配列そのものではなく、配列の先頭の要素が入っている メモリの番地が渡される */ } int main(int argc, char *argv[]) { char carray[] = "ohayou"; testfunc ( "Hello, World" ); /* 文字列 */ testfunc ( carray ); /* 文字配列の配列名 */ return 0; }
#include <stdio.h> void testfunc1 ( char ch ) { printf ( "前 : %c\n", ch ); ch = 'X'; printf ( "後 : %c\n", ch ); } void testfunc2 ( char ch[] ) { printf ( "前 : %c\n", ch[0] ); ch[0] = 'X'; printf ( "後 : %c\n", ch[0] ); } int main(int argc, char *argv[]) { char str[] = { 'a' }; char ch = 'b'; printf ( "call 前 : %c\n", ch ); testfunc1 ( ch ); /* 番地の先の値 */ printf ( "call 後 : %c\n", ch ); printf ( "call 前 : %c\n", str[0] ); testfunc1 ( str[0] ); /* 番地の先の値 */ printf ( "call 後 : %c\n", str[0] ); printf ( "call 前 : %c\n", str[0] ); testfunc2 ( str ); /* 番地を表す */ printf ( "call 後 : %c\n", str[0] ); printf ( "call 前 : %c\n", ch ); testfunc2 ( &ch ); /* 番地を表す */ /* & は、scanf の時に出てきて、その時はおまじない */ /* 実は、その変数名が表すメモリセルの番地を取り出している */ printf ( "call 後 : %c\n", ch ); /* 変数名は、「番地」に対応している ただし、「変数名」を直接指定すると、「その番地」のセルの内容を表す */ return 0; }
#include <stdio.h> int main(int argc, char *argv[]) { int a; /* 単純な変数 */ /* a +-------+ sizeof(int) -> 4 | | +-------+ | | <- 4 byte 分のセルが準備 +-------+ | | +-------+ | | +-------+ */ a = 10; /* a が表す番地が指すセルの内容を 10 に変更 */ printf ( "a=%d\n", a ); /* ふつうの変数参照 */ /* a が表す番地が指すセルの内容(10) を取り出す */ printf ( "*&a=%d\n", *(&a) ); /* a と同じなので、右辺値になる */ *(&a) = 999; /* *(&a) は a と同じ左辺値なので.. */ printf ( "a=%d\n", a ); /* ふつうの変数参照 */ return 0; }
#include <stdio.h> int main(int argc, char *argv[]) { int p[1]; /* 単純な変数 */ /* p ---> p[0]+-------+ sizeof(int) -> 4 配列名 p は | | 最初の要素の +-------+ 番地を意味する | | <- 4 byte 分のセルが準備 +-------+ | | +-------+ | | +-------+ */ printf ( "p=%p\n", p ); printf ( "&(*p)=%p\n", &(*p) ); return 0; }
#include <stdio.h> void setup( int *p, int v ) { /* p という変数には &a の値が入っている (v には 999 が入っている) */ *p = v; /* *(&a)=999 */ /* a=999 */ /* a という変数に 999 を代入したのと同じ結果 */ } int main(int argc, char *argv[]) { int a; /* a +-------+ a = 0xffff1234 | | +-------+ | | +-------+ | | +-------+ | | +-------+ | | */ int b; a = 10; /* a +-------+ | | +-- ---+ | | +-- ---+ | 10 | +-- ---+ | | +-------+ | | */ b = 20; printf ( "a = %d\n", a ); /* a という名前を指定すると、a の対応する番地が指すセル の内容 (10) が表示される */ setup ( &a, 999 ); /* setup に渡されるのは、a の「番地」と 999 と値 */ /* 変数「a」を関数に渡す事はできない */ /* たんに「a」とかいたら、「a」ではなく、 「a」が指すセルの値が渡される */ /* &a と書くと(「変数 a」が渡されるわけでないが、 a の「アドレス値」を渡す事が可能になる */ printf ( "a = %d\n", a ); printf ( "b = %d\n", b ); setup ( &b, 888 ); printf ( "b = %d\n", b ); return 0; }
[先週] 共用体(union) : 二つ(以上)の型の和集合型を作る cf. 構造体(struct) : 二つ(以上)の型の直積集合型を作る 複数の型のデータが入る一つの変数を作る その変数には、一つの値しか入らない cf. 構造体の方は、二つ(以上)の値が入る 制御構造とデータ構造の関係 制御構造 データ構造 順接 構造体 条件分岐 共用体 繰り返し 配列 => 制御構造(プログラム)の裏にデータ構造がある -> プログラムを作るために、データ構造を配慮する事が望ましい 「文字列」と文字配列の関係 「文字列」は、実は、特殊な文字配列の事だった。 文字列は、文字配列に、文字と EOS ('\0')が詰まったもの 特に、"?" は、その中身が変更できないようになっている <= 文字配列を文字列として利用する事ができる [ポイント] (今まで、「文字列」を関数の引数に指定してきたのと 同じように) 配列名を関数の引数に指定できる。 (なぜか、他の型と異なり..) 配列のメンバーが、関数内と呼び出し側で共有される => 配列名や"?" は、「ポインター値」を持つ <= 「ポインター値」がいろいろな事をする 見かけ上、引数に対する振る舞いが異なるのはなぜか ? [今日の内容] メモリーモデル メモリーは、セルと呼ばれる小さな記憶装置の集まり 一つセルは、8bit = 1 byte の情報(0?255=2^8-1)が記録できる 一つ一つのセルには、番地が付けられている C 言語でいう変数の実体は、このセルのあつまり。 char 型の変数は、一つセルに対応している !! 今の言語は、このメモリーに関しては、抽象度が高いので、 !! 意識しなくてもよい !! C 言語はメモリを意識しないといけない言語(中級言語) 「文字列」と呼んでいた "?" が直接表現しているのは、 "?" の文字が入っているセルのアドレスの値を表現している 1 を加えると、アドレスが 1 増えて、一つ文字を飛ばした場所を 表現する事になるので、そこから出力すると、先頭の文字が一文字短くなったように見える ここで、「文字列」は、「文字配列」だったので、 文字配列の配列名を引数渡しても同じ事がおきる C 言語の変数と、メモリモデルの関係 C 言語 メモリモデル 変数 セルの集まり 変数名 セルの集まりの先頭のアドレス 変数名の右辺値 セルの内容そのもの 式の中に「変数名」だけを書く => 右辺値を表す # 関数の引数が「式」である事に注意 もし、「左辺値」を必要する場合は、変数の前に「&」を付ける => アドレス値(に相当する値)が取り出される 変数名の左辺値 アドレスを指す 配列 セルの集まり 配列名 セルの先頭の要素のアドレス値 もし、「配列名」だけを、書くと、これは、そのアドレス値となる 配列名は、「定数」 # char a[10]; a[0]='A' は OK だが、 a="abc" は NG !! 変数名と配列名は扱いが異なるのは気持ち悪い !! => 番地を利用する事で、メモリが直接利用可能なのは、大変強力 !! 効率が全然違う !! => 配列名を単独でかいた場合に、番地になるという扱いは、 !! C 言語で効率的なプログラムが書けるようにする仕組み # 代入文 a=a の時、左の a と右の a は、違う意味を持つ # 左側の a は、代入先のアドレスを表し、 # 変数 a の左辺値 # 右側の a は、セルの中身を表している # 変数 a の右辺値 C 言語では、メモリの番地がデータとして扱える & 「左辺値を持つもの」の前につけて、「アドレス値」を取り出す アドレス演算子(ポインター演算子) * 「アドレス値」から、「左辺値を持つもの」に変える 間接参照演算子(ポインター剥ぎ演算子) => この二つは、逆演算子になっている 「a」が変数の時 a == *(&a) 「p」が「アドレス値」の時 p == &(*p) 「a == *(&a)」 &a は値を持つので、関数の引数に渡したり、値として返せる メモリモデル アドレスがある セルを指す C 言語の変数は、変数名がある 記憶領域を指す => 記憶領域を指す「アドレス値」が扱える(表面にでてくる) => 配列名は、「アドレス値」そのもの 演算子 * / & & : 変数からアドレス値 * : アドレス値から変数 互いに、逆変換になっている !! アドレス値もまた「値」である !! => C 言語の中で、操作可能な対象 [いろいろ分かった事] & -- scanf の所で出てきた scanf が、変数の値を変更できる理由は、 scanf にアドレス値を渡していたから cf. int a; scanf ( "%d", &a ); /* キーボードから整数値を入力し、変数 a に代入する */ /* scanf は、引数で渡されたアドレス値と、 * (間接参照演算子)を利用して a の値を 書き換えた */ * -- 「文字列」の所で出てきた 「文字列」は、アドレス値をもっており、 * はアドレス値から、そのセルに対応した変数を作る そして、その変数の(右辺)値が、先頭の文字になっていただけ # なぜ、「文字列」に 1 を加えると、先頭の文字が欠けるのか # => 「文字列」がアドレス値で、1 を加えると、「次の文字から始まる、文字列」のアドレス値になるから
課題プログラム内の「/*名前:ここ*/」の部分を書き換え「/*この部分を完成させなさい*/」の部分にプログラムを追加して、プログラムを完成させます。
Download : 20181221-01.c
/* * 20181221-01-QQQQ.c * union の応用 (整数型変数のメモリ構造の出力) */ #include <stdio.h> /* * IntCharArray */ /* 共用体を利用した IntCharArray 型の宣言 */ typedef union { int iv; /* 整数部 */ char cv[sizeof(int)]; /* 文字(byte)配列部 */ } IntCharArray; /* * printIntCharArray */ void printIntCharArray ( IntCharArray x ) { int i; /* 整数値として出力 */ /* ** この部分を完成させなさい */ /* 文字(byte)配列として出力 */ printf ( "配列として出力 : " ); for ( i = 0; i < sizeof ( int ); i++ ) { printf ( " %2d", x.cv[i] ); } printf ( "\n" ); } /* * main * 2^b ( b = 0 〜 sizeof(int)*8 - 1 ) を整数値と文字(byte)配列として出力 */ int main ( void ) { int b; /* 2 の指数 */ int v = 1; /* 2 の k 乗の整数値 */ IntCharArray ica; /* 整数と文字配列の共用体 */ for ( b = 0; b < sizeof(int) * 8; b++ ) { /* ica に v の値を整数値として代入 */ /* ** この部分を完成させなさい */ /* printIntCharArray を呼出して、内容を出力する */ printIntCharArray ( ica ); v = v * 2; } return 0; }
$ ./20181221-01-QQQQ.exe 整数値として出力 : 1, 配列として出力 : 1 0 0 0 整数値として出力 : 2, 配列として出力 : 2 0 0 0 整数値として出力 : 4, 配列として出力 : 4 0 0 0 整数値として出力 : 8, 配列として出力 : 8 0 0 0 整数値として出力 : 16, 配列として出力 : 16 0 0 0 整数値として出力 : 32, 配列として出力 : 32 0 0 0 整数値として出力 : 64, 配列として出力 : 64 0 0 0 整数値として出力 : 128, 配列として出力 : -128 0 0 0 整数値として出力 : 256, 配列として出力 : 0 1 0 0 整数値として出力 : 512, 配列として出力 : 0 2 0 0 整数値として出力 : 1024, 配列として出力 : 0 4 0 0 整数値として出力 : 2048, 配列として出力 : 0 8 0 0 整数値として出力 : 4096, 配列として出力 : 0 16 0 0 整数値として出力 : 8192, 配列として出力 : 0 32 0 0 整数値として出力 : 16384, 配列として出力 : 0 64 0 0 整数値として出力 : 32768, 配列として出力 : 0 -128 0 0 整数値として出力 : 65536, 配列として出力 : 0 0 1 0 整数値として出力 : 131072, 配列として出力 : 0 0 2 0 整数値として出力 : 262144, 配列として出力 : 0 0 4 0 整数値として出力 : 524288, 配列として出力 : 0 0 8 0 整数値として出力 : 1048576, 配列として出力 : 0 0 16 0 整数値として出力 : 2097152, 配列として出力 : 0 0 32 0 整数値として出力 : 4194304, 配列として出力 : 0 0 64 0 整数値として出力 : 8388608, 配列として出力 : 0 0 -128 0 整数値として出力 : 16777216, 配列として出力 : 0 0 0 1 整数値として出力 : 33554432, 配列として出力 : 0 0 0 2 整数値として出力 : 67108864, 配列として出力 : 0 0 0 4 整数値として出力 : 134217728, 配列として出力 : 0 0 0 8 整数値として出力 : 268435456, 配列として出力 : 0 0 0 16 整数値として出力 : 536870912, 配列として出力 : 0 0 0 32 整数値として出力 : 1073741824, 配列として出力 : 0 0 0 64 整数値として出力 : -2147483648, 配列として出力 : 0 0 0 -128 $