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 $