バグ検出ドリル(15)なぜ「山田三郎」が「まだまだ天田三郎」に化けるのか:山浦恒央の“くみこみ”な話(115)(3/3 ページ)
バグは至るところに、しかも堂々と潜んでおり、自信満々なプログラマーほど、目の前のバグに気付かないものです。「バグ検出ドリル」の第15回の問題は、「思った通りに動いてくれないプログラム」のバグです。少しはプログラマーに忖度してくれよ!
4.今回の解答
「山田三郎」が、「まだまだ天田三郎」になっていますね。この原因は、「strcatにより、隣接する文字列を書き換えてしまっている」です。
問題を簡単に振り返ります。名前を表す変数name1〜3に、名字を表す変数familynameをそれぞれ連結し、表示します。例えば、name1にfamilynameを連結すると、IchiroYamadaになると期待しています。連結には、C言語の標準関数、strcat関数を使います。そんなに難しいプログラミングではないのに、うまく表示できません。
正しい動作をしない原因は、隣接している文字列を書き換えているためです(いわゆる、「爆撃」ですね)。まず、以下のソースコードをご覧ください。
// 名字 char familyname[] = "Yamada"; // 名前 char name1[] = "Ichiro"; char name2[] = "Ziro"; char name3[] = "Saburo";
上記の配列宣言部では、名字を表すfamilynameに「Yamada」を代入し、名前を表す配列name1〜3にそれぞれの名前を代入しています。図1に各配列の値を示します。1段目が変数名、2段目は簡易的なアドレス※3)、3段目で変数値を表示しました。
※3)筆者の環境(Cygwin GCC 7.3.0)での、各変数のアドレスの末尾2桁を記載しました。別の環境で実行すると同様の結果とならない場合があるので、ご注意ください。
図1の変数の状態で、1回目のstrcat(name1,familyname)を実行すると、図2となります。name1の18番地からYamadaが書き込まれているのが分かりますね。問題は、name1とfamilynameが、隣接していることです。strcat関数で、名字を追加するのは良いのですが、肝心のfamilynameの値を上書きし、19番地以降が、「amada」となってしまいました。
図2に対し、2回目のstrcat(name2,familyname)を実行すると、図3となります。name2の末尾の11番地にfamilynameを書き込んだ結果、隣接するname1に「amada」を上書きしてしまいました。
図3に対し、3回目のstrcat(name3,familyname)を実行すると、図4になります。この状態で、各変数を表示すると、各変数の先頭番地(6、d、12)から表示するので、「Saburoamada」「mada」「mada」と表示してしまいました。
正しい結果を出すためには、name変数の配列のサイズを大きく取っておくといいでしょう(例えば、char name1[100])。ただし、想定のサイズを超えると、同様の結果となるので注意が必要です。
5.自己採点シート
今回の自己採点シートを以下に示します。
問題 | 内容 | 配点(点) |
---|---|---|
文字列連結プログラム | 問題を一通り読んだ | 50 |
隣接する変数の値を書き換えているバグを見つけた | 50 | |
リスト4 自己採点シート |
6.終わりに
プログラムを思い通りに作ることは簡単ではありません。今回は、文字列連結プログラムの問題を出題しました。皆さんはバグを見つけられたでしょうか。
C/C++言語で文字列を扱う場合、strcatやstrtokなどの標準関数を使うことがあります。標準関数の中には、使い方を誤ると思わぬ誤動作につながるものがあり、注意が必要です。
実際のソフトウェアの開発では、文字列の処理はよく出てきますね。筆者は、機器の情報を文字列で取得し、整形して使うソフトウェアを作成したことがあります。文字列を処理するプログラムでは、文字列の操作関数を注意深く使わないと、不可解なバグでたっぷり悩むことになります。お互い、気を付けましょう。
参考文献
「Cプログラミング専門課程」(藤原博文、1994年、技術評論社)
東海大学 大学院 組込み技術研究科 非常勤講師(工学博士)
Copyright © ITmedia, Inc. All Rights Reserved.
関連記事
- ≫連載「山浦恒央の“くみこみ”な話」バックナンバー
- バグ検出ドリル(14)タイポグリセミア現象もびっくり、恐るべきは思い込み
バグは至るところに、しかも堂々と潜んでおり、自信満々なプログラマーほど、目の前のバグに気付かないものです。「バグ検出ドリル」の第14回の問題は、前回に続いて「煮詰まったバグ」です。煮詰まっているがゆえに、バグの原因が分からなくなる事態に対処してください! - バグ検出ドリル(13)のたうち回る地獄、煮詰まったバグを解決せよ
バグは至るところに、しかも堂々と潜んでおり、自信満々なプログラマーほど、目の前のバグに気付かないものです。「バグ検出ドリル」の第13回の問題は、筆者の経験を基にして作った「煮詰まったバグ」です。プログラムが思い通りに動かず「のたうち回る地獄」のような状況に対処してください! - バグ検出ドリル(12)コンパイルエラーにならないコンパイルエラー
バグは至るところに、しかも堂々と潜んでおり、自信満々なプログラマーほど、目の前のバグに気付かないものです。「バグ検出ドリル」の第12回は、前回に引き続き「コンパイルエラー」がテーマです。コンパイルエラーにはならないのに、うまく動かないプログラムのバグを見つけ出してください! - バグ検出ドリル(11)コンパイルエラーは意外に難しい
バグは至るところに、しかも堂々と潜んでおり、自信満々なプログラマーほど、目の前のバグに気付かないものです。「バグ検出ドリル」の第11回では、初心者が苦労する「コンパイルエラー」がテーマです。コンパイラが指し示すエラーの箇所から、バグを見つけ出してください! - バグ検出ドリル(10)“昔のバグ”は生命力が最強
バグは至るところに、しかも堂々と潜んでおり、自信満々なプログラマーほど、目の前のバグに気付かないものです。「バグ検出ドリル」の第10回では、生命力最強という“昔のバグ”がテーマです。前回の第9回で扱った迷路探索プログラムの改造版からバグを見つけ出してください!