平成30年度秋期午後のC言語問題である問9の私の解き方・考え方について解説していきます。
問題
IPAの公式サイトで公開されています。このページでは問9についての解説を行います。
https://www.jitec.ipa.go.jp/1_04hanni_sukiru/mondai_kaitou_2018h30_2/2018h30a_fe_pm_qs.pdf
問題の概要
列車の運行シミュレーションに関する問題です。楽しそうな問題ですが、リスト構造的な考えが用いられており、ポインタや構造体の知識も必要になる問題です。
プログラムに2つの構造体が出てきていますのでまずはそれを見てみましょう。
・block_info
問題の図1における区間3に対するblock_info構造体は下記のようになっています。
trainポインタは区間3内にいる列車2のtrain_info構造体のアドレスを指しています。
next[0]ポインタ、next[1]ポインタは次区間である区間4と区間7のblock_info構造体のアドレスを指しています。
区間3には列車2がいますので、signalの値はREDとなります。
区間7には列車がいない&次区間が1つしかないため、区間7におけるblock_info構造体ではtrainポインタとnext[1]ポインタはNULLとなります。
・train_info
図1における列車2に対するtrain_info構造体は下記のようになっています。
numberには列車番号である2が格納されています。
destポインタは列車2の終着駅を出口とする区間である区間6のblock_info構造体のアドレスが格納されています。
ここで出てきた「終着駅を出口とする区間」は、今後「終着区間」と呼ぶようにします
わかりにくい言葉なので
この2つの構造体を利用していろんなことを調べることができます。
例えば、ある区間にいる列車の列車番号を知りたければ(block変数をある区間に対するblock_info構造体とする)、
block->train->number
の値で知ることができます。
ある区間に列車がいるかどうかは
block->train == NULL
であるかどうかで調べることができます。
またある区間にいる列車の終着区間の信号の色を知りたければ
block->train->dest->signal
の値を見てやれば確認することができます。
スポンサーリンク
設問1
それでは設問を解いていきましょう。
[ a ]はset_signal関数のプログラムの穴埋め問題です。 [ a ]に条件文が入り、これが成立するときにその区間の信号の色を緑色にセットするプログラムになっています。その区間の信号の色を緑にセットするのは、その区間に列車がいない時ですので、
block->train == NULL
が成立した時に緑にセットしてやればset_signal関数の仕様通りに動かすことができます。
ですので[ a ]の答えは選択肢カのblock->train == NULLとなります。
設問2
設問2もプログラムの穴埋めですね。
[ b ]と[ c ]はproceed関数の穴埋めです。 [ b ]は条件文であり、これが成立する時にblock->train = NULL;
が実行されます。この処理はその区間にいる列車を取り除く処理になります。このtrainが他の区間に移動させられるのであれば他のblock_info構造体のtrainポインタにこのtrainのアドレスがセットされるはずですが、ここではそれが実行されていないので路線全体からこのtrainの列車が取り除かれる処理であると言えそうです。
つまり、ここで実行されている処理は「このblockにいる列車の終着区間がこのblockの区間」である時の処理です。ですので、[ b ]には「このblockにいる列車の終着区間がこのblockの区間」であるかどうかを調べるための条件文が入ります。
この区間にいる列車に対するtrain_info構造体はblock_info構造体のtrainポインタが指すアドレスに存在します。
つまり、この区間に対応するblock_info構造体をblockとすれば下記によりこの区間内にある列車を特定することができます。
block->train
また列車の終着区間がどこであるかはtrain_info構造体のdestポインタが指すblock_info構造体の区間になり、下記によりどこの区間であるかを調べることができます。
train->dest
したがって、「このblockにいる列車の終着区間がこのblockの区間」であるかどうかは、
block == block->train->dest
が成立するかどうかで判断可能です。ですので[ b ]の答えは選択肢ウのblock == block->train->destとなります。
[ c ]はある列車を次区間に移動させる処理 ↓ の一部になります。 [ c ]の選択肢としては、break、continue、returnがあります。
まずreturnは消えます。returnを実行するとこの時点で関数が終了することになります。proceed関数は「全ての区間」に対して列車移動を行う必要がありますので、1つの列車を移動した時点で終了してしまうと仕様通りの動きが満たせなくなります。
続いてcontinueで考えてみましょう。ここでcontinueが実行された場合、
for ( j = 0; j < 2; j++ ) {
に戻り、次の j に対するループ処理が実行されることになります。
そうなった場合不正アクセスが発生してしまいます。なぜなら次のループ時にも下記の条件文が実行されますが、continueをする直前にblock->trainにNULLが格納されますので、NULLアクセスしてしまうためです。
if (find_block(block->next[j], block->train->dest) == 1) {
ですので[ c ]でcontinueを実行するのは間違いです。
[ c ]では列車を移動した直後に j に対するループを抜けるようにbreakを実行するのが正しいです。したがって[ c ]の答えは選択肢アのbreakが正解になります。正解を「アのcontinue」と書いてしまっていましたので修正しました。
正しくは「アのbreak」です。間違った記載をしてしまって申し訳ございません。
find_block関数は第1引数で指定したblock_info構造体blockから第2引数で指定したblock_info構造体のdestに辿れる場合に1、辿れない場合に0を返す関数となっています。
上の方でも説明したように、block_info構造体にはnext[0]とnext[1]ポインタがあり、このポインタから次区間をたどることが可能です。ですので、blockのnext[0]もしくはnext[1]が指すblock_info構造体のnext[0]もしくはnext[1]が指すblock_info構造体の・・・・といった感じでnext[0]とnext[1]をずっと辿っていくことでdestにたどり着けるかどうかを判断することが可能です。find_block関数ではこの処理を再帰処理を用いて実現しています。
では、find_block関数内で、どのように引数を指定してfind_block関数を呼べば上記の処理を実現できるでしょうか?これが[ d ]で問われていることになります。
上記の通り、destにたどり着けるかどうかはblock_info構造体のnext[0]もしくはnext[1]を辿っていく必要があるため、第1引数としてはblock->next[i]を次のblockとして指定するのが正しいです。
第2引数のdestはずっと変える必要がないのでdestはそのままで良さそうです。
したがって、[ d ]の答えは選択肢オのblock->next[i], destとなります。
設問3
設問3は[プログラム3]実行後の結果が問われる問題です。
実際にプログラムを実行した時にプログラムがどう動くのかを確認していくのが確実だと思います。ここでは図1の絵を用いて列車や信号がどのように変わっていくかをみていきたいと思います(図のスペース上、各列車の終着駅の情報の記載は割愛しています)。
[プログラム3]におけるループ処理前のset_signals関数実行後は列車や信号は下記のような状態になります。set_signals関数では列車がいる区間の信号を赤に、いない区間の信号を緑にセットする処理が行われますので、単純に列車がいる区間の信号が赤に、いない区間の信号が緑になります。
続いてループ処理の中を見て行きます。まずは i = 0 の時のproceed関数実行後の状態を確認しましょう。下図のようになるはずです。注意点は吹き出しで書いています。一番proceed関数がややこしいと思ったのは、列車を移動させるときに、移動後の区間の信号は赤に設定するのに移動前の区間の信号は緑に設定しないことです。これが行われないので次区間に列車はいないのに進めない列車があります(下の例だと列車2)。
次には i = 0 の時のset_signals関数実行後の状態を確認しましょう。信号の色が下記のように変わります。
要するにループ処理内ではproceed関数内で移動できる列車を移動し、set_signals関数で区間に列車がいる or いないに応じて信号を更新するという処理を行っているというわけです。
ここからはproceed関数とset_signals関数両方を実行した後の状態のみ図示します。
まず i = 1 の時は下図のような列車と信号の状態になります。
i = 2 の時は下図のような列車と信号の状態になります。
i = 3 の時は下図のような列車と信号の状態になります。
この状態で[プログラム3]は終了します。上の図から[ e ]と[ f ]の答えはすぐに導けると思います。
列車4がいる区間は2なので、[ e ]の答えは選択肢イの2となります。
また区間2の出口は1つで信号は緑ですので、[ f ]の答えは選択肢アの「一つあり、その信号は緑を表示している」となります。
スポンサーリンク
解いてみた感想
列車の問題で一目見た感じだととっつきやすい問題ですが、問題の難易度は高い気がしました。
何より必要な知識が多い!構造体・ポインタ・再帰処理・breakやcontinue、などなどプログラミングやC言語の知識が必要になる問題が多かったです。
ただこれらの知識さえあればプログラム自体はそれほど難しくなく、答えも導きやすかった印象です。
★オススメページ★
下記ページから他の回の解説もたどれます。他の回のC言語問題の解き方がわからない場合は是非読んでみてください!
本ページの図・プログラム・問題文について
図やプログラム、問題文はIPA公開の過去問題から引用しています。また図やプログラムに関しては説明に必要な部分に関してのみ加工して使用させていただいております。
出典:平成30年度 秋期 基本情報技術者試験(FE)試験区分 午後 問9
空欄cに対する解答・解説の最後が「選択肢アのcontinue」になってますよ。
Naokintoshさん
ご指摘ありがとうございます。
早速修正しました。大変助かりました。ありがとうございます。