コンパイラ 2012年10月29日 酒居敬一@A468(sakai.keiichi@kochi-tech.ac.jp) http://www.info.kochi-tech.ac.jp/k1sakai/Lecture/COMP/2012/index.html
意味解析 前回までの内容 今回の内容 構文解析では表面的な形式の整合性だけを見た。 各字句の意味に立ち入った解析とエラーチェックを行う。 「識別子名=識別子名+識別子名」と推定された式があったとして、 識別子名が変数名なのか関数名なのかは関知しない、では片手落ち。 今回の内容 各字句の意味に立ち入った解析とエラーチェックを行う。 識別子を変数・関数・配列・定数などに区別する。 そのための名前表に要求される情報を明らかにする。 実際の例を挙げる。
名前表 何の名前か? 型 語長 種別 アドレス 対応するソースプログラム中の行・桁 変数・関数・仮引数・定数・一時変数・自動変数など。 整数・実数・文字・文字列・配列・関数・構造型など。 語長 1バイト・2バイト・4バイト・8バイト スカラー・ベクター。 種別 全域的・ブロックなど。永続的・一時的・自動的など。 アドレス 対応するソースプログラム中の行・桁
名前表を使ったエラーチェック 二重宣言のチェック 未定義チェック 左辺値チェック 型の整合性チェック 同じ名前が2度定義されてるとエラー。 ただし、通用範囲外に関してはエラーにならない場合が多い。 未定義チェック 通用範囲内に参照する名前の物がなければエラー。 左辺値チェック 代入文・代入式の左辺に代入可能な物が置かれているか? 型の整合性チェック 型が同一視できるかどうか? 型を自動変換してもいいかどうか? なお、ここでできるのは静的な型チェックのみ。
名前表の探索1 名前から表中の該当エントリを探さないといけない。 線形探索 二分探索 ハッシュ 探索にO(n)必要。アルゴリズムは単純。 探索はO(n)必要。追加・削除を考え、構造型データで木を生成。 ハッシュ 開番地法 名前の個数が配列の大きさまでという制約を受けてしまう。 分離連鎖法 衝突が起きても使い続けることができる。一般的な方法。
スコープ(通用範囲) CやJavaでは、名前の通用範囲が決まっている。 名前表の名前は通用範囲と組み合わせる必要がある。 ファイルやブロックといった単位で範囲を決めている。 通用範囲の外側では、内側で定義した物は存在しない。 同じ通用範囲内で同名の物を定義するとエラー。 通用範囲の外側の物と同名の物を内側で定義することは可。 この場合、その名前で最も内側の物が見える。 名前表の名前は通用範囲と組み合わせる必要がある。 参照しようとしている場所から範囲外へ参照対象を探す。 ストレージクラスと通用範囲は別の概念。 ストレージクラス:自動変数・静的変数・インスタンス変数 通用範囲:全域・ファイル・クラス・ブロック
名前表の探索2 通用範囲ごとの名前表へのポインタをスタックに置く。 通用範囲に入ったら新しい名前表をスタックへプッシュ。 通用範囲から出たらポップ。 名前はスタックトップ側の名前表から、ボトム側の名前表へ 順に調べていく。 全域 ファイル 関数 ブロック1 ブロック2 スタックボトム スタックトップ
例:数 Cの場合を取り上げる。 float x = 1.0;としておきます。 sizeof(float)とsizeof(long)は同じ4バイトとします。 sizeof(char)は1バイトとします。 式 その値 型 1/10 整数 1/10.0 0.1 浮動小数点数 x+33554432 33554432.0 (long)x + 33554432 33554433 *((long *)(&x)) 0x3F800000 1065353216 (char)-1 0b11111111 (unsigned char)0xFF 1==2 33554432は2の25乗。
キャストのルール(抜粋) 整数の格上げ 整数と浮動小数点数 浮動小数点型 算術変換 元の型(char, short)のすべての値がintで表せればintに、 unsigned intで表せればunsigned intに変換する。 整数と浮動小数点数 浮動小数点数を整数に変換するとき、小数部分は切り捨て。 整数を浮動小数点数に変換するとき、最も近い値に変換。 浮動小数点型 より精度の高い型に変換するとき、値は不変。 より精度の低い型に変換するとき、表現可能な近い値に変換。 算術変換 2項のうち大きなほうの型に変換される。 2項のうち精度の高いほうの型に変換される。
ひどいプログラムの例 #include <stdio.h> extern int a, b, c; int hoge(int x) { return x + 100.0; } int main() int c; unsigned int i; c = hoge(a); hoge = b; for(i = 6; 0 <= i; i--){ printf("Hello!\n"); return main; ひどいプログラムの例 [sakai@star 2011]$ gcc -Wall -Wextra hoge.c hoge.c: In function `main': hoge.c:16: error: invalid lvalue in assignment hoge.c:18: warning: comparison of unsigned expression >= 0 is always true hoge.c:21: warning: return makes integer from pointer without a cast
演習(予習) 学生番号:____________ 名前:______________ C言語で全域的な変数を定義したとする。 この定義がコンパイラにより次のように翻訳された。 これらを次のように使うとエラーになる場合があった。 どこがエラーになるでしょうか?その理由は? int a = 20; int b[3] = {1, 2, 3}; a: .word 20 b: .word 1, 2, 3 int *p = b; a = 1; b = 9; p += a; a = p[0] + b[1];