計算機言語
I
第13
回 動的なメモリの確保この資料
: http://www.math.u-ryukyu.ac.jp/~suga/gengo/2019-1/13.pdf
レポートへのツッコミ
2
乗を計算するのにライブラリ関数pow()
を使う人が多いですが, 2
乗くらいだと積を,
つまりa
2の計算 を, a*a
と書くのが普通です.
関数呼び出しは,
幾らかの処理(
前処理,
後処理)
をプログラムに要求しますが, a*a
は,
即座に計算できます. CPU
が速くプログラムが小さいので,
この講義でのプログラムでは気になりま せんが,
大きなプログラム(
例えば, Firefox
のようなグラフィカルなWeb
ブラウザや,
画像編集ソフト)
では,
このような細かいチリが積もって山となります.
もちろん
, 100
乗を計算するときには, pow()
を使います.
「臨機応変」を意識してください.
動的なメモリ確保
今回の内容は割に難しいとされる事です
.
少し,
細かい話をします.
プログラムには
,
実行時にならないとデータの大きさが決まらないことがよくあります.
例えば
,
この講義で使っているテキストエディタですが,
どのくらいの大きさのファイルを編集するのかは,
実行するまでわかりませんし,
入力をどんどんしていくと必要なメモリもどんどん増えていきます.
テキスト エディタの最初の回(
情報科学演習)
のときに述べましたが,
入力画面に表示されている内容は,
一次記憶装置 の中に保存されており,
その内容は実行しているプログラムが管理しています.
タイトルにある「動的なメモリの確保」とは
,
「実行中にプログラムが必要となったメモリをシステムに要 求してもらってくる」という動作の事です.
実用的なプログラムを作成しようとすると,
このような仕組みが 必要であり,
そのプログラムを記述するプログラミング環境も,
このような仕組みが利用できるようにしてあ ります.
今回は, C
におけるその仕組みの使い方です.
これまででは
,
「変数宣言」という形で,
プログラム内では必要なメモリを確保してきました.
しかし,
これ までのメモリ確保の方法は,
プログラム実行前に必要な変数の量が決まっているときだけです.
ポインタの復習
動的なメモリの確保では
,
ポインタを多用します.
ポインタについてもう一度復習します.
C
のプログラムでは,
一部の例外を除いて,
識別子(
名前,
関数や変数の名前のこと)
には,
実行時に一次記憶 内でのアドレスが付与されることになっています.
実行時のある識別子のアドレスのことを,
その識別子へのポインタ
(pointer
は何かを指し示す物という意味の英語)
と言います.
1
アドレス演算子
&
は,
識別子に作用させることができ,
その値は,
その識別子を指すポインタになります. C
では,
ポインタ(
アドレス)
の値を保持するための変数型,
「ポインタ型」を使うことができます.
例えば,
double *dp;
という宣言は
,
次のように読みます. (
下で「アクセス」と書いてある部分は,
代入や値を取り出すという意味 です.)
• dp
はポインタ(
アドレス)
の値を保持するための変数であり,
この識別子でアクセスできる値は,
何らか のアドレスである.
• *dp
という形でアクセスを行う際には, dp
の値の場所には, double
型の値を保持するだけのメモリが すでに確保されており,
アクセスできる値は,
その場所にあるdouble
型の数値である.
上の事から
,
ポインタ型変数に対する*
をdereference operater(
意訳して間接参照演算子)
と言ったりしま す.
ポインタ値(
アドレス値)
を経由して,
その場所にある値をアクセスするからです.
上の宣言では
, dp
には,
アドレスとしての意味を持てば, (
例えば整数値を)
代入することができます.
従っ て,
プログラム的にアクセス出来ない場所の値をdp
に代入しても,
「文法エラー」にはならず,
「実行時エラー」になります
.
また,
このようなプログラムでも,
実行時エラーが発生しないこともあり,
「隠れたバグ」がプロ グラムに残ることも,
数多く発生しています.
これは, OS
開発用に作られたC
の仕様(
特徴)
であり,
プログ ラマがその辺の責任を持つ必要があります.
これまでの事に関する補足
NULL
ポインタ. Null Pointer(
ナルポインタ,
ヌルポインタ)
と呼ばれるもので,
「何も指していないということが確定している」ポインタ値です
.
大文字のNULL
で参照されますが,
ヘッダファイルに何がしか の値が,
マクロ置換で与えられています.
その値は,
処理系依存です.
式には値がある
.
プログラムでは,
例えば,
a = b + c;
のようにして
,
色々な変数の値を変化させて行きます.
上のような物を「式(expression)
」と言います. C
では,
式そのものが値を持ち,
その値は左辺値です.
前回のプログラムで,
if ( ( fp = fopen("input.txt", "r")) == NULL)
という記述がありました
.
これは, 2
番目のカッコ内の式( fp = fopen("input.data", "r"))
が先 に実行されます.
そして,
この式の値がNULL
か否かをチェックしているわけです.
2
変数のキャストに関する補足
教科書には
,
変数を別の型に変換するキャストの方法が書かれています.
このように
,
明示的に変数の型を変えるキャスト以外にも「暗黙のキャスト」があります.
例えば,
異なる型 の変数の計算の際にこれは起きます.
double a = 1.0;
int b = 2;
double c;
c = a + b;
のようなプログラムです
.
上のc
の計算では, b
の値がdouble
型に変換されて,
計算が実行されます.
このよ うな時の暗黙のルールは,
次です.
char < int < float < double
これは
,
異なる型の計算があったら,
より右にある型にキャストして計算が実行されるという意味です.
この講 義で使っている処理系では, int, float
がともに32bit
なので, int
型からfloat
型へのキャストでは,
情報 落ちが起き得ます. float
型では,
指数部を情報として持つため,
仮数部の有効桁数は23bit
しかありません.
また
,
皆さんが好きな, pow()
というライブラリ関数も,
本来の引数はdouble
型ですが, int
型を渡してもdouble
型に変換して,
べき乗を計算してくれます.
これは,
ライブラリ関数がそのようにプログラムされているのです
.
malloc(), free()
に関する補足malloc()
で確保したメモリ領域は,
目的の型のポインタにキャストしないと, warning(
警告)
が出る処理系が多いと思います
. warning(
警告)
とエラー(error)
の意味の違いに注意してください. worning
は,
「C
の文 法的には許容されるが,
実行時エラーが起きる可能性がある.
」というときに出されます.
free()
関数ですが, fclose()
と似たようなことがあります.
プログラムが終了するときに
, malloc()
で確保されたメモリは,
自動的にシステムに返されます.
従って,
こ の講義で書くようなプログラムでは, free()
を使わなくても問題になる事はありません.
ただし,
コンピュータの中には
, network
サービスを実行するプログラムのように, 24
時間365
日動き続けることを想定しなければならないプログラムもあります
.
このようなプログラムでは, malloc()
で確保したメモリは,
要らなくなったら
free()
でシステムに返却しないと,
使える記憶域が無くなって,
システム全体が停止してしまいます.
教科書
,
プログラム10.3
で注目すべきは, malloc()
で確保した連続したメモリは,
配列のようにアクセスで きることです.
ベクトルの添字の値をずらすのは
,
プログラマの慣れがあれば不要だと思いますので,
プログラム10.4
は飛 ばします.
3
ポインタへのポインタ
「ポインタ型変数」も識別子である
.
という認識は重要です.
また「ポインタ型変数の配列」もC
では多く 現れます.
レポート問題
:
締め切り, 7
月22
日(
月) 10:00 AM
今回は
,
適切なレポート問題を設定できなかったので,
次をレポート問題にします.
前回の授業で
, 40
人分の成績を構造体seiseki_t
の配列に読み込むプログラムを書きました.
この読み込 むプログラムに追加して, seiseki_t
の配列を変数score
の降順(
大きい方から小さい方に並ぶよう)
に並び 替えるプログラムを書け(
つまり,
成績の良い順に配列を並び替える).
並び替えの方法は,
第6
回の講義で解 説したバブルソートで良い.
より効率的なアルゴリズムを知っていれば,
それを利用しても良い.
プログラム では,
並び替えた結果を出力する部分も含める事.
件名
: gengo2019-1 report 13-1.
上のレポート問題をこれまでの講義で述べた方法だけで実装するなら
,
それはあまり 効率良いプログラムではありません.
次回