今日やること
前回は制御文をやりましたやりました。まだ、慣れていない所も多いと思いますが先に進みます。練習したい人はAtCoderのABCのA,B問題を解きましょう。さて、今回は関数とポインタについて説明していきます。ポインタは少し難しい概念なのでゆっくりとやっていきましょう。
関数
プログラミングをやっていない人は、関数といえば数学の f(x)=4x+3 みたいなものを思い浮かべる人が多いと思います。 プログラミングで扱う関数も似ています。例えば、 f(x)=4x+3 の関数は以下のように定義します。
int func(int x){
return 4*x+3;
}
この関数を使用する場合は以下のようにして使います。
#include<stdio.h>
int func(int x){
return 4*x+3;
}
int main(){
printf("%d\n",func(3));
return 0;
}
これで、func(3)の値すなわち4×3+3=15 が表示されます。
今ので、大体分かった人もいるでしょうが詳しく説明していきます。
まず、int funcについてです。funcは関数の名前で、intはこの関数がどういう型で扱われるかを表します。
今回funcはint型で定義されているので、func(3)はint型の値として扱われます。なので、%dで出力しているわけです。
次にfunc(int x)のint xについてです。これは引数と呼ばれるもので、関数を使用するときに値などを入れることでその変数がそれに初期化された状態で処理されます。
この値により関数内の処理が変わり、結果も異なるものとなります。例えば上のようにfunc(3)とすればxに3が代入されるので4×3+3=15となります。もし、func(4)とすれば4×4+3=19となるのです。ちなみに、この変数xは関数内で作られ、関数内で消えるようになっています。
最後にreturn 4x+3; についてです。returnは何かしらのものを返すコードで今回の場合、4x+3の値を返すという意味になります。
func関数を使用すると4*x+3という値が返されるので、func(3)を15として扱うことができるのです。なので、返すものの型と関数の型は一致していないといけません。
何を返すかによって関数の型を決めると考えるということです。また、値が返されると先にプログラムが書いてあったとしても、そこで関数内の処理が終了するので覚えておきましょう。
関数の仕組みは理解できたでしょうか?では、練習としてint型の引数xをとり、xの絶対を返す関数を作成してみましょう。関数内では制御文や変数の宣言などもできるので今回はif文を使って書きましょう。
さて、関数内では始め数学の関数と似ていると言い、それに近い関数について説明しましたが、実は数学の関数と少し違うような使い方もします。
1つは引数を取らない関数です。数学の関数ではその定義からも何かの値を代入しなければいけませんが、プログラミングにおいてそのような必要はありません。
たとえば、関数外で宣言したxの値を2倍にして返す関数は以下のように書きます。
int x=3;
int Twice(){
return 2*x;
}
もう1つは何も返さない関数です。 数学の関数は何か値を表さなければなりませんが、プログラミングにおいてそのような必要はありません。
ただの処理のまとまりとして使用することができるというわけです。例えば、**Hello World!**と出力す関数は以下のように書きます。
void HellowWorld(){
printf("Hello World!\n");
}
ここで、voidとは空のという意味の型です。voidはint Twice(void){... みたいに空のものを表現したいときに使われることもあるので覚えておきましょう。
ポインタ変数
変数はそのデータをメモリ上で保存しています。その、保存されている場所をアドレスといい、そのアドレスを扱う変数をポインタ、ポインタ変数といいます。 ポインタ変数は以下のように宣言します。
int *x;
xが持つアドレスの中身を扱いたい場合は*xと書きます。アドレスそのものに操作を加えたい場合はxのまま使います。
そして、ポインタでない、int型の変数aのアドレスは&aで参照できます。気づきましたか?
そうですね!printf()関数使用時に&を使っていたのはアドレスを渡してそこに読み込んだ値を代入していたということです。
さて、例として、int 型変数aの値のint型のポインタxに渡し、xが持つアドレス(aのアドレス)とxが指す変数の値(aの値)を表示するコードは以下のようになります。
int a=33;
int *x=&a;
printf("aのアドレス(16進数表記): %x\n",x);
printf("aのアドレス(10進数表記): %d\n",x);
printf("a値 : %x\n",*x);
また、配列について、[]配列の部分を書かずに名前だけを書いた場合、それは配列の先頭のアドレス、すなわち0番目の要素のアドレスを表すことになります。
よって、配列int A[100]について、Aと&A[0]は同じものであるということになります。
また、配列の要素はメモリ上に連続に格納されています。なので、配列のi番目の要素のアドレスはその配列の先頭のアドレスからiだけプラスした場所(厳密にはその配列の型の変数1個のメモリの大きさ✕iだけプラスした所)になります。よって、配列int A[100]について、A+iと&A[i]は同じものであるということになります。
例として、int A[100]の値を全て-1で初期化するコードを2通り書きます。
int A[100];
int i=0;
for(i=0;i<100;i++)A[i]=i;
for(i=0;i<100;i++)*(A+i)=i;
また、関数の引数としてポインタを渡すと変数の中身を変えることができます。 例えば、int型変数の値を倍にする関数は以下のように書きます。
void Twice(int *x){
*x*=2;
}
下の関数では、引数として渡された変数自体の値は変化し無いことに注意しましょう。
void Twice(int x){
x*=2;
}
ポインタは難しく、頭がこんがらがると思いますが頑張って慣れましょう!
練習しよう
練習として以下の問題を解きましょう。
1. 前回 の練習問題を参考に、引数をxとして、1からxまでの整数の和を返す関数を書け。
2. 2つのint型変数のポインタを受け取り、そのポインタが指す変数の値を入れ替える関数を書け。
3. グローバル変数としてint型配列A[101]を作り、ポインタを用いずにA[i] (0<=i<=100)の値をiで初期化する関数を書け。
4. int型配列の先頭のポインタとその配列の要素数を引数として受け取り、引数として渡された ポインタが指す配列のi番目の要素(0<=i<要素数)の値をiで初期化する関数を書け。
最後に
お疲れ様でした。
関数、ポインタと難しかったと思いますがなれると便利なので、ぜひ使っていきましょう!