Download : sample-001.c
/*
* 2017/11/24 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
/*
* 2017/11/24 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
/*
* 2017/11/24 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
/*
* 2017/11/24 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
/*
* 2017/11/24 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
/*
* 2017/11/24 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 です。 $
#include <stdio.h>
/*
* 「&」と「*」の関係の復習
*/
int main(int argc, char *argv[]) {
char va;
char vb;
/*
+-----------+
0 | |
+-----------+
...
+-----------+
va | | <- このアドレスは事前にはわからない
+-----------+
vb | |
+-----------+
| |
+-----------+
*/
/* 変数(名)には、アドレス(値)が対応している */
printf ( "&va = %p\n", &va );
/* 変数 va に対応した、アドレス値を表示する */
/* %p は、アドレス値を表示する時の書式指定 */
printf ( "&vb = %p\n", &vb );
/* 変数 vb に対応した、アドレス値 -> &va とは異なる */
va = 'A';
vb = 'b';
/*
+-----------+
0 | |
+-----------+
...
+-----------+
va | 'A' |
+-----------+
vb | 'b' |
+-----------+
| |
+-----------+
*/
printf ( "va = %c\n", va );
/* 変数 va の値(文字型なので、文字コードに対応した文字を表示する */
printf ( "vb = %c\n", vb );
/* 変数 va の値(文字型なので、文字コードに対応した文字を表示する */
printf ( "*&va = %c\n", *&va );
/* & でアドレスが取り出されるが、* で元の「変数」に戻る */
printf ( "*&vb = %c\n", *&vb );
/* & でアドレスが取り出されるが、* で元の「変数」に戻る */
*&va = 'Z'; /* アドレス経由で、代入も可能 */
/* *&va <==> va */
/* 「*&」は「ないのと同じ」=「恒等写像」
* と & が逆写像になっている
=> &* も「恒等写像」
*/
/*
+-----------+
0 | |
+-----------+
...
+-----------+
va | 'Z' | <- アドレスがわかれば、アドレスを使って
+-----------+ 値の変更が可能
vb | 'b' |
+-----------+
| |
+-----------+
*/
printf ( "va = %c\n", va );
printf ( "vb = %c\n", vb );
printf ( "&*&va = %p\n", &*&va );
printf ( "&*&vb = %p\n", &*&vb );
printf ( "&va = %p\n", &va );
printf ( "&va + 1 = %p\n", &va + 1 ); /* アドレス値も計算可能 */
printf ( "&vb = %p\n", &vb );
printf ( "va = %c\n", va );
printf ( "*(&va + 1) = %c\n", *(&va + 1) );
printf ( "vb = %c\n", vb );
*(&va + 1) = 'X';
printf ( "va = %c\n", va );
printf ( "*(&va + 1) = %c\n", *(&va + 1) );
printf ( "vb = %c\n", vb );
return 0;
}
#include <stdio.h>
void sub1 ( char vc ) {
/* 仮引数変数 vc には、実引数で指定された値が入る */
/* 一回目は 'A' が、二回目は 'b' が入っている */
printf ( "vc = %c\n", vc );
}
void sub2 ( char *pc ) {
/* 仮引数変数 pc には、実引数で指定された値が入る */
/* 初回は、 &va が、二回目は &vb が渡される */
/* 「char *」
-> 引数は無条件
-> 文字列はこのこの形
-> 一次元配列の配列名
-> アドレス値
*/
printf ( "*pc = %c\n", *pc );
/* *pc -> 一回目は pc <- &va ===> *pc == *(&va) == va */
/* *pc -> 二回目は pc <- &vb ===> *pc == *(&vb) == vb */
}
void sub3 ( char *pc ) {
*pc = *pc + 1; /* 一つ大きくする */
/* この機能を利用して、scanf が変数に値を入力[代入]している */
}
void sub4 ( char *pc ) {
*(pc+1) = 'X'; /* 引数のアドレスの次の値を変更 */
/*
*(pc+1) = pc[1]
*/
}
int main(int argc, char *argv[] ) {
char va = 'A';
char vb = 'b';
sub1 ( va ); /* sub1 の実引数には、変数 va の値 'A' が渡される */
sub1 ( vb );
sub2 ( &va ); /* sub2 の実引数には、変数 va のアドレス値が渡される */
sub2 ( &vb );
/* アドレス値は、「値」なので、関数に渡せる */
printf ( "sub3 の前 : va = %c\n", va );
sub3 ( &va ); /* 変数 va の値が更新される */
printf ( "sub3 の後 : va = %c\n", va );
printf ( "sub4 の前 : va = %c\n", va );
printf ( "sub4 の前 : vb = %c\n", vb );
sub4 ( &va ); /* 変数 vb の値が更新される */
printf ( "sub4 の後 : va = %c\n", va );
printf ( "sub4 の後 : vb = %c\n", vb );
return 0;
}
/*
値 アドレス
main
+------------------------------ &va
va -> 'A' ---> 'A' |
vb -> 'b' | |
| |
| |変数名をアドレスに変換
sub1 <-------------- vc | 値だから渡せる
vc ---> 'A' | アドレスなので変数の性質
|
sub2 <--------------------------------- pc
*pc --> 'A'
*/
#include <stdio.h>
void sub1 ( char vc ) {
/* 仮引数変数 vc には、実引数で指定された値が入る */
/* 一回目は 'A' が、二回目は 'b' が入っている */
printf ( "vc = %c\n", vc );
}
void sub2 ( char *pc ) {
/* 仮引数変数 pc には、実引数で指定された値が入る */
/* 初回は、 &va が、二回目は &vb が渡される */
/* 「char *」
-> 引数は無条件
-> 文字列はこのこの形
-> 一次元配列の配列名
-> アドレス値
*/
printf ( "*pc = %c\n", *pc );
/* *pc -> 一回目は pc <- &va ===> *pc == *(&va) == va */
/* *pc -> 二回目は pc <- &vb ===> *pc == *(&vb) == vb */
}
void sub3 ( char *pc ) {
*pc = *pc + 1; /* 一つ大きくする */
/* この機能を利用して、scanf が変数に値を入力[代入]している */
}
void sub4 ( char *pc ) {
*(pc+1) = 'X'; /* 引数のアドレスの次の値を変更 */
/*
*(pc+1) = pc[1]
*/
}
#define va v[0]
#define vb v[1]
int main(int argc, char *argv[] ) {
char v[2] = { 'A', 'b' };
sub1 ( va ); /* sub1 の実引数には、変数 va の値 'A' が渡される */
sub1 ( vb );
sub2 ( &va ); /* sub2 の実引数には、変数 va のアドレス値が渡される */
sub2 ( &vb );
/* アドレス値は、「値」なので、関数に渡せる */
printf ( "sub3 の前 : v[0] = %c\n", va );
sub3 ( &va ); /* 変数 va の値が更新される */
printf ( "sub3 の後 : v[0]= %c\n", va );
printf ( "sub4 の前 : v[0] = %c\n", va );
printf ( "sub4 の前 : v[1] = %c\n", vb );
sub4 ( &va ); /* 変数 vb の値が更新される */
/*
&va == &v[0] == &(*(v+0)) == &*v == v
*/
printf ( "sub4 の後 : v[0] = %c\n", va );
printf ( "sub4 の後 : v[1] = %c\n", vb );
return 0;
}
/*
値 アドレス
main
+------------------------------ &va
va -> 'A' ---> 'A' |
vb -> 'b' | |
| |
| |変数名をアドレスに変換
sub1 <-------------- vc | 値だから渡せる
vc ---> 'A' | アドレスなので変数の性質
|
sub2 <--------------------------------- pc
*pc --> 'A'
*/
#include <stdio.h>
int main(int argc, char *argv[] ) {
char ca[2];
int ia[2];
printf ( "ca = %p\n", ca ); /* 配列 ca の最初の要素のアドレス */
printf ( "&ca[0] = %p\n", &ca[0] );
printf ( "&ca[1] = %p\n", &ca[1] );
printf ( "ca + 1 = %p\n", ca + 1 ); /* 配列 ca の最初の要素のアドレス */
/* ca[1] <==> *(ca + 1) */
/* &ca[1] <==> &*(ca + 1) <==> ca + 1 */
/*
+-----------+
ia[0] | | int 型は 4 byte
+-----------+
| |
+-----------+
| |
+-----------+
| |
+-----------+
ia[1] | | 次の要素は アドレスが 4 つ先
+-----------+
| |
*/
printf ( "ia = %p\n", ia ); /* 配列 ia の最初の要素のアドレス */
printf ( "&ia[0] = %p\n", &ia[0] );
printf ( "&ia[1] = %p\n", &ia[1] );
/* ia の四つ先になる */
printf ( "ia + 1 = %p\n", ia + 1 ); /* 配列 ia の最初の要素のアドレス */
/*
[予想]
a) ia のアドレスに 1 を加えたもの
適切そうなのだが、誤り
b) ia[1] のアドレス
&ia[1] == &(*(ia + 1)) == &*(ia+1) == ia + 1
*/
/*
&変数 -> アドレス値が得れれる
-> ポインター値が得られる
ポインター値は、アドレス値と型の情報をもっている
# 引数として渡せるのは、アドレス値だけ
return 0;
}
#include <stdio.h>
void sub ( char *cp ) {
}
int main(int argc, char *argv[]) {
char vc;
sub ( &vc ); /* vc のアドレス値を .. (不十分) */
/* vc のポインター値を渡す */
return 0;
}
#include <stdio.h>
void sub ( char *cp ) {
}
int main(int argc, char *argv[]) {
int vc;
sub ( &vc ); /* vc のアドレス値を .. (不十分) */
/* vc のポインター値を渡す */
/* 型チェックによって、エラーになる */
return 0;
}
#include <stdio.h>
void sub ( char *cp ) {
printf ( "value = %d\n", *cp );
}
#include <stdio.h>
int main(int argc, char *argv[]) {
char cv = 10;
sub( &cv );
return 0;
}
#include <stdio.h>
void sub ( int *cp ) {
/*
+-----------+
cp=iv | 16 | 10000
+-----------+
| ? |
+-----------+
| ? |
+-----------+
| ? |
+-----------+
*/
printf ( "value = %d\n", *cp );
}
#include <stdio.h>
int main(int argc, char *argv[]) {
int cv = 10000;
/*
+-----------+
cv | a | 10000 = a + 256 * ( b + 256 * ( c +256 * d ) ) )
+-----------+ a = 1000 % 256
| b |
+-----------+
| c |
+-----------+
| d |
+-----------+
*/
printf ( "%d mod 256 = %d\n", cv, cv % 256 );
sub( &cv );
return 0;
}
#include <stdio.h>
#include "p-009-sub.h"
void sub ( int *cp ) {
/*
+-----------+
cp=iv | 16 | 10000
+-----------+
| ? |
+-----------+
| ? |
+-----------+
| ? |
+-----------+
*/
printf ( "value = %d\n", *cp );
}
#include <stdio.h>
#include "p-009-sub.h"
int main(int argc, char *argv[]) {
int cv = 10000;
/*
+-----------+
cv | a : 16 | 10000 = a + 256 * ( b + 256 * ( c +256 * d ) ) )
+-----------+ a = 1000 % 256
| b |
+-----------+
| c |
+-----------+
| d |
+-----------+
*/
printf ( "%d mod 256 = %d\n", cv, cv % 256 );
sub( &cv );
return 0;
}
p-002.c
表面上は、変数 va を指定して、sub4 を呼び出すと
(なぜか、無関係な..) vb の値が変更される
-> 奇妙 ( 無関係なものが変更された )
p-003.c
プログラムの内容や動作は、p-002.c と同じ
変更したのは、va, vb を、配列の要素にした
-> va のアドレス = 配列名の値
配列の要素が書き換わったので、「奇妙」ではない
ポイント
va の隣が vb である事は保証されていない
p-002.c は、何がおきても不思議はない
p-003.c では、「配列にした」事により、
v[0] と v[1] が隣にある事が保証される
アドレスの計算の話
-> ポインター計算
アドレス値(実行時の値)とポインター値(プログラム時/コンパイル時)
アドレス値は、あくまでも、変数がメモリのどの番地かという値
それに対して、ポインター値、(実態はアドレス値だが..) プログラム上では、
型情報を担っている
操作をするときに、その型に従って異なる扱いがされる
例1: +1 すると、アドレス値が sizeof 型だけ増える
例2: * をつけたとき、その型の変数に代わる
int * -> * をつけると int 型
char * -> * をつけると char 型
関数呼び出しで、実際に(実行時に)渡されるのは、
あくまでも、アドレス値
ただ、プログラム上はポインター値として型情報と一緒(プログラマやコンパイラ)に扱われる
実行時とプログラム時は、異なるもの
型情報を適切に補う事で、「同じもの」として扱えるようにする必要がある
関数の引数に関しては、型チェックが必要
# 関数の引数は、値が渡されるが、型情報は渡されない
もし、関数の呼び出し元と呼び出し先が同じファイル内なら、
コンパイラが、その情報を集めて、チェックしてくれる
ところが、分割コンパイル(関数を別のファイルにして個々にコンパイルして、あとで、まとめる(リンクする)事
をすると、情報が分散して、チェックできなくなる
そこで、ヘッダーファイルにプロトタイプ宣言をいれて、情報を共有する
p-006.c
+-----------+
|sub <-+ | 同一ファイル内なので
|main -+ | 型チェックが可能
+-----------+
p-008.c, p-007-sub.c
+-----------+
p-007-sub.c |sub | <-------+
+-----------+ | 相互チェックができない
+-----------+ |
p-008.c |main | <-------+
+-----------+
p-009.c, p-009-sub.c, p-009-sub.h
+-----------+
p-009-sub.c |sub | <--> +-------------------+
+-----------+ |プロトタイプ情報 | p-009-sub.h
+-----------+ <--> +-------------------+
p-009.c |main |
+-----------+
#include <stdio.h>
ライブラリが提供している関数のプロトタイプ宣言も含まれている
(まとめ)
&でえられるのは、アドレス値だけでなく、それと型情報を加えた物
!!! 「型情報」というのは、プログラマやコンパイラが共有している規則(実態はない)
!!! 実行時には、存在しない
!!! 「扱い方の違い」という形で、「間接的にのこっている」
!!! アドレス値の方は、実態(実行時に決まる)がある
=>
型が異なるので扱いが異なる
例 : ポインター値に 1 を加えると sizeof 型だけ増える
関数の引数として渡されるのは、あくまでもアドレス値だけ
関数に渡すときに、型情報がなくなり、
関数側、その型情報を「仮引数変数の型を利用して」補われる
効率的だが危険 -> ヘッダーの形、安全性を担保している
配列とポインター
配列は、同じ型の変数がならんだもの
メモリ上をしめるサイズが同じ
個々の要素のアドレスが、簡単な計算式で求められる
int a[3]; /* a[0], a[1], a[2] */
struct {
int a0, a1, a2
} s; /* s.a0, s.a1, s.a2 */
a[1] -> *(a+1)
同じサイズのものが規則正しく並んでいる
その性質を利用して、効率よく計算で、参照可能
s.a1 -> a0 のサイスが固定ではないので、
簡単には、計算できない
配列名は、先頭の要素のポインター値をもっている
-> a[i] == *(a + i) により、計算で参照できる -> 速い
多次元配列は ?
-> これも、添え字から計算で、得ることができる
例:
char a[3][2];
+-------+
a |a[0][0]|
+-------+
|a[0][1]|
+-------+
|a[1][0]| a[i][j] = * ( a + i * 2 + j )
+-------+ のように計算で求められる
|a[1][1]| ランダムアクセスできる
+-------+ 高速
|a[2][0]|
+-------+ 要素が、型と先頭のアドレスだけで計算できる
|a[2][1]| C 言語では、先頭のアドレスを渡して済ませている
+-------+
この計算では、最初の添え字のサイズは使わない
省略可能
一次元の場合は * でよい
Download : 20171208-01.c
/*
* 課題 CNAME-01
*
* 2017/12/08 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 *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 ) {
/* 動的メモリは取り出せるとは限らないので、結果をチェック */
value[ size ] = 0; /* 最後の要素として 0 を代入 */
} /* else {} */ /* NULL が帰った場合は、そのまま、値として返す */
} else { /* 入力が終っていないので、更に、値を読むために再帰呼び出し */
if ( ( value = read_n_integers( size + 1 ) ) != NULL ) {
/* 結果が NULL でなければ、配列が作られている */
/* size 番目の要素を配列に記録 */
/*
** この部分を完成させなさい
*/
} /* else {} */ /* NULL が帰った場合は、そのまま、値として返す */
}
/* いずれの場合でも 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 で解放する */
/*
** この部分を完成させなさい
*/
} /* else {} */ /* NULL の場合はエラーなので、何もしない */
return 0;
}
12 34 5 6 90 -1
$ ./20171208-01-QQQQ.exe 正の整数値を入力してください(0 以下だと入力を終了します):12 正の整数値を入力してください(0 以下だと入力を終了します):34 正の整数値を入力してください(0 以下だと入力を終了します):5 正の整数値を入力してください(0 以下だと入力を終了します):6 正の整数値を入力してください(0 以下だと入力を終了します):90 正の整数値を入力してください(0 以下だと入力を終了します):-1 0 th data = 12 1 th data = 34 2 th data = 5 3 th data = 6 4 th data = 90 $