- 20161125-01-9999.c (SJIS)
20161125-01-9999.c
/*
* 課題 CNAME-01
*
* 2016/11/25 FILENAME
*
* 動的なメモリの確保
* キーボードより正の整数を幾つか入力して、その要素が入った配列を返す
* 0 以下の整数が入力されたら、終了とする
* 配列のサイズは、正の整数の個数 + 1 とし、最後の要素には 0 を入れる
*/
/*
例題とよくにている
入力するデータの個数が、予めわかっていない
動的な領域確保を利用する
例題では、最初に、個数を指定している
その個数にしたがって、領域を確保する
データの入力の前に、領域が確保できる
データは、確保した領域に保存可能
この問題では、
最後を表すデータを、他のデータの後に入力する
最後のデータが入れば、個数も確定する
再帰を利用することによって、
データを一時的に保存する領域を、扱うことができる
*/
/*
* 利用方法
* コンパイル
* cc -I ~/c/include -o BASENAME.exe FILENAME
* 実行
* ./BASENAME.exe
*/
#include <stdio.h>
#include <malloc.h> /* calloc/free を利用するので必要 */
/*
* read_n_integers ( int size )
* size : すでに入力されているデータの個数
*/
int *read_n_integers( int size ) {
int num; /* キーボードから入力された数値を保存する */
int *value; /* 確保された配列の先頭要素へのポインター */
printf ( "正の整数値を入力してください(0 以下だと入力を終了します):" );
scanf ( "%d", &num ); /* 入力されたデータは、変数 num に保存される */
if ( num <= 0 ) { /* 入力が全部終ったので、配列を作成する */
/* 配列のサイズは、引数で指定された個数 + 1 となる */
if ( ( value = (int *)calloc ( size + 1, sizeof ( int ) ) ) != 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; /* value には NULL がはいっているかもしれないが
その場合も適切(領域が確保できなかったという情報を担う)
な値
*/
}
/*
* 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 で解放する */
/*
** この部分を完成させなさい
*/
} /* else {} */ /* NULL の場合はエラーなので、何もしない */
return 0;
}
- p-001.c (SJIS)
p-001.c
#include <stdio.h>
void sub(int *ip) {
/*
仮引数変数 ip は、int 型へのポインタ型
ポインタ型の値の実体
アドレス値+型情報
型の情報は、渡す側と渡される側が、「共通にする」と約束して
しかも、それを守ることによって実現される
型が合わない(約束を守らない) -> コンパイラがそれをチェック
*/
printf ( "ip[0]=%d\n", ip[0] ); /* 1 になるはず */
printf ( "ip=%p\n", ip ); /* ary[0] の場所 */
}
int main(int argc, char *argv[]) {
int ary[10];
ary[0] = 1;
printf ( "ary[0]=%d\n", ary[0] ); /* 1 になるはず */
printf ( "ary=%p\n", ary ); /* ary[0] の場所 */
/* ここには、「int 型」,
配列サイズが 10 という情報はない */
sub ( ary ); /* 値としてはアドレス値のみ渡される */
return 0;
}
- p-002-01.c (SJIS)
p-002-01.c
#include <stdio.h>
#include "p-002-01.h" /* ここに、型に関する情報(契約書)を記述 */
void sub(double *ip) {
/*
仮引数変数 ip は、int 型へのポインタ型
ポインタ型の値の実体
アドレス値+型情報
型の情報は、渡す側と渡される側が、「共通にする」と約束する
その約束の内容が、ヘッダーファイルに記述されている
*/
printf ( "ip[0]=%d\n", ip[0] ); /* 1 になるはず */
printf ( "ip=%p\n", ip ); /* ary[0] の場所 */
}
- p-002.c (SJIS)
p-002.c
#include <stdio.h>
#include "p-002-01.h"
int main(int argc, char *argv[]) {
int ary[10];
ary[0] = 1;
printf ( "ary[0]=%d\n", ary[0] ); /* 1 になるはず */
printf ( "ary=%p\n", ary ); /* ary[0] の場所 */
/* ここには、「int 型」,
配列サイズが 10 という情報はない */
sub ( ary ); /* 値としてはアドレス値のみ渡される */
return 0;
}
- p-003.c (SJIS)
p-003.c
#include <stdio.h>
void sub( int *ip ) {
/*
指定されたポインタ値を利用して、
そのポインタ値を持つ変数の内容を 10 倍する
*/
*ip = *ip * 10;
/*
*ip <-> *(&i) <-> i
*/
}
int main(int argc, char *argv[]) {
int i;
i = 123;
printf ( "i=%d\n", i ); /* 123 */
sub( &i ); /* 変数 i へのポインタ値 */
/* 結果として、 i 自身の値が 10 倍される */
printf ( "i=%d\n", i ); /* 1230 となる */
return 0;
}
- p-004.c (SJIS)
p-004.c
#include <stdio.h>
int main(int argc, char *argv[]) {
int i;
char str[10]; /* 長さが 9 までの文字列を収める */
printf ( "整数値 = %d\n", 123 );
printf ( "文字列 = %s\n", "abc" );
scanf ( "%d", &i );
/*
scanf に 変数 i へのポインタ値を渡す
型を示すために、書式 "%d"
同じように %lf, %c がつかえる
-> %s の話は抜かした
*/
scanf ( "%9s", str ); /* str はすでに、ポインタ値で、
これに & を付けるのは誤り(だった) */
/* これに 9 文字よりの長いデータをいれた
らどうなるか ??? */
fgets ( str, 10, stdin );
/* 標準入出力から、長さ 9 までの文字列を入力する */
printf ( "i = %d\n, str = %s\n", i, str );
return 0;
}
- p-005.c (SJIS)
p-005.c
#include <stdio.h>
void sub( int *ip ) {
printf ( "*ip = %d\n", *ip );
}
int main(int argc, char *argv[]) {
int *p; /* int ???|?C???^?^??|?C???^(???) */
int i; /* int ?^???? */
int j; /* int ?^???? */
p = &i; /* (?|?C???^)??? p ??A??? i ???w???????????? */
i = 1;
printf ( "i = %d\n", i );
printf ( "*p = %d\n", *p ); /* ????????Ap == &i <-> *p == *&i == i */
j = 2;
p = &j; /* (?|?C???^)??? p ??A??? j ???w???????????? */
printf ( "j = %d\n", j );
printf ( "*p = %d\n", *p ); /* ????????Ap == &j <-> *p == *&j == j */
sub ( &i ); /* i ??l??\?? */
sub ( &j ); /* j ??l??\?? */
/* ?P??l??p?????????????A?|?C???^??p??????A
?]????????(?l????????n??????j
?????l???X??????????
?l????????T?C?Y???????????A?R?s?[??????????????
????????????????????A?|?C???^?l??n??
*/
/*
a = tentimes(a);
a ??l?? 10 ?{?????l??????????A?????
??? a ????
*/
/*
tentimes(&a);
?|?C???^?l???n??????A???????A????l???X???鎖???????
???????u?P??????v?????A??????@????????????
*/
return 0;
}
/*
void func ( const int *p ) {
p ???w?????l???X?????
*/
- p-006.c (SJIS)
p-006.c
#include <stdio.h>
/*
いくつかの整数値を入力し、その値を逆順に出す
いくつの整数値をいれるかは、最初に、その個数を入力する
*/
int main(int argc, char *argv[] ) {
int input_data[10]; /* 入力されるデータ数のサイズ */
int input_size; /* いくつのデータを入力するか */
int i; /* 配列の要素の添え字 */
printf ( "いくつのデータを入力しますか : " );
scanf ( "%d", &input_size ); /* データ数を入力 */
/* 本当は
int input_data[input_size];
とできたらうれしいが、できない
配列は、「サイズを変更できない」という制約
# この制約を利用して、効率と自動化が可能になっている
# 対価は柔軟性
*/
/* 本当は、input_size がてきせつかを判断する必要があるが
サボる
input_size として 10 より大きな数が指定されたら ??
配列は、最初[プログラム作成時]にそのサイズを宣言する
(サイズを固定にする)必要がある
*/
/* input_size 個のデータ入力 */
for ( i = 0; i < input_size; i++ ) {
printf ( "%d 番目のデータを入力してください : ", i + 1 );
scanf ( "%d", &input_data[i] ); /* &input_data[i] = input_data + i */
}
/* input_size 個のデータを逆順に出す */
for ( i = input_size-1; 0 <= i ; i-- ) {
printf ( "%d\n", input_data[i] );
}
/*
for ( i = 0; i < input_size; i++ ) {
printf ( "%d\n", input_data[input_size-1-i] );
}
*/
return 0;
}
- p-007.c (SJIS)
p-007.c
#include <stdio.h>
#include <malloc.h> /* free/calloc の宣言がはいっています */
/*
void *calloc( 配列のサイズ, 配列の要素のサイズ )
その配列と同じ領域を動的に確保して、
その領域の先頭のアドレス値を帰す
取れない場合は NULL が返る
int free( 確保した領域のアドレス値 )
指定された領域を開放する
開放された領域は、あとで再利用される
不要になった領域は、必ず、free する必要がある
*/
/*
いくつかの整数値を入力し、その値を逆順に出す
いくつの整数値をいれるかは、最初に、その個数を入力する
*/
int main(int argc, char *argv[] ) {
int *input_data; /* 入力されるデータを収める領域の先頭へのポインタ */
int input_size; /* いくつのデータを入力するか */
int i; /* 配列の要素の添え字 */
printf ( "いくつのデータを入力しますか : " );
scanf ( "%d", &input_size ); /* データ数を入力 */
/* 本当は、input_size がてきせつかを判断する必要があるが
サボる
*/
input_data = calloc( input_size, sizeof(int) );
/*
int [input_size] だけの領域を動的に確保し、
その領域へのポインタ値を返す
領域は、メモリ上に用意されるが、それがどこかは
その時にならないとわからない
名前がつけられないので、ポインタ値を経由してしか扱えない
*/
if ( input_data != NULL ) { /* 領域が確保できたら */
/* input_size 個のデータ入力 */
for ( i = 0; i < input_size; i++ ) {
printf ( "%d 番目のデータを入力してください : ", i + 1 );
scanf ( "%d", &input_data[i] ); /* &input_data[i] = input_data + i */
}
/* input_size 個のデータを逆順に出す */
for ( i = input_size-1; 0 <= i ; i-- ) {
printf ( "%d\n", input_data[i] );
}
free ( input_data ); /* その領域を開放する */
}
return 0;
}
/*
配列 : 静的な領域
関数の先頭の位置に固定サイズで宣言する
サイズが固定なので、領域の確保の効率は良い
「配列名」が利用できる
関数の中で、自由に利用できる
関数が終了すると、かってに回収される
動的領域
領域の確保は、calloc 関数を利用して、必要なときに行う
管理するために、ポインタ(ポインタ型の変数)が必要
# 配列名の変わりに利用する
利用がおわったら、その時点で free する
好きなときに free できるので自由度が高い (権利)
free をする必要がある(義務)
*/