宮商定時制計算部

基本情報技術者試験
平成29年秋午後問9 C言語

顧問の先生の計らいで、この度、誠に僭越ながら基本情報技術者試験の言語問題の解説をさせていただく運びとなりました。
問題を理解する際の参考になれば幸いです。

設問1


プログラム中の穴埋めを行う問題です。
空欄aより先に関数find_charの呼び出しが行われているので そちらの空欄から先に見ていきます。
空欄cの条件を満たした時点でi番目のアドレスを返却し、 そうでない場合はNULLを返却して終了していますね。
関数find_charの仕様は以下のように説明されています。
文字列str中で、文字chが最初に現れる位置を求める とあるので、引数で渡されたstrの先頭から走査し、
「対象の文字が文字chと一致すれば」
その場所のポインタを返却するようにすれば良さそうです。

文字が「一致」している必要があるので選択肢からイとウは除外できます。
また、先程の関数find_charの仕様やプログラムの説明から 英字の大文字と小文字の区別はしない とあるので単純な比較のみを行うアもダメです。
オやカは、一見すると正しいように見えてしまいますが片方ずつにtolowerをかけたものを比較してしまっているので仕様通り動いてくれません。
ウであれば、お互いを小文字で比較するので仕様通りの結果が得られます。
よってcはウが正解です。

返却値がNULLでなけば(=同一文字が存在していれば)while文が実行され、psizeに空欄aが代入されます。
このpsizeは宣言時に 文字の並びの長さ とコメントされていますが、一体なんの文字でしょうか。
空欄aがまた後回しになってしまいますが、psizeがどのように使用されているかを見るために関数is_palindromeを見てみましょう。
仮引数に渡されたpsizeは同関数内ではsizeとして扱われており、変数rの初期化に使用されています。
関数is_palindromeの仕様は以下のように説明されています。
文字の並びcharsが回文がどうかを判定する とあるように、この関数内で実際に回文であるかどうかの判断が行われているみたいです。
for文の更新部では、lとrがそれぞれインクリメント、デクリメントされていますね。(lはleft、rはrightの略でしょうか)
ループごとの変数の変化や継続条件式を見ると、どうやら外側から内側に向かって文字を比較しているようです。
2つのwhile文は、記号や空白を飛ばす(無視する)処理です。
返却値は回文の場合は'1'を、 回文でない場合は'0'とあります。プログラムを見てみるとforループ中に空欄bの条件が真になれば0を、そうでなければ1を返しているので、空欄bには回文でない場合の条件が入れば良さそうですね。
回文ではない(文字が一致しない)条件にしたいのでアウオは除外、添字で比較しているイも除外、エでは大文字と小文字を区別せずに比較できないので、bはカが正解となります。

is_palindromeの動作から、psizeには両端に同一文字が存在してる文字列(以下、仮回文と呼びます)の右端の文字数、つまり仮回文の長さが入っていれば良いことがわかります。空欄aの選択肢ではiだけを返却しているアや現在の文字のアドレスからテキストの先頭アドレスを引いているイウは除外、文字数を求めたいので値を1足しこんだオが正解になります。

設問2


プログラムの変更に関する問題です。

関数find_charを、同一の文字が複数存在する場合に長さが最も長いものを表示するようにしたfind_last_charに置き換えています。
プログラムの実行順序は空欄eのほうが先なのでこちらから解いていくことにしましょう。
選択肢を見てみるとどの回答も添字iがcount(かそれに-1したもの)で初期化されていますね。
find_last_charの説明によればcountは"文字数"となっていますが一体なんの文字数なんでしょうか、確認してみましょう。
引数countは16行目でtextlen - i - 1として渡されています。
文に当てはめてみると、この式は終端文字から比較対象の次の文字までの長さ(文字数)になっていることがわかります。

ここで気をつけなくてはならないのが、添字演算子[]を使用した場合数字はゼロ始まりであるということです。
strからn文字離れているstr[n]からstrそのものであるstr[0]までを見れば良いので空欄eはエになります。

空欄dでは2回目のfind_last_charの引数countに渡す仮引数の部分が問われています。
16行目のfind_last_charでは捜査対象の文字列としてtextlen - i - 1を渡していましたがここではどのような値を渡せば良いでしょうか。
実際に回文を当てはめて考えてみましょう。
もし16行目で見つかった仮回文の中にある同一文字が一つだけだった場合は27行目ではNULLを返せば処理が進みますが、
同一文字が複数あり、かつ外側の文字が回文ではない
場合には再度文字列を走査する必要があります。
16行目と同じ範囲を指定すると同じ文字が引っかかってしまうので、終端から走査している本プログラムでは 「以前の走査範囲 - 1」になる値を渡すことができれば良さそうです。
前回の走査範囲(psize(=count) - 1) - 1 になっていれば良いわけですから、空欄dはオが正解となります。

設問3


変数の呼出回数などトレースに関する問題です。

関数find_palindromeが回文の表示を終了するまで の関数find_last_charの呼出回数ですので、文字列中の回文
Madam,_I'm_Adam
の仮回文を発見するまでの呼出回数がわかれば良いことになります。
先頭の'N''o'は同一の文字が文中に存在しないのでそれぞれ1回、'!'は英数字では無いので無視、Mは末端の "gym"から"my""Graham""Adam"の4つまで戻り、計6回の呼出で回文の表示が行われることがわかります。
よって空欄fはオが正しい答えになります。

最後の呼出"Adam"の'm'における引数countは、走査対象の文字列から(ゼロ始まりの添字として)1を引いた値「psize - 2」になっていれば良いので、
Madan,_I'm_Adamの文字数15から2を引いた13になり、空欄gはアが正解になります。


最終更新日:2018/06/10 作成日:2017/12/28