文字配列の文字数(空白などを含む)を返す関数 count() を作成しなさい。 また、下記のプログラムによりテストを行い、結果を示しなさい。
#include <stdio.h>
int count(char *s){
/* ここにプログラムを書く */
}
int main(void){
char a[]="This is a pin.";
printf("文字列 %s の文字数は %d です。\n",a,count(a));
return 0;
}
課題 1-1 で作成した count 関数を使い、次のプログラムAを改造して、各文字 列の長さを表示するようにしなさい。 なお、 a は char * 型の配列で、各要素 a[0], a[1], ... にはそれぞれ、 "This is a pin." の先頭番地、 "This question ..." の先頭番地, ... が入る。
#include <stdio.h>
void dispStrings(char **s){
char **p;
for(p=s; *p!=NULL; p++){
printf("%s\n",*p);
}
}
int main(void){
char *a[]={"This is a pin.", "This question is not so difficult.",
"You should understand what the pointer is.",NULL};
dispStrings(a);
return 0;
}
改造の仕方は次のようにすること。
文字列を大文字にして表示する関数 void dispInCapitals(char p[]) を作りなさい。 そして、以下のプログラムでテストを行い、結果を示しなさい。
#include <stdio.h>
int dispInCapitals(char *s){
/* ここにプログラムを書く */
}
int main(void){
char a[]="This is a pin.";
dispInCapitals(a);
return 0;
}
課題 1-3 で作成した dispInCapitals 関数を利用して、文字列(文字ポインタ) の配列を受け取り、全ての文字列を大文字で表示する dispStringsInCapitals を作りなさい。 この関数は必ず dispInCapitals 関数を必ず呼ぶこと。 また、以下のプログラムでテストを行いなさい。
#include <stdio.h>
void dispInCapitals(char *s){
/* 課題 1-3 で作成したもの */
}
void dispStringsInCapitals(char **s){
/* この部分を作成する */
}
int main(void){
char *a[]={"This is a pin.", "This question is not so difficult.",
"You should understand what the pointer is.",NULL};
dispStringsInCapitals(a);
return 0;
}
C 言語では変数の格納されている番地を扱うことができる。
変数名の前に & を付けると、その変数の番地を意味する。
また、変数の番地を取り扱う変数も用意されている。
そのような変数をポインタと言う。
ポインタは格納される値の型で区別される。
int の値が入る変数のポインタは int 型のポインタなどと言う。
そして int 型のポインタの宣言は
int x, *y; x=1; y=&x; printf("%d\n",*y); /* 1 が表示される */
C 言語では配列変数はポインタにより実装されている。 配列 a[0], a[1] に対して、 a は a[0] の番地を示している。 つまり a と &a[0] は同じになる。 また、ポインタに対しても [値] とすると配列と同じように要素にアクセスで きる。 さらに、ポインタに 1 を足すとメモリの番地として 1 増えるのではなく、配列 として次の要素を指すようになる。 つまり次のような操作が許される。
int a[2];
int *x;
a[0]=5;
a[1]=6;
x=a;
printf("%d\n",*x); /* 5 が表示される */
printf("%d\n",x[1]); /* 6 が表示される */
x+=1;
printf("%d\n",*x); /* 6 が表示される */
つまり配列の宣言とポインタの宣言は領域を確保するかしないかだけで、基本 的には同じような意味となっている。
C 言語ではプログラムを分割、再利用するために関数と言う概念がある。
関数は
一方、関数の内部で定義された変数はローカル変数と呼び、他の関数か らアクセスできない。 関数の外側でも変数の定義ができる。 外側で定義した変数をグローバル変数と呼ぶ。 グローバル変数にはあらゆる関数からアクセスができるので、関数の情報を受 け渡すのに使用できるが、受渡しがうまく行ってない場合に原因を突き止める のが難しい。
C 言語の関数は値呼び出しである。 つまり関数の引数は関数側にとっては値が代入されているローカル変数となる。 したがって、関数の内部で値を変更しても、呼び出し側の引数の値を変更すること はできない。 そのため、変数の内容を関数で変更させるには、変数の番地を関数に与え、関数 内部ではポインタにより変数にアクセスする必要がある。
番兵とは複数のデータを扱う際に、データの終りにさらにデータの終りを示す データを与えるものである。 番兵を用いることによりデータの個数を意識しなくても複数のデータを処理す ることができるようになる。 配列変数を関数で処理する場合も、番兵を与えておけばデータの個数を関数に 渡す必要がなくなる。
関数の入力は文字配列の入っているメモリの先頭番地である。 また、 文字列の番兵は '\0' である。そのため、次のようにプログラムを組む。
このようにして作成した dispArray 関数を付録に示した。 これを指示されたテストプログラムを用いてテストを行う。 予想される出力は「XXXXXXXXXX」となるはずである。 また、実行した結果を付録に示した。 実行結果より、予想と一致し、正しいプログラムが得られたことがわかった。
配列変数 a のメモリの構造は図XX のようになっている。 (メモリの構造を図示すること) これはポインタの配列であるため、 a の型自体は char ** とポインタのポイ ンタとしても考えることができる。 そのため、 dispStrings の引数の型は char ** になっている。 またポインタの配列であるので、番兵は空のポインタを指す NULL が使用され ている。
さて、 dispStrings の for ループ中で p は順番に a の中、つまり a[0], a[1], ... を指すことになる。 したがって、 *p はそれぞれの文字列の先頭番地を指している。 これを printf に与えれば文字列が表示できるが、課題 1-1 で作成した count 関数に与えれば文字数を得ることができる。 したがって、count を毎回呼び出して値を表示するようにすれば良い。 このようにして変更を与えたプログラムを付録に示した。 これを指示されたテストプログラムを用いてテストを行う。 予想される出力を図XXに示す。さらに、実行した結果を付録に示した。 実行結果が予想と一致するため、正しいプログラムが得られたことがわかった。
省略
省略
今回は関数とポインタを使用した関数 count, dispCount, dispInCapitals, dispStringsInCapitals を作成した。
考察すべき点であるが、今回は簡単過ぎてなにも考えずに完璧にプログラムで きたので特になにも書く事はない。 また課題自体も完璧であるのでなにも指摘すべきことがない。