/*
* 課題 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 $