Download : sample-001.c
/*
* 2021/12/10 sample-001.c
*
* ポインター型変数の利用
*
*/
#include <stdio.h>
#include <malloc.h>
/*
* 利用方法
* コンパイル
* cc -o sample-001.exe sample-001.c
* 実行
* ./sample-001.exe
*/
/*
* main 関数
*/
int main ( int argc, char *argv[] ) {
int v; /* 整数型の変数を確保 */
int *p = &v; /* ポインター型変数 p に v のポインター値を代入する */
/* *p は、変数 v と全く同じに振る舞う */
*p = 1;
printf ( "*p = %d\n", *p );
printf ( "v = %d\n", v );
*p = *p + 10;
printf ( "*p = %d\n", *p );
printf ( "v = %d\n", v );
return 0;
}
$ ./sample-001.exe *p = 1 v = 1 *p = 11 v = 11 $
Download : sample-002.c
/*
* 2021/12/10 sample-002.c
*
* 動的領域の確保 (単純変数)
*
*/
#include <stdio.h>
#include <malloc.h> /* calloc/free を利用する場合に必要 */
/*
* 利用方法
* コンパイル
* cc -o sample-002.exe sample-002.c
* 実行
* ./sample-002.exe
*/
/*
* main 関数
*/
int main ( int argc, char *argv[] ) {
int *p;
/* 整数型変数の領域を 1 つ分確保 */
p = (int *)calloc ( 1, sizeof(int) );
if ( p == NULL ) { /* メモリの確保に失敗した場合は NULL が返る */
printf ( "整数領域のメモリ確保に失敗しました。\n" );
} else {
/* 以下は、*p を普通の整数型変数と同じ様に利用できる */
*p = 1;
printf ( "*p = %d\n", *p );
*p = *p + 10;
printf ( "*p = %d\n", *p );
/* 動的に確保したメモリは最後に必ず、free で解放する */
free ( p );
}
return 0;
}
$ ./sample-002.exe *p = 1 *p = 11 $
Download : sample-003.c
/*
* 2021/12/10 sample-003.c
*
* ポインター型変数の利用(2)
*
*/
#include <stdio.h>
/*
* 利用方法
* コンパイル
* cc -o sample-003.exe sample-003.c
* 実行
* ./sample-003.exe
*/
/*
* main 関数
*/
int main ( int argc, char *argv[] ) {
int a[10]; /* 整数型の配列を確保 */
int *p = a; /* ポインター型変数 p に a の先頭の要素のポインター値を代入する */
/* p は、配列名 a と全く同じに振る舞う */
p[2] = 1;
printf ( "p[2] = %d\n", p[2] );
printf ( "a[2] = %d\n", a[2] );
p[4] = p[2] + 10;
printf ( "p[4] = %d\n", p[4] );
printf ( "a[4] = %d\n", a[4] );
return 0;
}
$ ./sample-003.exe p[2] = 1 a[2] = 1 p[4] = 11 a[4] = 11 $
Download : sample-004.c
/*
* 2021/12/10 sample-004.c
*
* 動的領域の確保 (配列)
*
*/
#include <stdio.h>
#include <malloc.h>
/*
* 利用方法
* コンパイル
* cc -o sample-004.exe sample-004.c
* 実行
* ./sample-004.exe
*/
/*
* main 関数
*/
int main ( int argc, char *argv[] ) {
int *p;
/* 整数型変数の領域を 10 つ分確保 */
p = (int *)calloc ( 10, sizeof(int) );
if ( p == NULL ) { /* メモリの確保に失敗した場合は NULL が返る */
printf ( "整数領域のメモリ確保に失敗しました。\n" );
} else {
/* 以下は、p を普通の整数型配列名と同じ様に利用できる */
p[2] = 1;
printf ( "p[2] = %d\n", p[2] );
p[4] = p[2] + 10;
printf ( "p[4] = %d\n", p[4] );
/* 動的に確保したメモリは最後に必ず、free で解放する */
free ( p );
}
return 0;
}
$ ./sample-004.exe p[2] = 1 p[4] = 11 $
Download : sample-005.c
/*
* 2021/12/10 sample-005.c
*
* 不定長の入力 (最大値が存在)
*
*/
#include <stdio.h>
/*
* 利用方法
* コンパイル
* cc -o sample-005.exe sample-005.c
* 実行
* ./sample-005.exe
*/
#define MAX_OF_INPUT_SIZE 10 /* 入力できるデータ数 */
/*
* main 関数
*/
int main ( int argc, char *argv[] ) {
int array[MAX_OF_INPUT_SIZE]; /* 整数型の配列を確保 */
int n; /* 入力される整数値の個数 */
int i;
printf ( "データ数を入力してください : " );
scanf ( "%d", &n );
if ( ( 0 <= n ) && ( n < MAX_OF_INPUT_SIZE ) ) {
for ( i = 0; i < n; i++ ) {
printf ( "%d 番目の数値を入力してください : ", i + 1 );
scanf ( "%d", &array[i] );
}
printf ( "入力された内容は以下の通りです。\n" );
for ( i = 0; i < n; i++ ) {
printf ( "%d 番目の数値は %d です。\n", i + 1, array[i] );
}
} else {
printf ( "データ数(%d)が異常です。\n", n );
}
return 0;
}
5 453 32 -239 0 294
$ ./sample-005.exe < sample-005.in データ数を入力してください : 5 1 番目の数値を入力してください : 453 2 番目の数値を入力してください : 32 3 番目の数値を入力してください : -239 4 番目の数値を入力してください : 0 5 番目の数値を入力してください : 294 入力された内容は以下の通りです。 1 番目の数値は 453 です。 2 番目の数値は 32 です。 3 番目の数値は -239 です。 4 番目の数値は 0 です。 5 番目の数値は 294 です。 $
Download : sample-006.c
/*
* 2021/12/10 sample-006.c
*
* 不定長の入力 (動的に領域を確保)
*
*/
#include <stdio.h>
#include <malloc.h>
/*
* 利用方法
* コンパイル
* cc -o sample-006.exe sample-006.c
* 実行
* ./sample-006.exe
*/
/*
* main 関数
*/
int main ( int argc, char *argv[] ) {
int *parray; /* 整数型の配列の先頭 */
int n; /* 入力される整数値の個数 */
int i;
printf ( "データ数を入力してください : " );
scanf ( "%d", &n );
if ( 0 <= n ) {
if ( ( parray = (int *)calloc( n, sizeof(int) ) ) != NULL ) {
for ( i = 0; i < n; i++ ) {
printf ( "%d 番目の数値を入力してください : ", i + 1 );
scanf ( "%d", &parray[i] );
}
printf ( "入力された内容は以下の通りです。\n" );
for ( i = 0; i < n; i++ ) {
printf ( "%d 番目の数値は %d です。\n", i + 1, parray[i] );
}
free ( parray );
} else {
printf ( "整数領域のメモリ確保に失敗しました。\n" );
}
} else {
printf ( "データ数(%d)が異常です。\n", n );
}
return 0;
}
15 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
$ ./sample-006.exe < sample-006.in データ数を入力してください : 15 1 番目の数値を入力してください : 1 2 番目の数値を入力してください : 2 3 番目の数値を入力してください : 3 4 番目の数値を入力してください : 4 5 番目の数値を入力してください : 5 6 番目の数値を入力してください : 6 7 番目の数値を入力してください : 7 8 番目の数値を入力してください : 8 9 番目の数値を入力してください : 9 10 番目の数値を入力してください : 0 11 番目の数値を入力してください : 1 12 番目の数値を入力してください : 2 13 番目の数値を入力してください : 3 14 番目の数値を入力してください : 4 15 番目の数値を入力してください : 5 入力された内容は以下の通りです。 1 番目の数値は 1 です。 2 番目の数値は 2 です。 3 番目の数値は 3 です。 4 番目の数値は 4 です。 5 番目の数値は 5 です。 6 番目の数値は 6 です。 7 番目の数値は 7 です。 8 番目の数値は 8 です。 9 番目の数値は 9 です。 10 番目の数値は 0 です。 11 番目の数値は 1 です。 12 番目の数値は 2 です。 13 番目の数値は 3 です。 14 番目の数値は 4 です。 15 番目の数値は 5 です。 $
/*
* DATE-02-QQQQ.c
*
* 動的なメモリの確保
* キーボードより正の整数を幾つか入力して、その要素が入った配列を返す
* 0 以下の整数が入力されたら、終了とする
* 配列のサイズは、正の整数の個数 + 1 とし、最後の要素には 0 を入れる
*
* 沢山の整数値を入力して、処理したい
* => 「複数個の整数値を保存できるもの」に入力データを保存する
* <= 整数型の配列を利用するのが第一候補
* 配列のサイズは、プログラムの中で指定
* => コンパイル時に確定
* <= 自動的に確保/解放するには、コンパイラにこの情報を与える必要がある
* <= 配列のサイズを超える情報は記録できない
* => 無駄に沢山(利用しないかもしれない)配列の領域を確保するの無駄
* <= 必要領域を、実行時に動的に確保する
*/
/*
* 利用方法
* コンパイル
* cc -o BASENAME.exe FILENAME
* 実行
* ./BASENAME.exe
*/
#include <stdio.h>
#include <malloc.h> /* calloc/free を利用するので必要 */
/*
* read_n_integers
* キーボードから整数値を読み込んで、
* 動的に確保した、領域に、その値を保存する
* 事前に、何個入るかはわからない
* => 読み終わった時点で、サイズが確定する事に注意
*/
int *read_n_integers( int size ) {
int num; /* キーボードから入力された数値を保存する */
int *value; /* 確保された配列の先頭要素へのポインター */
printf ( "正の整数値を入力してください(0 以下だと入力を終了します):" );
scanf ( "%d", &num );
if ( num <= 0 ) { /* 入力が全部終ったので、配列を作成する */
/* 配列のサイズは、引数で指定された個数 + 1 となる */
if ( ( value = (int *)calloc ( size + 1, sizeof ( int ) ) ) != NULL ) {
/*
calloc
malloc との違い
1. 整数型の引数が二つで、結果的に、その二つの
引数の値の積 byte の領域を確保する
2. その領域を 0 で初期化
calloc も malloc も領域が確保できた場合は、
その領域の先頭アドレス値を返す
領域の確保に失敗した場合は、
NULL
という特別なポインター値をかえす
NULL 値は適切(いみのある)な領域をささない事が保証されている
*/
/* 動的メモリは取り出せるとは限らないので、結果をチェック */
value[ size ] = 0; /* 最後の要素として 0 を代入 */
} /* else {} */ /* NULL が帰った場合は、そのまま、値として返す */
} else { /* 入力が終っていないので、更に、値を読むために再帰呼び出し */
if ( ( value = read_n_integers( size + 1 ) ) != NULL ) {
/* 結果が NULL でなければ、配列が作られている */
/* size 番目の要素を配列に記録 */
value [size] = num;
} /* else {} */ /* NULL が帰った場合は、そのまま、値として返す */
}
/* いずれの場合でも value を返す */
return value;
}
/*
* main
*/
int main ( int argc, char *argv[] ) {
int *array; /* n 個数の要素をもつ配列の先頭をもつ */
int i;
/* read_n_integers を呼び出して、n 個の整数値を入力する */
/* 引数には、入力済のデータ数を指定するので、最初は 0 を指定する */
if ( ( array = read_n_integers( 0 ) ) != NULL ) {
/* read_n_integers は、NULL を返す可能性がある */
/* 入力された要素を画面に出力 */
for ( i = 0; array[i] > 0; i++ ) {
printf ( "%d th data = %d\n", i, array[i] );
}
/* malloc/calloc で確保したメモリは、必ず free で解放する */
free ( array ); /* malloc/calloc で確保した領域を解放する */
} /* else {} */ /* NULL の場合はエラーなので、何もしない */
return 0;
}
#include <stdio.h>
int main(int argc, char *argv[]) {
int x, x2; /* 整数型の変数 x を宣言 */
/*
メモリの一部 ( sizeof(int) 個のセル ) が確保される
その 4 つセルの型は「int (整数)型」とした扱う
*/
double y, y2; /* 浮動小数数型の変数 y を宣言 */
/*
メモリの一部 ( sizeof(double) 個のセル ) が確保される
その 8 つセルの型は「double(浮動小数点数)型」とした扱う
*/
/*
+-----------+
0x7fffc63c4a60 x | | int 型 : 4 byte 分
+-----------+
| |
+-----------+
| |
+-----------+
| |
+-----------+
0x7fffc63c4a64 x2 | | int 型 : 4 byte 分
+-----------+
| |
+-----------+
| |
+-----------+
| |
+-----------+
0x7fffc63c4a68 y | | double 型 : 8 byte
+-----------+
| |
+-----------+
| |
+-----------+
| |
+-----------+
| |
+-----------+
| |
+-----------+
| |
+-----------+
| |
+-----------+
0x7fffc63c4a70 y2 | | double 型 : 8 byte
+-----------+
| |
+-----------+
| |
+-----------+
| |
+-----------+
| |
+-----------+
| |
+-----------+
| |
+-----------+
| |
+-----------+
*/
x = x2; /* メモリのコピーをしている */
/* コピーされるセルの個数は 4 byte */
y = y2; /* メモリのコピーをしている */
/* コピーされるセルの個数は 8 byte */
/* 同じ代入文なのに、やっている事が違う */
/* 違い => 代入文の操作対象となる
変数 (x,x1), (y,y1) 型が異なる */
/* この型の違いをコンパイラが判断して、
実施にプログラムが実行される
実行時の命令が変化する */
/* C 言語上の表現では同じ命令なのに、
型によって、異なる振る舞い */
printf ( "変数 x のポインター値(アドレス値) = %p\n", &x );
printf ( "変数 x2 のポインター値(アドレス値) = %p\n", &x2 );
printf ( "変数 y のポインター値(アドレス値) = %p\n", &y );
printf ( "変数 y2 のポインター値(アドレス値) = %p\n", &y2 );
return 0;
}
#include <stdio.h>
int main(int argc, char *argv[]) {
int x; /* メモリ上に、 4 byte 分の領域が確保される */
/* => アドレス値がきまる */
printf ( "&x (変数 x のポインタ値[アドレス値]) = %p\n", &x );
/*
変数 x 自身の値は、初期化を行ってないので、
ここでは不確定
アドレス値は、宣言(有効になった時点)と同時に利用可能
有効になった時点 : 実行時に変数が有効になるのと同時定まる
*/
x = 1234;
printf ( "x = %d\n", x );
printf ( "&x (変数 x のポインタ値[アドレス値]) = %p\n", &x );
/* アドレス値と、変数値は独立 */
x = 987;
printf ( "x = %d\n", x );
printf ( "&x (変数 x のポインタ値[アドレス値]) = %p\n", &x );
/* アドレス値と、変数値は独立 */
/* ポインター値の操作 */
printf ( "&x = %p, &x + 1 = %p\n", &x, &x + 1 );
/*
ポインター値に、+1 をすると、新しいポインター値が作られる
が、そのアドレス値は、
sizeof(型)
だけ増える
*/
/* ポインター値の操作 (アドレス値の変更 : 整数値の加減) */
printf ( "(&x + 1) + 1 = %p\n", (&x + 1) + 1 );
return 0;
}
#include <stdio.h>
/*
\sum_{i=1}^n \frac{1}{i}
=
1/1 + 1/2 + 1/3 + ... + 1/n
-> 無限 ( n -> 無限 )
*/
int main(int argc, char *argv[]) {
double sum = 0.0;
int i;
int n;
printf ( "n を入力してください : " );
scanf ( "%d", &n );
for ( i = 1; i <= n; i++ ) {
/*
sum = sum + 1/i; X
*/
/*
1/i は、i が整数型なので、
整数割り算になる
i > 1 の時に、 1/i は 0
=> +0 しても結果がかわらない
*/
/*
1/i だとまずいのでどうするか ?
キャストを行い i の値の型を、浮動小数点数型にする
*/
sum = sum + 1/(double)i;
/*
キャスト「(double)」により、
整数型変数 i が持つ整数値が、浮動小数点数の型を持つようように変更され、
割り算が、浮動小数点数の割り算になる
(この時に分子の 1[整数値を持つ]も浮動小数点数に昇格する
*/
}
printf ( "sum of 1/1 + 1/2 + .. + 1/%d = %f\n", n, sum );
return 0;
}
#include <stdio.h>
int main(int argc, char *argv[]) {
int i;
i = 0x12345678; /* 16 進数で、整数値を表現 */
/* 「0x」で始める事による整数を 16 進数で表現可能 */
/* 計算は 10 進数で行う */
printf ( "i=%x\n", i );
/*
整数値を 16 進数表現出力する
*/
printf ( "100(十進)=%x(十六進)\n", 100 );
printf ( "100(十六進)=%d(十進)\n", 0x100 );
/*
i +-------+
| |
+-------+
| |
+-------+
| |
+-------+
| |
+-------+
i = 0x12345678;
i +-------+
|0x78 | <- (char *)(&i)
+-------+
|0x56 | <- ((char *)(&i)+1)
+-------+
| |
+-------+
| |
+-------+
*/
printf ( "((char *)&i)[0] = %x\n", ((char *)&i)[0] );
/*
((char *)&i)[0] <-> *(((char *)&i)+0)
<-> *(((char *)&i))
* を使ってデータを取り出す時に、
char 型へのポインタ値(として、キャストされている)なので
1 byte ( sizeof(char) ) だけ取り出される
*/
printf ( "((char *)&i)[1] = %x\n", ((char *)&i)[1] );
printf ( "((char *)&i)[2] = %x\n", ((char *)&i)[2] );
printf ( "((char *)&i)[3] = %x\n", ((char *)&i)[3] );
/*
int 型の変数(i)に対応したセル(4つ[sizeof(int)])を
キャストをつかって、別々にあつかう事ができる
*/
return 0;
}
#include <stdio.h>
void rec( int n ) {
int i = n; /* main 中の変数 i とは別物 */
if ( n == 0 ) {
printf ( "&i = %p, n = %d\n", &i, n );
} else {
printf ( "&i = %p, n = %d\n", &i, n ); /* [c] */
rec ( n - 1 );
printf ( "&i = %p, n = %d\n", &i, n ); /* [d] */
}
}
void rec2( int n ) {
int i = n; /* main 中の変数 i とは別物 */
int j; /* j の値は代入していない */
if ( n == 0 ) {
printf ( "&i = %p, n = %d\n", &i, n );
printf ( "&j = %p, n = %d\n", &j, n );
} else {
printf ( "&i = %p, n = %d\n", &i, n ); /* [c] */
printf ( "&j = %p, n = %d, j = %d\n", &j, n, j ); /* [c] */
rec ( n - 1 );
printf ( "&i = %p, n = %d\n", &i, n ); /* [d] */
printf ( "&j = %p, n = %d\n", &j, n ); /* [d] */
}
}
int main(int argc, char *argv[] ) {
int i = 3; /* 変数 i には、0x7fffdd61c744 */
printf ( "&i = %p\n", &i );/* [a] 変数 i には、0x7fffdd61c744 */
rec ( i );
printf ( "&i = %p\n", &i );/* [b] */
rec2 ( i );
printf ( "&i = %p\n", &i );/* [b] */
return 0;
}
/*
&i = 0x7fffdd61c744 [a]
&i = 0x7fffdd61c714, n = 3 [c:n=3]
&i = 0x7fffdd61c6e4, n = 2 [c:n=2]
&i = 0x7fffdd61c6b4, n = 1
&i = 0x7fffdd61c684, n = 0
&i = 0x7fffdd61c6b4, n = 1
&i = 0x7fffdd61c6e4, n = 2 [d:n=2]
&i = 0x7fffdd61c714, n = 3 [d:n=3]
&i = 0x7fffdd61c744 [b]
*/
/*
&i = 0x7ffff3dd1434 [a]
&i = 0x7ffff3dd1404, n = 3 rec の i
&i = 0x7ffff3dd13d4, n = 2
&i = 0x7ffff3dd13a4, n = 1
&i = 0x7ffff3dd1374, n = 0
&i = 0x7ffff3dd13a4, n = 1
&i = 0x7ffff3dd13d4, n = 2
&i = 0x7ffff3dd1404, n = 3
&i = 0x7ffff3dd1434 [b]
&i = 0x7ffff3dd1400, n = 3
&j = 0x7ffff3dd1404, n = 3 rec2 の j
&i = 0x7ffff3dd13d4, n = 2
&i = 0x7ffff3dd13a4, n = 1
&i = 0x7ffff3dd1374, n = 0
&i = 0x7ffff3dd13a4, n = 1
&i = 0x7ffff3dd13d4, n = 2
&i = 0x7ffff3dd1400, n = 3
&j = 0x7ffff3dd1404, n = 3
&i = 0x7ffff3dd1434
*/
/*
&i = 0x7fffd62466f4
&i = 0x7fffd62466c4, n = 3
&i = 0x7fffd6246694, n = 2
&i = 0x7fffd6246664, n = 1
&i = 0x7fffd6246634, n = 0
&i = 0x7fffd6246664, n = 1
&i = 0x7fffd6246694, n = 2
&i = 0x7fffd62466c4, n = 3
&i = 0x7fffd62466f4
&i = 0x7fffd62466c0, n = 3
&j = 0x7fffd62466c4, n = 3, j = 3 <= rec の i 値が表示
&i = 0x7fffd6246694, n = 2
&i = 0x7fffd6246664, n = 1
&i = 0x7fffd6246634, n = 0
&i = 0x7fffd6246664, n = 1
&i = 0x7fffd6246694, n = 2
&i = 0x7fffd62466c0, n = 3
&j = 0x7fffd62466c4, n = 3
&i = 0x7fffd62466f4
=> rec で利用された変数領域が、
rec2 で、再利用されている ( 同じアドレスが使われている )
=> 変数は、自動的に割り当てられるだけでなく、
自動的に、解放されている
*/
#include <stdio.h>
#include <malloc.h> /* malloc, free のために必要 */
int *get_array(int n) {
int *ip; /* (int *) 型の変数 ip の宣言 */
/* 変数 ip には、 (int *) 型の値(ポインタ値が入る) */
/* ポインタ値を持つ変数の事を「ポインタ」と呼ぶ事が多い */
int i;
ip = (int *)malloc ( n * sizeof(int) );
/*
malloc : 指定されたサイズ(byte 数) の領域を
確保し、その先頭のアドレス値を(void *)型のポインタ値として返す
!! 「(void *)」型の値は、ポインタ値(アドレス値)だが、
!! そのアドレスにあるセルの型は(形式的には)「void」である
!! => これは、「型」が不明(その値を直接操作できない)
!! <= キャストを利用して、操作できるようにする
int ip[n]; の宣言と同じ状況が生まれる
*/
for ( i = 0; i < n; i++ ) {
ip[i] = i * i; /*
*(ip + i) : ip が (int *) 型
ip + i は アドレス値が ip + (i * sizeof(int))
型情報は (int *) 型
これに * をつければ int 型の変数
*/
}
return ip; /* ポインター値を返す */
}
int main(int argc, char *argv[]) {
int *p;
int i;
p = get_array ( 10 ); /* 領域が確保 */
for ( i = 0; i < 10; i++ ) {
printf ( "p[%d]=%d\n", i, p[i] );
/*
関数 get_array の中で(malloc で)確保した領域
が、関数の終了後も利用できる
=> 利用可能な範囲
(プログラムが終わるか..) その領域を解放するまで
解放するには、
その領域を関数 free に指定すればよい
*/
}
free ( ip );
/*
関数 malloc が返した値(ポインタ値)と同じ「アドレス値」
を free に渡す必要がある
free ( ip + 5 );
気持ち : get_arry で整数値 10 個分確保
そのうちの後ろの 5 個分だけ返す
<= できない
領域の確保と、解放は、1:1 にする必要がある
もし、沢山確保して、一部開放するような事がしたいならば、
はじめから、小さな一部を沢山確保して、
不要なものをその確保した単位で解放する
malloc で確保した領域は、
不要になった時点で free する必要がある
そうしないと、
空きがなくなってしまう ( ゴミだらけになる )
*/
return 0;
}
2021/12/17 ソフトウェア概論
[前回の復習]
前回の内容 : データ構造 (6)
データ構造
データの組み合わせる仕組み
データに構造を持たせ、複数の要素を組み合わせて、
目的のデータを表現する仕組み
組み合わせ方法
データ型とデータ型の直積を取る
構造体
複数のデータ型を組み合わせる
配列
同じ型のデータ型を複数並べる
C 言語でのデータ構造
データの組み合わせではなく、変数の組み合わせを作る
変数にそれぞれ、データを記録させる事により、
その(構造化された)変数に入っているデータが、
基本となるデータを組み合わせた複雑なデータとして表現される
例:
二次元平面上の点
=> x 座標と y 座標 ( それぞれ浮動小数点数 ) の対
点 P の表現 ( 1.0, -2.4 )
点 Q の表現 ( 0.0, 0.0 )
...
実数 x 実数 ( 二つの実数の直積空間 ) の要素
# C 言語では、この「実数値の対」を直接表現する事ができない
# => 実数の値の対を保存できるような変数を作る
# この変数に値を入れることにより、その変数の値として、
# 目的の表現を得る
struct { 構造体の宣言
double x; x, y 座標のデータを保存する変数
double y;
} P, Q; x,y の変数の対で、それぞれが座標値を保持する事により
(間接的に..) 座標値の対を表現する
点 P の表現 ( 1.0, -2.4 )
P.x = 1.0;
P.y = -2.4;
点 Q の表現 ( 0.0, 0.0 )
Q.x = 0.0;
Q.y = 0.0;
現実の世界(データ型)
点の表現 実数 x 実数
+----- (1.0, -2.4) 1.0 -----------+
| -2.4-+ |
| (0.0, 0.0) 0.0 | |
| 0.0 | |
| | |
| | |
| 構造体(struct) | |
+-------> P, P.x <-------|---+
変数 P.y<+
Q Q.x
Q.y
C 言語では、データ型 <=> 変数の組み合わせの仕方
配列名の正体
「ポインター値」を持つ
データ型 => 変数の組み合わせ
変数 => メモリ(セルの集まり)の一部
セル自身は、1 byte ( = 8bit [2^8] : 0 ? 255 ) 記録する機能
セルは「記憶」しかいない
そこに
何が記録されていて、
それが、
どのような表現なのか、
どのように扱われるか
=> セルには情報がない
C のコンパイラが、
その変数 ( セルに対応 ) の「型情報」として
知っていて、そのように扱う
変数[メモリ]をどのように扱うかは、[メモリに対応する]変数の型できまる
変数
=> メモリ
先頭番地[メモリのセルに一つずつ振られている番号]をもっている
----+ アドレス値 +--+
セルの個数 ( sizeof ) ----+ (実行時) |
x | +-- ポインター値
型 +-- 型情報 -----+
セルに保存する表現の方法 | (コンパイル時)
セルの値をどのように扱うか ----+
ポインタ値のアドレス値を表示するには、
printf の "%p" 書式を利用する
変数には、必ず、対応するメモリ(セルの並び)があるので、
変数から、対応するセルの並びのポインター値を取るには、
変数名の前に「&」を付けるとよい
[ポインター値とメモリモデル]
ポインター値
+-- アドレス値 ( %p ) / メモリ上の番地
| 実行時の情報
+-- 型情報
+--- サイズ
+--- 演算の仕方
コンパイル時の情報
アドレス値と型 (sizeof 演算子)
セルの属性
アドレス値 : 先頭セルの番地
sizeof : セルの個数 <-> 連続したセルの個数
「&」演算子
変数 ( セルの並びに対応 ) からポインター値
アドレス値 : 変数に対応するセルの番地
型情報 : 変数型
T 型の変数から取り出されるポインター値の型
(T *) で表現する
=> ポインター型
例:
char * => char 型の変数から作られるポインター
char 型へのポインター型
int * => int 型の変数から作られるポインター
int 型へのポインタ型
!! ポインター値が持つアドレス値は、
!! 対応する変数の(メモリ上の)位置を表す
!! 「位置」を表す気持ちが「へ」で表現されている
配列名
配列を宣言する時に作られる
int a[3];
+-------+
a ->a[0]| |
+-------+
| |
+-------+
| |
+-------+
| |
+-------+
a[1]| |
+-------+
| |
+-------+
| |
+-------+
| |
+-------+
a[2]| |
+-------+
| |
+-------+
| |
+-------+
| |
+-------+
配列名が表現する(ポインタ)値は、
配列の先頭の要素(変数 a[0]) へのポインタ値に
a = &(a[0])
より一般的には、
配列名 <-> &(配列名[0])
という関係が成立する
[本日の内容]
ポインター値の操作
ポインター値
+-- アドレス値
+-- 型情報
ポインター値に対する、整数の加減算は、
=> ポインター値のアドレス値を変更する操作に相当する
型情報の変更
=> キャストを利用する
(型)キャスト(演算子)
C 言語では、値に対し、その値(型情報をもっている)を、別の型として扱うように指定できる
(値に対する)型の(強制的な..)指定の事を、
(型)キャスト
# 本当なら、型は自動的に決まる
# => 普通は、値に対して型を指定する必要はない
# 場合によって、(自然に[コンパイラが判断して..]決まる)型が不適切な場合
# 適切な型情報を(コンパイラに)指定して、適切な処理を行う必要がある場合がある
例:
割り算の計算
整数同士の割り算は、整数値になる
1/2 -> 0
0.5 にしたい場合は、1.0/2.0 の形で浮動小数点数の割り算にする必要がある
キャストの仕方(キャストの表現)
値(をもとめる式)の前に、「(型表現)」を書く事により、
値の型を強制的に「『型表現』が表す『型』」に、
する(キャスト)
1/((double)2)
=> 1/2.0 ( 演算の一方が浮動小数点型だと、他方も自動的に浮動小数点数型に変わる(型の昇格))
=> 1.0/2.0
=> 0.5
ポインター値のもつ型情報を変更するには、
キャストを利用する
すでに、適切な型を持つポインター値にたいして、
キャストする事に意味があるか ?
=> 多くの場合意味がない ( できる事をしっている事は重要 )
<= もともと、適切な型を持っていない場合に役立つ
[動的なメモリの確保]
ローカル変数の宣言
=> メモリの一部が自動的に確保され、利用可能
関数(宣言されたブロック)が終了すると、
自動的に無効になる (auto 変数)
変数とメモリの対応関係は、
その変数が宣言されている(ブロックを含む)関数が、
よびだされた時
ローカル変数は、自動的に領域が確保され、
自動的に、解放される
=> メモリ領域という資源を、効率的に活用(無駄にしない)
=>
その変数に保存されている値は、
その変数が有効な間しか、利用できない
# 変数に割り当てられた領域が解放されると、
# その領域に記録されている値(変数の値)は、
# 保証されない ( そもそも普通には参照できない )
# (再利用されてしまうと、値が書き変わる)
=> もし、
ある値を(保存するメモリ領域)を、
関数をまたいで、利用したい場合
=> ローカル変数に保存するのでは、問題がある
=> ローカル変数を経由せず、
直接、(情報を保存する)メモリを確保できると便利
=> malloc 関数の役割
課題プログラム内の「/*名前:ここ*/」の部分を書き換え「/*この部分を完成させなさい*/」の部分にプログラムを追加して、プログラムを完成させます。
Download : 20211217-01.c
/*
* 20211217-01-QQQQ.c
*
* 三角形の形をした配列
*
*/
/*
* 利用方法
* コンパイル
* 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;
}
$ ./20211217-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 $