C,C++分科会テキスト04
目的
文字列についての詳細をみます。まず文字列という型はCにはありません。
文字列は文字の配列を使ってあらわし、配列はポインタ値によって特定されます。
というわけで今回は配列とポインタと参照型についてです
初心者にとってわかりにくいところといわれていますが、わかってしまえば非常に簡単なのであきらめずにがんばりませう。
目次
- 配列
- 配列は同じ型の変数を並べたものです。
- 配列とポインタ変数
- 配列自身は列の最初の変数のポインタ値によって表現されます
- 文字列
- 文字列はchar型の配列にほかなりません。
- ポインタ変数と参照型
- ポインタ値を処理するポインタ変数と変数自身を意味する参照型
配列は同じ型の変数を並べたものです。どのように定義するかというと
int a[4];
です。これでint型の4つの変数a[0],a[1],a[2],a[3]を作ってくれます。
ただこのような名前の変数を作るだけでなく、a[1+2]とかa[i]等のように値をいれて変数を特定することができます。
また、
int a[4]={1,3,5,7};
とすると、配列を定義すると同時に初期化することができます。(ただし、これ以後は一括代入はできません。)
すなわちint a[0]=1,a[1]=3,a[2]=5,a[3]=7;とできるわけです。
さて配列を作るとたいていメモリー上に並んでデータが入ります。
さて、この配列はどうやってあらわされる事が多いのでしょう。
結論から言えば配列の一番最初のデータすなわちa[0]、のメモリ上の位置をしめすポインタ値(メモリ上のアドレスを意味する)によって表されているのです。
そしてこの「 a[0] 」のポインタ値は「 a 」です。
そしてこのようなポインタ値を格納する変数がポインタ変数と呼ばれるものです。
まず先に書いてしまうと、「ポインタ変数」と、「ポインタ値」と、「変数」と、「値」というのがあります
値というのは値であり、
ポインタ値というのは値が格納されているメモリー上の位置をあらわす値であり
変数というのはそのポインタ値と1対1に対応するラベルのようなものであり、値を格納するもので
ポインタ変数というのはポインタ値という値を格納させたときのメモリの部分に対応する変数ということです。
使い方は簡単です。
ポインタ値と変数は、ポインタ値からそれに対応する変数自身(値ではない)を示すということと、
変数のポインタ値を出すという二つの形でやり取りができます。
hogeという変数が定義されているとします。すると
- &hogeはhogeという変数のポインタ値を示します。このポインタ値に対し、
- *(&hoge)はhogeという変数自身になります。
また、変数hogeの型がintなら&hogeの型はint *型となりますしchar型ならchar *型となります。
つぎに、ポインタ変数とポインタ値の関係ですが言葉の示すとおり変数と値の関係にほかなりません。
変数と値とは変数に値を入れる、値を変数から出すという形でのみやり取りが可能でした。
同様にポインタ変数とはポインタ値を格納する変数なのです。
先ほど述べたようにint型の変数のポインタ値はint *型なのでそれを格納する変数もint *型です。
int hoge;
int *phoge; //phogeはint型の変数のポインタ値(int *型である)を格納するための変数(ポインタ変数)。
phoge=&hoge; //hogeのポインタ値がphogeのなかに代入された。
という具合です。これによって*phogeはhogeのことになります。ですから、
*phoge=123;
というのと、
hoge=123;
というのはまったく同じ意味を持ちます。
さて、なぜポインタ値だのポインタ変数だのを持ち出すかというと、これ特有の演算があるからに他なりません。
- ポインタ値は整数値とたし合わせることができ、たし合わせた結果はポインタ値です。
+1によって、配列上の次の変数へのポインタ値を示します。つまり
*(a+1)すなわち*(&a[0]+1)というのはa[1]のこととなるのです。
- ポインタ値はポインタ値から引くことができます。引いた結果は整数値です。
この整数値は先ほどと同様変数の個数の単位で示してくれます。
配列上で&a[5]-&a[0]とやればこれは5のこととなります。
さて、配列とポインタの関係がわかったので、今度は文字列です。
文字列は文字の配列ですchar型変数の配列です。ただし、配列を明示的に定義する必要がありませんのでpirntf関数などの引数に使うときに便利です。
printf("hello,world\n");
というのがありましたが、これはprintf()という関数に引数として文字列"hello,world\n"を与えているのですが、これは実は次のようにしても実現できます。
const char a[16]={'h','e','l','l','o',',','w','o','r','l','d','\n'};
printf(a); //printf(a[0]);でももちろん良い。
つまり、"hello,world\n" と書くと、文字の配列をつくり{'h','e','l','l','o',',','w','o','r','l','d','\n'}で初期化し、
この配列の一番最初のポインタ値を値として持つということである。
ですから、
printf("hello,world\n"+1);
とやると、ello,worldと表示されたりします。
Cではポインタ型は、関数に変数自身を渡すのに使われてきました、というのも関数の引数や戻り値というのは値のみを渡すというやり方だったからです。
値を渡すというのは関数の変数に変数を入れてもその値のみがコピーされて使われるということです。
そのため、変数と一対一に対応するポインタ値を渡すことによって、変数自身を渡すのと同じ効果を得ようという作戦なのです。
変数を渡すと、それに10を代入してくれるという役に立たないプログラムを書いてみよう。
int function10(int *phoge){
*phoge=10;
return 0;
}
int main(){
int a,b;
function10(&a); //こうやってint型へのポインタ値を渡す
function10(&b);
return 0;
}
役に立たないと書いたが、scanfという関数は
scanf("%d",&a);
とやってつかうので同じ構造であることがわかると思う。
しかし、C++では、変数自身がそのままの形で扱えるようになった。
変数自身を表す型が登場した。それを参照型とよぶ。
int型の変数自身を表す型はint&型である。どのように使うかというと次のようである。
int function10(int& hoge){
hoge=10;
return 0;
}
int main(){
int a,b;
function10(a); //aを値ではなく変数として渡す(参照渡し)
function10(b);
return 0;
}
参照渡しでできることはポインタでもできるが、参照型を使うほうがほうがわかりやすい場合が多い。
この関数を
int a;
function10(a);
と呼び出すと、関数が
int& hoge=a;
のような操作をする。
int&型の変数への代入は値ではなく変数自身が入るので、hogeにaがはいる。
このhogeは実際に使うときにはaの別名のように扱える。
ところで、int *phogeというポインタの宣言の仕方はint *型としてphogeを宣言するという意味を持つと同時に、
*phogeがint型になるようにphogeの型を決定するという意味があるのだが、
int&型となるとぜんぜんそのような意味が無い。
実はint* phogeという書き方やint &hogeという書き方もあるのだがわかりやすくするため
int *phoge、int& hogeとしてみた。