計算機言語 I 第 9 回 ポインタ ( その 1)
この資料: http://www.math.u-ryukyu.ac.jp/~suga/gengo/2023a/09.pdf
レポートへのツッコミ
とりあえず,私が書いたプログラムを例示しておきます.
unsigned long long fibonacci(int n) /* 繰り返し版 */
{
unsigned long long a=1;
unsigned long long b=1;
unsigned long long fib;
int i;
if (n==0||n==1) return 1;
for (i=2; i<=n; i++){
fib= a+b;
a=b;
b=fib;
}
return fib;
}
unsigned long long fibonacci(int n) /* 再帰版 */
数列を配列変数を用意して順に計算するプログラムを書いた人も居ました. 50の配列では差は現れません
が, 10000項目の計算とかだと,配列の方が計算が遅くなります. 途中の項も後で利用するのなら, 配列を用意
して途中項を保存しますが,最終結果だけが必要ならそのようなプログラムは書きません. 遅くなる理由は,今 日の講義で少し解説します.
プログラムの書くときの方針ですが, 読みやすく簡潔に書く.
を意識してください. 読みやすさは,以前インクリメント演算子の時に述べましたが,どのように処理が実行さ れるかを分かりづらくしてはいけないという意味です. 簡潔さは, 無駄な変数や処理は省くということです. 今回の課題で配列を用いるのは,無駄だといえます.
今回もFibonacci 数列が計算できていないプログラムがありました. 実行結果が明らかにおかしいので,そ
れには気付くことができるようになって下さい.
ポインタの解説への補足
ポインタや先週のレポート問題で処理時間に大きな差が出る理由を理解するには, (C の処理系がプログラ マに見せる)コンピュータでの処理の仕組み(のモデル)の概要を理解するのが早道だと思います.
ペンタブレットを利用してそれを解説しますので,中間モニタもしくはプロジェクタを見て下さい.
教科書 p. 102, プログラム 7.1
このプログラムは,複数回実行してみてください. 実行の度に出力結果が違うと思います.
ポインタ型変数
Cの解説をした本を読むときに注意すべきなのは,「ポインタ」という言葉の2重性だと思います.
• メモリ内の場所を示す意味でのポインタ
• 上の情報を保持するための変数であるポインタ型変数
Kernighan – Ritchieの本(Cのバイブル)の記述が上の事を明確に書いていないために,それ以外の本もあま
り明確な区別をしていないように見えます. 後述するように, 配列の先頭へのポインタを配列名で参照できま すが,この配列名で参照できるポインタ値は,実行時に変更できません. すなわち「ポインタ定数」と呼べるも のなのです.
Cの特徴のひとつが,ポインタ変数を利用してハードウェアにかなり密着したプログラムを書くことができ ることです. これは Cの開発動機である,「OS を高級言語で記述する」ということからは当然の仕様なので すが,ハードウェアの知識がない人には,難しい言語仕様になってしまいます. また,多くの高級言語では,裏 でCのポインタに相当する処理をさせるにしても,プログラマにはそれを隠すような仕様になっています. そ のため, 処理速度が考えた程上がらないということがあるのですが, Cは, CPU の実際の動作を想定したプロ グラムを書くことができるため,処理速度の高速化がやりやすいというメリットがあります.
通常の高級言語で,ハードウェアに密着したプログラムを書くことはできないようになっているのは,その ようなプログラムはシステム全体に影響を与えることができ, 最悪, 間違ってシステムを止めてしまうプログ ラムや, 意図しない動作を実行させる可能性があるからです. これは,セキュリティホールにも繋がります. 実 際,その弱点をついたコンピュータウィルスが過去にたくさん作られています.
Cが普及する以前は, ハードウェアに密着したプログラムは, アセンブリ言語(機械語)を直接書いていまし た. しかし,アセンブリ言語を書くのは大変で, Cが普及したためにそのようなプログラムは, Cで書かれるよ うになりました.
教科書 p.105, プログラム 7.3
このプログラムは, その前にある記述からすると誤植です. おそらく次のプログラムを掲載するつもりだっ たのでしょう. ただし,どちらの場合も実行時エラーになります.
#include <stdio.h>
int main() {
int *p=NULL;
*p = 10;
return 0;
}
Cの難しいところのひとつに, 上のような文法エラーは無いが実行時エラーが起きるプログラムが,簡単に 書けるところにあります. 通常の高級言語は,「文法的に正しいなら,実行時にはエラーにならない」を目指し て設計されます.
上の例(教科書プログラム 7.3)は, OS によって書き込みが禁止されている記憶場所にデータを保存しよう として, OSからその動作を拒否されたわけです.
教科書p. 106,「重要ポイント」の(2)は誤解を与える表現です. 正確には,次のように書くべきです.
(2) (double *pという宣言があると)ポインタ変数 pに代入を実行する際, 代入されるアドレスの場所に
は,double型を保持するのに必要な記憶領域がすでに確保されていなければならない.
ポインタと配列
ポインタと配列との関係で重要なのは,次だと思います.
配列の先頭へのポインタは,配列名を参照することによって得られる.
ポインタの加法(減法)と配列要素との関係は, 教科書にある通りで,これも Cの特徴です. 配列の各要素を
どうでも良い注意: 上のポインタと配列の関係ですが, C言語で見えるアドレスの差と, 実際のハードウェア でのアドレスの差は今では一致しません.
「仮想記憶, 仮想機械」という技術を利用して,プログラマに, プログラム7.5のような実行結果で正しくプ ログラムが動くように見せています. また, この技術を用いて,セキュリティホールを塞いでいます.
今回の講義で,ポインタ変数が持つアドレス値を出力させました. 実際のハードウェアも,アドレスで主記憶 にアクセスしています. しかし,実行時にアクセスしているハードウェアのアドレスと,今回プログラムを利用 して出力したアドレスは,実は一致しません.
今のCのプログラムでは,プログラマは利用している計算機の設定されたメモリを全て使えるという形でプ ログラムを書くことができます. そのように書かれて作られたプログラムも, 実行時には計算機の全てのメモ リを使うという形で実行します.
しかし, 現実には,多くのプログラムが同時に実行されているので, そのようにすると「メモリの取り合い」
がおきます. 仮想記憶というのは,この取り合いを解消するための仕組みで, プログラム毎に,どのメモリ領域 を利用させるかをOSの側で調整しているのです.
レポート問題
締切: 6月19日(月) 10:00(JST), 件名: gengo2023 report 9
char, double, long double, long longのそれぞれの型に対して要素数が 2個の配列を作り,先頭と次 の要素のアドレス値を出力するプログラムを書き,先頭と次の要素のアドレスの差は幾つになったかをレポー トせよ.
レポートは,プログラムとアドレスの差を両方記述すること.