平成27年度秋期午後のC言語問題である問9の私の解き方・考え方について解説していきます。
問題
IPAの公式サイトで公開されています。このページでは問9についての解説を行います。
https://www.jitec.ipa.go.jp/1_04hanni_sukiru/mondai_kaitou_2015h27_2/2015h27a_fe_pm_qs.pdf
問題の概要
入室管理がテーマの問題です。文字列操作等の知識も必要になってくる問題です。
スポンサーリンク
設問1
設問1はプログラムの穴埋め問題です。早速プログラムをみてみましょう。
プログラムの流れは、基本的に「ファイルからデータを読み込み」と「読み込んだ内容を出力する」を繰り返すものになっています。
[ a ]はwhile文の中で実行されるgetRecord関数においてfscanf関数をEOFを返した時の処理になります。コメントにもあるようにfscanf関数がEOFを返した時はすでにファイルの最後までデータを読み込んでいる状態ですので、それ以上データを読み込むことができません。
ですので、次のファイル読み込みが行われないようにループを抜け出してやる必要があり、[ a ]では青枠で示すwhile文の終了条件が成立するようにlogEOFを設定してやる必要があります。したがって[ a ]はlogEOF = EOF;となります。
続いて[ b ]と[ c ]を考えていきましょう。[ b ]と[ c ]はレコードを表示するputRecord関数内のプログラムの穴埋めになります。プログラムを見てみましょう。
putRecord関数は図3のようにレコードを表示する関数になっています。大きく青枠と緑枠とオレンジ枠の部分に分かれており、それぞれ色に対応した部分を出力する処理を行っています。
まず[ b ]が含まれる青枠部分を見てみましょう。青枠では、出力しようとしているレコードのカードIDが前のレコードのカードIDと同じであれば(strcmp(cardID, lastID)の返却値が0)空白を出力し、違っていれば(strcmp(cardID, lastID)の返却値が0以外)カードIDと名前を出力する処理になっています。
この処理を行うためには、前のレコードのカードIDをlastIDに格納しておく必要があります。プログラムの他の箇所ではlastIDへの値の代入が行われていませんので、[ b ]でその処理を行う必要があります。ですので、[ b ]の答えはstrcpy(lastID, cardID)となります。
(2019/10/17追記:[ b ]の答えが lastID = cardID と間違っていましたので修正しました。申し訳ございません)
[ c ]は緑枠の中の処理になります。putSpaceに格納される値の計算式が[ c ]の答えとなります。
緑枠ではレベルごとに表示位置をずらして表示する必要ために、printf関数で空白を出力する処理を行っています。この空白の出力はwhileループの中でprintf関数をすることで行われています。
また、そのwhileループを続ける条件が(0 < putSpace–)になっていますので、putSpace * 20文字分の出力が行われることになります。
[プログラムの説明] – (3)に記載の通り、レベル1とレベル2では空白20文字分ずらし、レベル2とレベル3では空白40文字分ずらす仕様になっていますので、緑枠部分では20 * (レベル – 1)分の空白を出力する必要があります。したがってputSpaceには(レベル – 1)が格納されます。
[プログラムの説明]の冒頭部分に記載されている通り、ドア番号の10の位がレベルを表すことになっています。ドア番号はプログラムのdoor[3]に格納されていますので、door配列の1文字目、つまりdoor[0]がレベルとなります。
door[ ]はchar型ですので、数字に変換するためには’0’引いてやる必要があります。ですので、レベルを数字化したものは「door[0] – ‘0’」で計算可能であり、[プログラムの説明]の仕様を満たすためにはputSpaceは「door[0] – ‘0’ – 1」である必要があります。以上よりputSpaceに格納される[ c ]の答えは「door[0] – ‘0’ – 1」となります。
設問2
設問2ではプログラムに入退室記録に不整合があった場合にその旨を出力する機能が追加されたプログラムに関する問題になっています。
[ d ]はcheckLevel関数を追加する場所を問う設問になっています。checkLevel関数は設問2 – (1)に記載されている下記の処理を行う関数です。
「順序の不整合が起きた場合は、本来入退室の記録があるべき行のレベル1の印字位置に “***** Level x–>y”と印字する。これは、入退室者がこの前後にレベルxからyヘ移動したはずであるが、その記録がないことを意味する。」
この関数をプログラムの③〜⑧のどこに入れれば良いかを見て考えてみましょう。「レベル1の印字位置に」というところがポイントになっています。
設問1の[ b ]を解く際に見たように、青枠が実行されるとカードIDと名称、もしくはそれと同じ文字数分の空白が出力されます。これによりレベル1の位置まで印字位置が移動することになります。
青枠の前だと印字位置は1文字目ですし、緑枠が実行されると設問1の[ c ]で説明したように印字位置がレコードのエリアレベルに応じて移動してしまいます。したがって[ d ]は青枠の直後に実行しなければレベル1の印字位置に「 “***** Level x–>y」を出力することができません。ですので、[ d ]の答えは⑦となります。
[ e ]はclearLevel関数を追加する場所を問う設問になっています。
これは図4の入力レコードから図5の出力結果が得られている理由を考えると見えてくると思います。
注目すべきは図5の「 “***** Level 1–>0」の出力結果ですね。図4の入力レコードはドア番号21に関するものが最後のレコードになっており、この記録はプログラムの④のputRecord関数で出力されます。プログラム上④のputRecord関数の直後に⑤のgetRecord関数が実行されますので、ドア番号21の次にレコードがもう無いため、logEOFにEOFが格納されることになります。そうなると次のwhileループが終了し、そのままプログラムも終了することになります。
ですので、最後のレコードを出力するputRecord関数終了後から、whileループの最後までの間に「 “***** Level 1–>0」を出力するclearLevel関数を実行する必要があります。候補は③と④になりますが、clearLevel関数内でprintf関数が実行されるのはlogEOFがEOFもしくはcardIDとlastIDが異なる場合だけです。
③のputRecord関数直後にclearLevel関数が実行されたとしても、ドア番号21に関する出力をした直後ではlogEOFはEOFではありませんし、cardIDとlastIDも同じですので「 “***** Level 1–>0」は出力されません。その後getRecord関数が実行されてlogEOFがEOFになるのでwhileループが終了して「 “***** Level 1–>0」も出力されずにプログラムが終了してしまいます。
一方④の場合はlogEOFがEOFになった後にclearLevel関数が実行されるので、ドア番号21の出力の後に「 “***** Level 1–>0」が出力されて図5の印字結果にすることができます。ですので[ e ]の答えは④になります。
[ f ]と[ g ]に関しては、実際に図4をプログラムに入力したときの動きを追ってみて、「 “***** Level x–>y」のxとyが何であれば図5の出力結果になるかを考えてみると答えることができると思います。
例えば「 “***** Level 3–>2」を出力する時は、clearLevel関数のprintf時点では、levelが3、afterLevelが1、beforeLevelが2ですので、「 “***** Level level–>beforeLevel」でなければ話が合わなくなってしまいます。ですので、[ f ]の答えはlevel、[ g ]の答えはbeforeLevelとなります。
解いてみた感想
割と難易度の高い問題だった印象です。特に設問2の関数実行をどこに追加するかの問題が難しかったですね。
実は私は最初解いた時に答えを間違ってしまいました。理由は単純に問題の読み方が甘かったからですね。具体的には設問2の[ d ]を間違いました・・・。「レベル1の印字位置に」のところを読み飛ばしていたので違う場所を選択してしまいました。やはり問題文をしっかり読むということは大事です。
★オススメページ★
下記ページから他の回の解説もたどれます。他の回のC言語問題の解き方がわからない場合は是非読んでみてください!
本ページの図・プログラム・問題文について
図やプログラム、問題文はIPA公開の過去問題から引用しています。また図やプログラムに関しては説明に必要な部分に関してのみ加工して使用させていただいております。
出典:平成27年度 秋期 基本情報技術者試験(FE)試験区分 午後 問9
(1)のbの答え違います
これ文字列だからそれだと無理
っg さん
コメントありがとうございます。
ご指摘の通り、私の回答が間違っておりました。文字列なので strcpy を使うのが正しかったです。申し訳ございません。
早速記事の内容を修正させていただきました。ご指摘大変ありがとうございました。