期末試験 2024#
答えの表示/非表示
時間:60分,問題:16問,配分:各6点(出席1点,コメント3点)
 SCコードとJavaのバイトコードに関する補足資料あり
1. 論理型言語#
Prologに関する説明のうち,適切なものを全て列挙せよ.
- Prologは命令型言語の一種である
 - Prologは宣言型言語の一種である
 - Prologのプログラマは処理の手続きを記述する
 - Prologの内部処理は単一化に基づく推論の繰り返しである
 - Prologでは単一化に失敗した際に自動的にバックトラックが行われる
 - Prologでは開発者による強制バックトラックが可能である
 
Exam-answer
2,4,5,6
 以下はProlog処理系への質問の入力である.=演算子により左右2項の単一化を試みている.単一化に成功する処理の番号を全て列挙せよ.
?- 1 = 1.              % 1
?- 2 = 3.              % 2
?- X = 1.              % 3
?- X = A.              % 4
?- f(1) = f(1).        % 5
?- f(X) = g(X).        % 6
?- f(X, Y) = f(1, 3).  % 7
?- f(g(X)) = f(g(1)).  % 8
Exam-answer
1,3,4,5,7
 以下はPrologプログラムである.最大公約数を表す述語gcd(A, B, X)の記述であり,これは整数値AとBの最大公約数がXである,という関係を表す.A < Bとする.???の部分を埋めて述語gcd(A, B, X)を完成させよ.
 GMがCの祖母という関係を表す述語grandmother(GM, C)の論理式を記述せよ.以下2つの述語を用いること.論理式のヘッドとボディの全てを記述すること.
child(C, P):CがPの子供という関係を表す述語female(F):Fが女であることを表す述語
2. C言語#
Cソースコードと等価なSCコードのペアを示す.各ペアについてSCコード内の???の部分を埋めよ.以下の点に注意すること.
???は1行以上の複数行で構成される- 局所変数のベースポインタからの相対番地は,
iを3,jを4とすること - SCコードの各行のアドレスは,数値ではなく記号番地(
xやy等)で表すこと - 最適化は禁止とする.例えば
i = 10 + 20;をi = 30;と解釈してはいけない 
 以下SCコードの???の部分を埋めよ.
 以下SCコードの???の部分を埋めよ.
 以下SCコードの???1の部分を埋めよ.
 Q7のSCコードの???2の部分を埋めよ.
 SCコードでは関数実行時にENT命令を用いて,戻り番地と動的リンクのメモリセルをスタック上に確保している.この2つのメモリセルの役割として最も適切なものを全て選択せよ.
- 大域変数用のデータ領域
 - 関数呼び出し時の引数データの保持
 - 関数終了後の元関数への復帰
 - 関数終了後のレジスタ値の復元
 
Exam-answer
3
3. Java言語#
Javaソースコードと等価なJavaバイトコードのペアを示す.各ペアについてバイトコード内の???の部分を埋めよ.以下の点に注意すること.
???は1行以上の複数行である- 局所変数のベースポインタからの相対番地は
iを3とすること - バイトコードのアドレスは数値ではなく記号番地(
xやy等)で表すこと - 最適化は禁止とする.例えば
i = 10 + 20;をi = 30;と解釈してはいけない - int定数のプッシュ命令は値のサイズに依らず,常に
bipush命令を使って良い _を含むオペランドなしバイトコード命令は,_を含まない汎用命令で代用せよ(istore_0はistore 0とすること)
 以下バイトコードの???の部分を埋めよ.
 以下バイトコードの???の部分を埋めよ.
 以下はあるインスタンスoのメソッドm()を呼び出すソースコードと,それに対応するバイトコードである.メソッドm()は一つの引数を受け取る.oの相対番地は3である.バイトコード命令invokevirtualがJVM上で行う処理として適切なものを全て選択せよ.
- スタック上の値の比較
 - 局所変数の値のプッシュ
 - 現在のレジスタ値の退避
 - スタック最上部への引数の値のプッシュ
 m()用の新たなスタックフレーム領域の構築
Exam-answer
3,5
 invokevirtualの前にaload命令を用いてインスタンスoの参照をスタックに積み上げる理由として,適切なものを全て選択せよ.
oをm()内でのthisとして用いるため- メモリ領域をスタック形式で利用するため
 - 引数のサイズに応じて効率的にメモリを利用するため
 m()終了後に元スタックフレームを再構築するため
Exam-answer
1
以下はJavaによる局所変数の宣言と初期化である.各局所変数について,初期化された値自体がスタック領域・ヒープ領域・メソッド領域のどこに格納されるか,それぞれ答えよ.
int i       = 10;               // 1
int j       = list.size();      // 2
int a[]     = {1, 2, 3};        // 3
double d    = 3.14;             // 4
String s    = "test";           // 5
Exception e = new Exception();  // 6
Exam-answer
- スタック:1,2,4
 - ヒープ:3,5,6,7
 
