上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。





長らく開けてごめんなさい。

とはいっても今日もそんな大した更新じゃなく、C言語の解説どまりです。





▼メモリ

変数は宣言された時点でメモリのどこかに置かれます。
いうなれば、紙です。 例えば・・・

int a = 5;

aという変数を宣言して、5で初期化しました。
するとメモリ上のどこかに、



こんな感じで確保されて、書き込まれます。

ちなみに変数には寿命があって、関数内に書かれた普通の変数の寿命は関数を抜けるまでです。
寿命が来たらきれいさっぱり(厳密にはきれいさっぱりではないんだけど)なくなります。


この確保は、変数を宣言した場合ほぼ例外なく行われます。
例外は後で説明するとして、変数宣言というのは、

int a;

みたいな感じで、

変数型 変数名

と書かれた時です。
つまり、普通に関数内に書かれた時は分かりやすいですが、

for (int i = 0; i < 5; i++) {
  ...

これも、

int function1(int x) {
  x = x + 5;
  return x;
}

これもそうです。
実は引数も変数宣言に入ります。

ここで、上のfunction1を使って例題を一つ。

int function1(int x) {
  x = x + 5;
  return x;
}

int main() {
  int a = 5;
  int b = function1(a);

  printf("a=%d, b=%d", a, b); // a = 5, b = 10
}

printfの書式は分かる人だけでいいです、あまり関係ないので。
aは5になって、bは10になるということだけが重要です。

何が言いたいのかというと、関数function1にaを渡しました。 中ではaは+5されているのにこちらでは変わっていないですね。

今回function1の引数はxで書きましたが、aで書いたところでmain関数のaが変わることはありません。
なぜなら、関数に渡した変数と、引数で宣言された変数が別物だからです。


ここで、変数の別物、同一をどう区別するかの定義を確認しましょう。
答えは単純で、メモリ(場所)が同じか否か、です。
別な場所にあったら別物です。

順を追うと、

int main() {
  int a = 5;

ここでさっきの状況ができます。



これです。

で、次にbの場所が先に確保されるのか、関数から返った後なのかは知らないのですが、どうでもいいのでどうにでもなってください。

※灰色っぽい文章はこんがらがる人は読まないでくださいね。 どうでもいいことなので。

次でfunction1が呼ばれますね。

int function1(int x) {

この時点でどうなるかというと、



こうなります。
xという変数が宣言されたので、何もないメモリ上に新たに確保されて、初期値はもらってきたaの値、つまり5がそのままコピーされます。

で、

  x = x + 5;
  return x;
}

こうですね。

これはつまり、xに5を足すのですから、



こういうことです。
で、これをbに代入ですね。

int b = function1(a);

ですから、function1の戻り値10が代入されます。
ちなみにbもこの時点で宣言されているので、新たに作られます。



で、関数function1を抜けたので、xはきれいさっぱりなくなります。



きれいさっぱりではないですねこれ(汗)
でも実際はこんな感じになります。


さあ、もうaの値が変わらなかった理由は分かりますね。
引数は全く別物の変数として宣言され、別の場所で作業をしていたので、もともとの変数aの値を変えることはなかったのです。




▼ポインタ

ポインタとは何でしょう。
ポイントは指し示すという意味です。 ポインタは指し示す者、みたいな感じでしょうか。

簡単に言ってしまうと、場所です。
変数のある場所を示します。

例で言うなら、A君がいたとしましょう。
さあ、A君に伝えたいことができました。 どうしましょう。

1 直接言いに行く
2 電話する

直接行けば間違いはないですね。
A君(実体)に直接ものを言えます。

では電話はどうでしょう。
これも電話番号があれば連絡可能ですね。
この電話番号がポインタに当たります。


じゃあ実際にやってみましょう。
まず1番 A君に直接言います。

日本語が喋れればおっけーですね。 (A君はアメリカ人とかそういう設定はないです)
Cでは、
int message = 5;
int A = messsage;

とこんな感じになるでしょうか。 A君にmessageを伝えています。


お次は2番。
現実世界では、電話の使い方を知っていれば問題ないですね。
ではC。 電話機の操作に相当するものがあって、それは「*」と「&」です。

int A; // A君がいます
int message = 5; // メッセージがあります
int* tel = &A; // まず電話番号を確認します
*tel = message; // 電話します

無理やりに電話に当てはめるとこんな感じでしょうか。
色文字が3つ(緑の色文字はコメントなのでノーカウントです)です、それぞれ説明すると、

&A &はポインタを返します。 要するにAの場所を示す。
*tel *はポインタを元に、示している場所にアクセスします。 要するに電話番号から電話するってことです。

では最後にint*の説明。
この*と青文字の*は別物です。 これがややこしい。

ちなみに多くの場合、

int *tel;

ってこういう書き方している人、こういう教え方をするところが多いです。
実はどっちでもよく、こういってるわたしも普段は後者の書き方をしています。
でもイメージ的には、

int* tel

なんです。

何かというと、最初に変数の宣言は、

変数型 変数名

だといいました。
つまりこういうことです。

int* tel

int*は型名を表しています。
intのポインタ っていう型名です。 (もちろん正式名称じゃないよ!)

先のたとえで言うなら、電話番号という型です。
電話番号はA君という人間との互換性は全くありません。
「0120-117-117」と「Alico君」は足し算ができるわけでもないし大小関係の比較ができるわけでもありませんね。

同様に、intという整数値を示す型と変数の場所を示す型に互換性はないのです。
なので別の変数型をとります。


ここで気をつけてほしいのは、

int* tel = &A;

で、A君の場所を&によって示しました。
でもtelとAは別物だということは忘れないでください。



コンピュータっぽく16進数で書いてみました。
まあぶっちゃけメモリ位置も整数値なんですよね。
でもこう書けば初心者なら別物っぽい錯覚に陥ってくれるはず・・・!


これが何を意味しているのかというと、ポインタも変数だっていうことです。
変数なら、その変数の場所を示すこともできます。

int** memo = &tel;

電話番号を書いたメモみたいなイメージです。
メモから電話番号を見て、その電話番号でA君と話す。
ポインタが2個出てきます。 こういうのをダブルポインタといいます。



こんなイメージ。

ところで、

int** memo = &tel

telはint*っていう型だといいました。
int*っていう型のポインタ、はどうするかというと、ポインタとして示す型に*をつければいいんでしたね。
つまり、(int*)* となります。

もちろんこれはどこまでもつなげられます。 int**************なんてのもありです。
が、実際問題使うのはダブルポインタまででしょう。



ここで前にやった例題をもう一度やってみましょう。

int function2(int* x) {
  *x = *x + 5;
  return *x;
}

int main() {
  int a = 5;
  int b = function2(&a);

  printf("a=%d, b=%d", a, b); // a = 10, b = 10
}

今度のfunctionはポインタを引数に取ります。
ポインタを使ってやると、あら不思議、aまで変わってしまいました。

からくりは簡単です。



aが確保されるのは一緒。
次です。

int function2(int* x) {



ポインタで渡されます。
場所をもらいました。

*x = *x + 5;

*がポインタの前につくとポインタが指し示している物、つまり変数aそのものを表すんでしたね。
変数が同一である定義はメモリの場所が同じであることだといいましたね。
つまり *x と a は同一の、全く同じ変数だということです。

なので・・・



aそのものが書き換えられてしまいます。

function2の戻り値の型はintなので、これはただの10という整数値を返しています。
で、その整数値で変数bが宣言されて、ポインタ変数のxはなくなります。



この場合はしっかりとaが書き換えられましたね。




▼参照渡し

この章はぶっちゃけ読まなくても平気です。
前のほうでチョコっと言っていた、宣言されない例外のお話です。


単刀直球に答えを言うと、

int function3(int& x) {
  x = x + 5;
  return x;
}

int main() {
  int a = 5;
  int b = function3(a);

  printf("a=%d, b=%d", a, b); // a = 10, b = 10
}

これです。
これはただ1つ、引数に&が付いていることを除けばポインタを全く使わなかったfunction1とまったく一緒です。
ではint&とは何なのか。

これが参照渡しといわれる方法で、簡単に言うとこうです。



引数を値コピーでもらうのではなく、変数そのものをもらいます。
同じメモリ位置を指す変数は同一変数なので、aもxも同じなのです。

もちろんxはint型(ポインタではない)なので普通にx = x + 5で足し算ができますが、xとaは全く一緒のものなので・・・、



こういうことになります。
なので呼び出し元の関数でも値が変わってしまいます。


ちなみに int& のような書き方ができる場所は、引数リスト内の1か所しかありません。
引数以外ではint&みたいなものは出てきません。




▼イメージが大事

まとめです。

C言語の山は、個人的にはデータ型ポインタだと思っています。
これは前にも書いたことがありましたね

データ型というのはいわば識別子で、例えばabcdとアルファベットを使う言語はいくつかありますが、日本語のアルファベットなのか、英語なのか、・・・そういうたぐいのものです。

コンピュータの扱う文字は0と1しかありません。
でも、だからと言って2個しか表せないか、といわれるとそういうわけではありません。
それは、0と1の羅列から、ここは整数(int)として扱ってくれ!、ここは文字(char)でお願い!とプログラムに頼んで識別して処理してもらっているからなのです。
この識別子が変数型です。


なぜ識別子が重要なのか。
その理由は簡単です。

元が0と1しかないから、もっといえばコンピュータは整数しか扱えないからです。
なので、コンピュータ内では整数はもちろん、少数も文字も画像も音もすべて数字で表しています。
言い方を変えると、コンピュータの二次変換を使えば絵を数字にして、その数字を音にすることだってできるのです。

ここまで大きなものではなくとも、例えば文字を小数にすることもできます。
こういう変換はキャストといわれています。

char a = 'a';
float f = (float)a;


まあ脱線しましたが、すべてを数字として扱っている以上、見た目じゃ違いがわからないのです。

文字を表しているのか、整数を表しているのか、小数なのか、ポインタなのか。

これらを明確にするために変数型というものがあります。

ちなみにrubyなどではあまり変数型を意識しなかったと思いますが、あれは内部的にやってくれているだけであって、中ではちゃんとこういうことをやってくれているのです。
まあ、だからこそrubyは優しい言語といわれるのでしょうね。




ポインタは長々と説明してきました。
でも結局のところ、簡単にいえばただのメモリの場所でしかありません。

ポインタは、いうなれば同一の変数を作るための道具です。
同一の変数とは、ただ単に a = b; なんていうただの代入ではできないことです。 なぜならメモリの指す位置が違うからです。
同じメモリを指すためには、値ではなくメモリの位置が必要なんです。


同一の変数がどんなときに力を発揮するのか。
それはいわゆる初心者本レベルのソースコードには出てこないでしょう・・・。


ポインタのもう1個重要な働きとして配列があるのですが・・・。
まあその辺は長くなるので割愛です。

どうしても見たいなら・・・、こちらで

だいぶ内容が重複していますが、配列のことやポインタが生きる状況等も書いています。
後この記事を書くだいぶ?前に書いたものなので、ちょっと今思うとあいまいというかなんというか、っていうところもあるかなぁ。


このリンク先は、以前から物置として使っていたシーサーのブログです。
アカウント削除対策に、せっかくなのでいろいろと。

子守歌もソース付きで上げておいたので気になる人は是非どうぞ(笑)


スポンサーサイト




 注意事項
コメントの記述にhtmlタグは使用できません。
後、中傷コメントはもちろん、場違いなコメントもしないようにお願いします。
また、httpは広告コメント防止のため禁止ワードになっています。 URLを載せたい場合はhttp以降をのせるなど工夫してください。

それと、上の"この記事のコメント"とかかれた部分をクリックするとコメントフォームに飛べます。
コメントが大量でスクロールが面倒な場合などにご利用ください。


 メデスさん
言ってることは理解した
やってることも理解できた


ただし、書け!といわれたら3秒でマイッタ^ー^


 斬り+聖魔さん
はい ビックバン後のメイプルストーリー ゆっくりでもいいので宜しくお願いします





コメント非公開の場合はチェック





copyright © 2006-2008 かぜのこもりうた all rights reserved.
powered by FC2ブログ Material by web material * Essence
上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。