[前回まで] メモリモデル # メモリ:実在するものをモデル化 # アドレスで操作ができる # サイズ持っている # 情報を記録できる # => 「変数」という「概念」につながってゆく メモリを操作するには、アドレスがあればよい アドレス => C 言語では、「変数名」に相当する メモリ : 1 byte のセルが並んでいる 個々のセルは、アドレスで区別できる 個々のセルは 1 byte の情報を個別に記録可能 read/write 可能 メモリ C 言語 情報の記録 メモリセル 変数 区別 アドレス 名前 区別の形 数値 ラベル?(区別はできても操作できない) 処理 色々可能 区別しかできない 計算 関数の引数に渡せる 関数の値にできる C 言語では、基本は「変数」という抽象化がおこなれている => ほとんどの場合は、その背景である、メモリを意識する必要がない needs : メモリモデルを意識しないとできない事がある 文字列に + 1 すると、長さが短くなる理由 scanf で、変数の前に & を付ける理由 最初は、おまじない => メモリモデルを利用するとわかる seeds : メモリモデルを具体的に処理する機能が用意されている C 言語では、 変数名からそのアドレスを取り出す演算 & 逆に アドレスから、変数のように値を参照したり、変更可能にする演算 * が用意さている。 => メモリモデルを意識したプログラムが作れる [今日の話] メモリを意識したプログラミング & を利用する事により、変数のアドレスを見ることができる 値は、 %p で表示して、確認できる p-001.c 〜 p-005.c 基本、変数が異なれば、アドレスも異なる => 個々の変数は、別々に値を保持できる(独立) 特に、関数の呼び出し側と呼び出される側も、異なるメモリを利用 => 互いに独立 <= 「値」を共有するために、「引数変数」に「値」がコピーされる 変数に対応する領域は、「再利用」される 関数の中の変数は、関数呼び出しが行わた時点で有効(他の用途に利用されない事が保証される..)になり、関数から帰ると、無効(その領域は、再利用可能な形になる..)になる。 => 変数の「生存時間」と呼ぶ => 変数に対応するメモリ領域は、何度か再利用される => 「前の利用のごみが残っている」ので「初期化忘れ」が問題となる 再帰呼び出し 関数の呼び出しがあるたびに、関数で利用される変数には、 別のメモリ領域が割り当てられる => 再帰呼び出しを可能とする原理 「アドレス値」 メモリ上のアドレスを表す値(%p)で表示されたもの 「ポインター値」 数値の大きさとしては、アドレス値を持つが、それと一緒に、 「型」情報を持っている !! & 演算子 : 変数からアドレス値をとる => 変数から、ポインタ値を取り出す演算子 int 型の変数に & を付けた結果得られる値(ポインタ値)の型は、 (int *) 型になる # 「int *pi」と書くと、変数 pi は(int *) 型になる # これは、 「*pi が、int 型になる」という意味 # 「int *p」=> 「(int *)p」なので、p は(int *)型 # 「int *p」=> 「int (*p)」とすると(*p) が(int)型 ポインタ型の値は、アドレス値と同時にそれがどの型の 変数のアドレスかの(型)情報を保持している # アドレス値は、単なるアドレスなので、 # そのさしている先が、何の型かの情報を持っていない 「ポインター値」 = アドレス値 + 型 = サイズ + 演算の手段 !! 「型」: コンパイル時(C 言語)の概念 !! アドレス値(サイズ) : 実行時(メモリモデル/CPU)の概念 !! => コンパイルによって、これらの情報は失われる(別の形になる) !! 特に !! 関数の引数に渡されるものは、「値」(型情報はない) !! 関数の呼び側と受け側が異なるように扱えててしまう !! # 利点と欠点がある(ほぼ欠点だが..) ポインタ値の操作 アドレス値の操作 ( 加算・減算 ) 型情報の操作 キャスト [まとめ] ポインター値 変数の保存されているメモリのアドレス(アドレス値)と、 その変数の型の情報の組み合わせの事 ただし、型情報はコンパイル時のみ保持され、実行時は、アドレス値しかない 関数の値として、渡されるのは、アドレス値だけ => 両側で、型情報の了解が必要 T 型の値を保持する変数のポインタ値の型は (T *) 型となる 操作としては、 アドレス値の変更 (加算減算) 型情報の変更(キャスト) が可能 変数から、ポインター値(&:アドレス演算子) ポインター値から変数(*:間接参照演算子) の双方向の変換が可能 特に、関数間で、「変数」そのものを渡す事ができないが、 「変数」の「ポインター値」は渡せる => 関数内で、ポインター値を経由する事により、 関数の外の変数の値も(参照と)変更が可能 ==