プログラムの構造化 〜関数と構造体〜 (4/4)
作成:2011-02-05 22:09
更新:2011-02-05 22:09
更新:2011-02-05 22:09
■ポインタで構造体を受け渡す
先のサンプルでは、構造体を引数で渡す関数を定義しました。このように構造体を引数として渡すとき、注意しなければならないのは「引数には構造体のコピーが渡される」という点です。
例えば、下の「そのまま構造体を渡す」というサンプルを見てください。ここでは、先の構造体を利用する「clearMail」「printPerson」の2つの関数を用意しました。clearMailではmailの値を"<<no mail>>"に変更し、printPersonでは先程と同じく内容を出力します。
このサンプルでは、Person構造体を1つ作成し、printPersonで出力後、clearMailを呼び出してから、再度printPersonを呼び出しています。が実行すると、mailの値がまったく変わってないことに気がつくはずです。clearMailを呼び出しても、mailの値が変更されないのです。
これは、「引数では、値のコピーが渡される」ということを知らないために起こる間違いです。変数への値の代入は、常に「その値のコピーが渡される」ということを思い出してください。ですから、clearMailで引数に渡されるのは、変数dataの構造体そのものではなく、それをコピーしたものなのです。ですから、clearMail内でどう値を変更しようが、それはすべてコピーされたものを操作しているだけであり、変数dataの中身は一切変更されない、というわけです。
ということは、構造体を引数で渡して、その構造体を操作する関数というのは作れないのでしょうか。せっかく引数として渡しても、値が全部コピーされてしまい、もとの構造体を操作することができないのですから……。
いいえ。実は、構造体をコピーせず、そのまま関数に渡す方法があるのです。それは、構造体そのものではなく、構造体の「アドレス」を渡すのです。そう、「ポインタ」を利用するのです。
構造体のポインタを引数として受け渡すようにすれば、引数にはアドレスが渡され、そのアドレスにある構造体をそのまま利用することができます。これなら、渡した構造体そのものを操作することができます。
下の「ポインタで構造体を渡す」に、関数の引数をPerson構造体のポインタに変更したものをあげておきました。今度は、clearMailでちゃんとmailの値が変更されるようになります。ここでは、
これで、構造体のポインタを利用して、1つの構造体をあちこちの関数に受渡して操作することができるようになりました。この考え方は、実はとても重要なのです。Objective-Cでは、「オブジェクト」というものが登場し、これが非常に重要な役割をはたすことになるのですが、これは「構造体とポインタ」の考え方を更に発展させたものなのです。
ですから、Objective-Cに進む前に、この「構造体とポインタ」の基本について、しっかりと理解しておきましょう。その知識は、必ずObjective-Cでも役に立つはずです。
例えば、下の「そのまま構造体を渡す」というサンプルを見てください。ここでは、先の構造体を利用する「clearMail」「printPerson」の2つの関数を用意しました。clearMailではmailの値を"<<no mail>>"に変更し、printPersonでは先程と同じく内容を出力します。
このサンプルでは、Person構造体を1つ作成し、printPersonで出力後、clearMailを呼び出してから、再度printPersonを呼び出しています。が実行すると、mailの値がまったく変わってないことに気がつくはずです。clearMailを呼び出しても、mailの値が変更されないのです。
これは、「引数では、値のコピーが渡される」ということを知らないために起こる間違いです。変数への値の代入は、常に「その値のコピーが渡される」ということを思い出してください。ですから、clearMailで引数に渡されるのは、変数dataの構造体そのものではなく、それをコピーしたものなのです。ですから、clearMail内でどう値を変更しようが、それはすべてコピーされたものを操作しているだけであり、変数dataの中身は一切変更されない、というわけです。
ということは、構造体を引数で渡して、その構造体を操作する関数というのは作れないのでしょうか。せっかく引数として渡しても、値が全部コピーされてしまい、もとの構造体を操作することができないのですから……。
いいえ。実は、構造体をコピーせず、そのまま関数に渡す方法があるのです。それは、構造体そのものではなく、構造体の「アドレス」を渡すのです。そう、「ポインタ」を利用するのです。
構造体のポインタを引数として受け渡すようにすれば、引数にはアドレスが渡され、そのアドレスにある構造体をそのまま利用することができます。これなら、渡した構造体そのものを操作することができます。
下の「ポインタで構造体を渡す」に、関数の引数をPerson構造体のポインタに変更したものをあげておきました。今度は、clearMailでちゃんとmailの値が変更されるようになります。ここでは、
void clearMail(Person* data){……このように、Person構造体のポインタを引数として受け渡すようにしてあります。そして呼び出す側では、clearMail(&data);このように、構造体を収めた変数のアドレスを渡すようにしているわけです。こうすることで、構造体のアドレスを引数に受け渡すことができます。また関数側では、受け取った構造体ポインタの値を利用するのに、
data->mail = "<<no mail>>";このような書き方をしています。「◯◯->△△」というように、「->」という演算子を使って構造体ポインタから値をとり出しています。通常のように「◯◯.△△」という形では値を取り出せないので注意しましょう。
これで、構造体のポインタを利用して、1つの構造体をあちこちの関数に受渡して操作することができるようになりました。この考え方は、実はとても重要なのです。Objective-Cでは、「オブジェクト」というものが登場し、これが非常に重要な役割をはたすことになるのですが、これは「構造体とポインタ」の考え方を更に発展させたものなのです。
ですから、Objective-Cに進む前に、この「構造体とポインタ」の基本について、しっかりと理解しておきましょう。その知識は、必ずObjective-Cでも役に立つはずです。
(by. SYODA-Tuyano.)
※プログラムリストが表示されない場合
AddBlockなどの広告ブロックツールがONになっていると、プログラムリスト等が表示されない場合があります。これらのツールをOFFにしてみてください。
●プログラム・リスト●
※そのまま構造体を渡す
#include <stdio.h>
typedef struct {
char* name;
int age;
char* mail;
} Person;
void clearMail(Person data){
data.mail = "<<no mail>>";
}
void printPerson(Person data){
printf("name: %s\n",data.name);
printf("age : %i\n",data.age);
printf("mail: %s\n",data.mail);
printf("\n");
}
int main (int argc, const char * argv[]) {
Person data = {"taro",30,"taro@yamada.com"};
printPerson(data);
clearMail(data);
printPerson(data);
return 0;
}
※ポインタで構造体を渡す
#include <stdio.h>
typedef struct {
char* name;
int age;
char* mail;
} Person;
void clearMail(Person* data){
data->mail = "<<no mail>>";
}
void printPerson(Person* data){
printf("name: %s\n",data->name);
printf("age : %i\n",data->age);
printf("mail: %s\n",data->mail);
printf("\n");
}
int main (int argc, const char * argv[]) {
Person data = {"taro",30,"taro@yamada.com"};
printPerson(&data);
clearMail(&data);
printPerson(&data);
return 0;
}
※関連コンテンツ