/* * 課題 CNAME-01 * * 2017/12/15 FILENAME * * 三角形の形をした配列 * */ /* * 利用方法 * コンパイル * cc -o BASENAME.exe FILENAME * 実行 * ./BASENAME.exe */ #include <stdio.h> #define ARRAY_SIZE_N 10 /* * a[i] の先頭オフセット : 1 + .. + i = i*(i+1)/2 * ( i = 0 の時は、「」(0) ) * ( i = 1 の時は、「1」 ) * ( i = 2 の時は 「1 + 2」 ) * a[i][j] のアドレスのオフセットは -> i*(i+1)/2 + j * * j 0 1 2 * i * 0 0 a[0][0] * 1 1 a[1][0], a[1][1] * 2 3 a[2][0], a[2][1], a[2][2] * 3 6 .. * 9 a[9][0], a[9][1], a[9][2], .., a[9][9] * */ int get_tri ( int *ary, int n, int m ) { /* ary を、三角形の配列だと思い、 その n,m 番目が欲しい */ /* * n,m 番目のオフセットを計算して、その値からアドレスを計算し * 値を取り出せばよい */ return ary[ n*(n+1)/2 + m ]; } void set_tri ( int *ary, int n, int m, int value ) { /* ary を、三角形の配列だと思い、 その n,m 番目に value を代入したいので... */ ary[ n*(n+1)/2 + m ] = value; } /* * main */ int main ( int argc, char *argv[] ) { int triary[ARRAY_SIZE_N*(ARRAY_SIZE_N+1)/2]; int n; int m; int i; for ( n = 0; n < ARRAY_SIZE_N; n++ ) { for ( m = 0; m <= n; m++ ) { set_tri ( triary, n, m, n * 100 + m ); } } for ( i = 0; i < ARRAY_SIZE_N*(ARRAY_SIZE_N+1)/2; i++ ) { printf ( "triary[%d] = %d\n", i, triary[i] ); } for ( n = 0; n < ARRAY_SIZE_N; n++ ) { for ( m = 0; m <= n; m++ ) { printf ( "triary[%d][%d] = %d\n", n, m, get_tri( triary, n, m ) ); } } return 0; }
#include <stdio.h> int main(int argc, char *argv[]) { int i; char a[10]; printf ( "&i = %p\n", &i ); printf ( "a = %p\n", a ); /* %p はポインタ値を表示 */ return 0; }
#include <stdio.h> int main(int argc, char *argv[]) { int i; char c; printf ( "&c = %p\n", &c ); printf ( "&c + 1 = %p\n", &c + 1 ); printf ( "&i = %p\n", &i ); printf ( "&i + 1 = %p\n", &i + 1 ); /* a[n] == *(a+n) 「&*」 == 「*&」 ==「] a + n == &*(a+n) == &(*(a+n)) == &(a[n]) -> n 番目の要素のアドレス値 アドレス値は先頭+n*要素のサイズ ^^^^^^^^^^^^ 型情報から */ return 0; }
#include <stdio.h> /* 配列とアドレス計算の関係 */ int main(int argc, char *argv[]) { char ca[10]; int ia[10]; printf ( "ca = %p\n", ca ); /* ca の値は定数 */ printf ( "ca + 1 = %p\n", ca + 1 ); printf ( "&ca[1] = %p\n", &ca[1] ); printf ( "ia = %p\n", ia ); printf ( "ia + 1 = %p\n", ia + 1 ); printf ( "&ia[1] = %p\n", &ia[1] ); return 0; }
#include <stdio.h> /* * アドレス値を 4 増やしたもの */ char *next_address( char *ptr ) { return ptr + 4; /* アドレス値は、sizeof(char) * 4 だけ増える */ /* sizeof(char) == 1 なので、結局 1 * 4 = 4 だけ増える */ }
#include <stdio.h> extern int *next_address( int *ptr ); /* これは、本来の結果とは異なる */ int main(int argc, char *argv[] ) { int a[10]; printf ( "a = %p\n", a ); printf ( "a + 1 = %p\n", a + 1 ); printf ( "&a[1] = %p\n", &a[1] ); printf ( "??? = %p\n", next_address ( a ) ); return 0; }
#include <stdio.h> int main(int argc, char *argv[] ) { int a[10]; /* a は int 型のポインタ値「(int *)型」*/ char c[10]; printf ( "a = %p\n", a ); printf ( "a + 1 = %p\n", a + 1 ); /* +1 するときに sizeof(int)=4 を加えた */ printf ( "&a[1] = %p\n", &a[1] ); printf ( "??? = %p\n", (char *)a + 4 ); /* a の前に (char *) を先行する事により、 そのポインタ値の型を (char *) 型に変更する事ができる */ printf ( "c = %p\n", c ); printf ( "&c[4] = %p\n", &c[4] ); printf ( "??? = %p\n", ((int *)c) + 1 ); return 0; }
#include <stdio.h> int main(int argc, char *argv[] ) { int v = 0x12345678; /* 16 進数で数値指定 */ /* +-----------+ 10 進数 16進数 v | 0x78 | 0 ? 255 -> 0x00 ? 0xff +-----------+ | 0x56 | +-----------+ | 0x34 | +-----------+ | 0x12 | +-----------+ */ printf ( "v = %x\n", v ); /* %x は整数値を 16 進数で出す */ printf ( "*((char*)&v)+0) = %x\n", *(((char*)&v)+0) ); printf ( "*((char*)&v)+1) = %x\n", *(((char*)&v)+1) ); printf ( "*((char*)&v)+2) = %x\n", *(((char*)&v)+2) ); printf ( "*((char*)&v)+3) = %x\n", *(((char*)&v)+3) ); printf ( "&v = %p\n", &v ); printf ( "(char*) &v = %p\n", (char*) &v ); printf ( "v = %x\n", v ); *(((char*)&v)+0) = 0xab; printf ( "v = %x\n", v ); return 0; }
#include <stdio.h> int main(int argc, char *argv[]) { int i; printf ( "整数値をいれてください : " ); scanf ( "%d", &i ); /* ポインタが利用されている */ /* うっかり 「&」を忘れたら.. */ /* 普通は、コアダンプする */ printf ( "入力したデータは %d でした。\n", i ); return 0; }
#include <stdio.h> int main(int argc, char *argv[]) { int ary[2][3]; int i; int j; for ( i = 0; i < 2; i++ ) { for ( j = 0; j < 3; j++ ) { ary[i][j] = i * 100 + j; printf ( "&ary[%d][%d] = %p\n", i, j, &ary[i][j] ); } } printf ( "((int *)ary)[3] = %d\n", ((int *)ary)[3] ); printf ( "((int *)ary)+3 = %p\n", ((int *)ary)+3 ); /* 1 * 3 + 0 = 3 */ printf ( "ary[1][0] = %d\n", ary[1][0] ); printf ( "&ary[1][0] = %p\n", &ary[1][0] ); return 0; }
#include <stdio.h> #include <malloc.h> /* malloc を利用するためのヘッダーファイル */ /* * 目的 * 最初に、ユーザに整数のデータ数とデータを入力してもらい、 * そのデータを逆順に出力する */ int main(int argc, char *argv[]) { int n; /* 入力するデータ数を保存 */ int *pary; /* pary は変数 : (int *) 型 -> int へのポインタ値を保持する変数 */ /* このポインタ型変数の先にデータを保存する */ int i; /* 繰り返しの制御変数 */ printf ( "何個の整数を入力しますか ? : " ); scanf ( "%d", &n ); pary = (int *)calloc ( n, sizeof(int) ); /* int 型のサイズ領域を n 個分利用します */ /* calloc が、(あらかじめ確保された領域から指定した分だけ 獲得してきたかのように..) その目的で利用できる 領域を「リザーブ」して、その先頭のアドレス値を返す 実は、当然、「獲得できない」事もありうる その場合は NULL という特別な値が返る事になっている -> 本来は、NULL かどうかを判定する必要があるが 今回は「説明のために省略した」 以下、pary は サイズ n の配列名と同じように扱える */ for ( i = 0; i < n; i++ ) { printf ( "%d 番目のデータ ? : ", i ); scanf ( "%d", &pary[i] ); } for ( i = 0; i < n; i++ ) { printf ( "%d\n", pary[n-i-1] ); } free ( pary ); /* 利用が終わったので、返却をする */ /* alloc で獲得した領域を返さないと、メモリ不足になる */ return 0; }
メモリモデル +-------+ | | セル : 1 byte = 8 bit = 0 ? 255 までの 256 +-------+ メモリ +-------+ | | <= 個々セルには、アドレス(番地)がついている +-------+ | | +-------+ | | +-------+ | | +-------+ .. +-------+ | | +-------+ セルを操作するには、そのセルのアドレスが解ればよい 個々のセルは、あたかも、C 言語の char 型変数と同じ振る舞い C 言語の変数は、連続したセルからなる いくつか ? -> sizeof 例 : sizeof ( char ) = 1 -> char 型変数はセル 1 個 例 : sizeof ( int ) = 4 -> int 型変数はセル 4 個からなる 今までの「C 言語の『変数』という概念」を、メモリのセルで説明できる C 言語では、変数名を利用して、変数を操作 変数名が解らないと操作できない !! 「変数名」を操作する方法はない メモリでは、アドレスを利用して、セルを操作 アドレスは計算できるので、知らなくても計算(作る)事で、知らないセルを操作できる -> 強力だが、危険な可能性がある ポインタ値: C 言語の(コンパイル時、実行共通の)概念 +--- アドレス値 +---+ | | +--- 型情報 | +--- size : sizeof で知る事ができる +---+-- メモリモデル | 実行時の概念 +--- 扱いかた (演算の仕方/コーディング) +-- 言語が導入 コンパイル時の概念 関数が実行時に引数で渡すのは、アドレス値だけ 型情報は、(コンパイル時の概念で、実行時はなく)渡されない == p-004.c p-004-sub.c 実行時には、関数にアドレス値しか渡されないことを悪用して アドレス値を自由に変更できる !! できるなら、やってもよい => C 言語の発想 !! それが、便利で、分かっている人が使うなら積極的にサポートする p-005.c ポインタ値 = アドレス値 + 型情報 アドレス値は、計算 ( 整数値を加える ) ができる 型情報は、値の前に、(型 *) を付けて、変更できる キャスト : 指定した(ポインタ値の)アドレス値を別の型のポインタ値(アドレス値は変わらない)に型変換する キャストの結果、アドレス値(対象)は変わらないが型が変わるので、「扱い」が変わる C 言語では、自由にポインタ値を(計算によって)作る事ができる その値が、適切(自分が参照したい変数を指す)である事を意識する必要がある !! 誰も(コンパイラも、OS もその値が適切かどうかは)見張っていない きちんと理解して、扱う必要がある(要注意) == (先週の続き) 多次元配列 : 配列の配列... -> メモリ上の連続したセルに対応する 例: char a[6]; a[0] ? a[5] の 6 個のようそ +-----------+ a-> a[0]| | +-----------+ a[1]| | +-----------+ a[2]| | +-----------+ a[3]| | <- a + 3 * sizeof(char) +-----------+ a[4]| | +-----------+ a[5]| | +-----------+ char b[2][3]; b[0][0], b[0][1], b[0][2], b[1][0], b[1][1], b[1][2] の合計 6 個 +-----------+ b-> b[0][0] | | b[0] は、b[0][0], b[0][1], b[0][2] という +-----------+ 三つの要素を持つ配列として扱われる b[0][1] | | b[1] も同様 +-----------+ だから、b は b[0] b[1] からなる配列 b[0][2] | | +-----------+ b[1][0] | | +-----------+ b[1][1] | | <- b + sizeof(char [3])*1 + sizeof(char) * 1 +-----------+ sizeof(char b[0] ) b[1][2] | | +-----------+ 今日の課題の 20171215-01 は、一次元配列の添え字を計算する事により、 任意の形の配列として利用可能 今日の課題は、 一次元配列を、好きな形配列にした これを援用すると... 一つの一次元配列を、複数の配列とみなす事もできる 例: int a[10] -> a[0] ? a[4], a[5] ? a[9] さらに、この考え方を推し進めると どこかに巨大な配列があって、 すきな時に好きなだけ、きりとって利用できるとうれしい 実際にこれが可能 !! C 言語の配列のサイズは、プログラム(コンパイル)時に決定する必要がある !! 一方、配列のサイズは、後(実行時)に決められると便利 !! これは矛盾している !! -> この矛盾の解決方法の一つは、最初に(無駄でも)、たくさんのサイズをとっているとよい !! 効率が悪い/無駄が生じる C 言語では、あらかじめ大きな一つ配列が用意され、 それが必要に応じて、切り取って使える様な仕組みが用意されている malloc の仕組み
Download : 20171215-01.c
/* * 課題 CNAME-01 * * 2017/12/15 FILENAME * * 三角形の形をした配列 * */ /* * 利用方法 * コンパイル * cc -o BASENAME.exe FILENAME * 実行 * ./BASENAME.exe */ #include <stdio.h> #define ARRAY_SIZE_N 10 /* * * 0 a[0][0] * 1 a[1][0], a[1][1] * 3 a[2][0], a[2][1], a[2][2] * 6 .. * a[9][0], a[9][1], a[9][2], .., a[9][9] * */ int get_tri ( int *ary, int n, int m ) { /* ** この部分を完成させなさい */ } void set_tri ( int *ary, int n, int m, int value ) { /* ** この部分を完成させなさい */ } /* * main */ int main ( int argc, char *argv[] ) { int triary[ARRAY_SIZE_N*(ARRAY_SIZE_N+1)/2]; int n; int m; int i; for ( n = 0; n < ARRAY_SIZE_N; n++ ) { for ( m = 0; m <= n; m++ ) { set_tri ( triary, n, m, n * 100 + m ); } } for ( i = 0; i < ARRAY_SIZE_N*(ARRAY_SIZE_N+1)/2; i++ ) { printf ( "triary[%d] = %d\n", i, triary[i] ); } for ( n = 0; n < ARRAY_SIZE_N; n++ ) { for ( m = 0; m <= n; m++ ) { printf ( "triary[%d][%d] = %d\n", n, m, get_tri( triary, n, m ) ); } } return 0; }
$ ./20171215-01-QQQQ.exe triary[0] = 0 triary[1] = 100 triary[2] = 101 triary[3] = 200 triary[4] = 201 triary[5] = 202 triary[6] = 300 triary[7] = 301 triary[8] = 302 triary[9] = 303 triary[10] = 400 triary[11] = 401 triary[12] = 402 triary[13] = 403 triary[14] = 404 triary[15] = 500 triary[16] = 501 triary[17] = 502 triary[18] = 503 triary[19] = 504 triary[20] = 505 triary[21] = 600 triary[22] = 601 triary[23] = 602 triary[24] = 603 triary[25] = 604 triary[26] = 605 triary[27] = 606 triary[28] = 700 triary[29] = 701 triary[30] = 702 triary[31] = 703 triary[32] = 704 triary[33] = 705 triary[34] = 706 triary[35] = 707 triary[36] = 800 triary[37] = 801 triary[38] = 802 triary[39] = 803 triary[40] = 804 triary[41] = 805 triary[42] = 806 triary[43] = 807 triary[44] = 808 triary[45] = 900 triary[46] = 901 triary[47] = 902 triary[48] = 903 triary[49] = 904 triary[50] = 905 triary[51] = 906 triary[52] = 907 triary[53] = 908 triary[54] = 909 triary[0][0] = 0 triary[1][0] = 100 triary[1][1] = 101 triary[2][0] = 200 triary[2][1] = 201 triary[2][2] = 202 triary[3][0] = 300 triary[3][1] = 301 triary[3][2] = 302 triary[3][3] = 303 triary[4][0] = 400 triary[4][1] = 401 triary[4][2] = 402 triary[4][3] = 403 triary[4][4] = 404 triary[5][0] = 500 triary[5][1] = 501 triary[5][2] = 502 triary[5][3] = 503 triary[5][4] = 504 triary[5][5] = 505 triary[6][0] = 600 triary[6][1] = 601 triary[6][2] = 602 triary[6][3] = 603 triary[6][4] = 604 triary[6][5] = 605 triary[6][6] = 606 triary[7][0] = 700 triary[7][1] = 701 triary[7][2] = 702 triary[7][3] = 703 triary[7][4] = 704 triary[7][5] = 705 triary[7][6] = 706 triary[7][7] = 707 triary[8][0] = 800 triary[8][1] = 801 triary[8][2] = 802 triary[8][3] = 803 triary[8][4] = 804 triary[8][5] = 805 triary[8][6] = 806 triary[8][7] = 807 triary[8][8] = 808 triary[9][0] = 900 triary[9][1] = 901 triary[9][2] = 902 triary[9][3] = 903 triary[9][4] = 904 triary[9][5] = 905 triary[9][6] = 906 triary[9][7] = 907 triary[9][8] = 908 triary[9][9] = 909 $