4. その他の言語(例外処理)#
以下のJavaメソッド宣言について,コンパイルエラーとなるメソッドの名前を全て列挙せよ.E1は検査例外,E2は非検査例外とする.
void f1() { throw new E1(); }
void f2() { throw new E2(); }
void f3() throws E1 { throw new E1(); }
void f4() throws E2 { throw new E2(); }
void f5() {
  try {
    throw new E1();
  } catch(E1 e) {}
}
void f6() {
  try {
    throw new E2();
  } catch(E2 e) {}
}
Exam-answer
f1()
以下はJavaを実行した際に発生したスタックトレースの内容である.null参照例外が発生していることが確認できる.null参照操作を直接的に行っている箇所のファイル名と行番号を答えよ.
ERROR!
Exception in thread "main" java.lang.NullPointerException:
    at Parser.parseInt(Parser.java:40)
    at Parser.parse(Parser.java:30)
    at Main.run(Main.java:20)
    at Main.main(Main.java:10)
Exam-answer
Parser.javaの40行目
補足資料#
SCコード命令一覧#
stack[]:スタック領域,sp:スタックポインタ,bs:ベースポインタ,pc:プログラムカウンタ
| # | SCコード | 意味 | スタック領域とレジスタに対する操作 | 
|---|---|---|---|
| 1 | LDC a |  load constant | sp++; stack[sp]=a; |  
| 2 | LLD a |  load local | sp++; stack[sp]=stack[bs+a]; |  
| 3 | LGD a |  load global | sp++; stack[sp]=stack[a]; |  
| 4 | LLA a |  load local addr | sp++; stack[sp]=bs+a; |  
| 5 | LGA a |  load global addr | sp++; stack[sp]=a; |  
| 6 | STL a |  store local | stack[bs+a] =stack[sp]; sp--; |  
| 7 | STG a |  store global | stack[a] =stack[sp]; sp--; |  
| 8 | STO |  store at indexed addr | stack[stack[sp-1]]=stack[sp]; sp-=2; |  
| 9 | IND a |  indexed fetch | stack[sp] =stack[stack[sp]+a]; |  
| 10 | UJP L |  jump | pc=L; |  
| 11 | FJP L |  jump if false | if(stack[sp]==0){pc=L; sp--;} |  
| 12 | ENT a |  enter block | sp=bs+a; |  
| 13 | MST |  mark stack | sp++; stack[sp]=base; sp++; |  
| 14 | CUP a※1 |  call user procecure | stack[sp-a]=pc+1; bs=sp-a-2; pc=stack[bs]; |  
| 15 | RET a※2 |  return from function | sp=bs-a; pc=stack[bs+2]; bs=stack[bs+1]; |  
| 16 | BOP o※3 |  binary operation | sp--; stack[sp]=(stack[sp])o(stack[sp+1]); |  
| 17 | UOP o※4 |  unary operation | stack[sp]=o(stack[sp]); |  
| 18 | HLT |  halt | EXIT |  
※1 aは呼び出す関数の引数の数
 ※2 関数の返り値がvoid型のときa=1,int型の時a=0
 ※3 oは二項演算子(+ - * / > >= < <= ==等)
 ※4 oは単行演算子(+ - !等)
Javaバイトコード命令一覧#
stack[]:スタック領域,heap[]:ヒープ領域,
 optop:optopレジスタ(スタック最上部),frame:frameレジスタ(制御領域),
 vars:varsレジスタ(スタック最下部),pc:pcレジスタ(プログラムカウンタ)
| # | 命令 | スタック領域とレジスタに対する操作 | 
|---|---|---|
| 1 | bipush c |  optop++; stack[optop]=c; |  
| 2 | ldc a |  optop++; stack[optop]=heap[a]; |  
| 3 | iload a |  optop++; stack[optop]=stack[vars+a]; |  
| 4 | aload a |  optop++; stack[optop]=stack[vars+a]; |  
| 5 | istore a |  stack[vars+a]=stack[optop]; optop--; |  
| 6 | astore a |  stack[vars+a]=stack[optop]; optop--; |  
| 7 | iadd |  optop--; stack[optop]=stack[optop]+stack[optop+1]; |  
| 8 | isub |  optop--; stack[optop]=stack[optop]-stack[optop+1]; |  
| 9 | goto L |  pc=L; |  
| 10 | if_icmpne L |  if(stack[optop]!=stack[optop-1]){optop-=2; pc=L} |  
| 11 | if_icmpeq L |  if(stack[optop]==stack[optop-1]){optop-=2; pc=L} |  
| 12 | if_icmpge L |  if(stack[optop]>=stack[optop-1]){optop-=2; pc=L} |  
| 13 | if_icmpgt L |  if(stack[optop]> stack[optop-1]){optop-=2; pc=L} |  
| 14 | if_icmple L |  if(stack[optop]<=stack[optop-1]){optop-=2; pc=L} |  
| 15 | if_icmplt L |  if(stack[optop]< stack[optop-1]){optop-=2; pc=L} |  
| 16 | invokevirtual m※1 |  stack[optop+v+1]=pc;stack[optop+v+2]=vars;stack[optop+v+3]=frame;vars =optop-p;frame=optop+v+1;optop=optop+v+4;pc=m; |  
※1 vはメソッドm()の局所変数の数,pはm()の引数の数