logo サブスレッド

C言語のポインタでハマった昔話

はじめに

どうも、名古屋の生き字引のおっさんです。どうぞよろしく。

先週、AndroidのトレンドはKotlinだとかなんとか言ってたのですが、あれはただの個人の感想です。
と最後に書くのを忘れてましたので、ここに書いておきます。

さて、今日はおっさんらしく、昔開発していたC言語のコードのことで思い出したことがあるので書いていきます。

イカンかったコード

今は昔、こんな感じのコードを書いておりました。

char *g_title = "title1";

void SetTitle(const char *title)
{
    strcpy(g_title, title);
}

int main()
{
    SetTitle("title2");
    return 0;
}

SetTitle() 内で長さ溢れチェックしてないとか、グローバル変数使っとるとか、そういう話は置いておいて、これ開発時にはちゃんと動いてました。
g_title は¥0含めて7byteの領域があって、そこに¥0含めて7byte以内の文字列をコピーする分には問題なかったのです。

これを8byte以上の文字列をコピーしたらメモリ破壊となり問題になるのですが
今回はそういう話ではなくてこの7byte以内のコードを実機で動かしたら落ちてしまったのです。

当時、まだ若かった青年は何故落ちたか悩みました。そして解決してくれたのはうちの代表の天の声でした。

「g_titleってバッファじゃないからじゃない?」

そうか、だでいかんかったんだ。

どういうことかと言うと、g_title は “title1” を指すポインタです。
その “title1” ってメモリ上のどこに配置されるか分からんのです。
開発時にはたまたま書き込み可能な領域に配置されていたかもしれないけど、実機ではプログラムからの書き込みが不可な領域に配置されていたのです。
で、たとえ7byte以内でも書き込み不可の領域に対して文字列を書き込んでしまったがために落ちてしまったということです。

イケたコード

原因がわかったので、修正です。
用は、g_title が指すポインタを書き込み可能なメモリ空間にすれば良いのです。
Cで書き込み可能なメモリ空間を表現するには配列とか malloc でメモリ動的確保とかありますが、配列が楽なのでそれでいきましょう。

char g_title[] = "title1";

void SetTitle(const char *title)
{
    strcpy(g_title, title);
}

int main()
{
    SetTitle("title2");
    return 0;
}

これで直りました。g_title を配列にしただけです。
初期値として文字列を入れればその長さ分だけバッファを確保してくれます。
今回、g_title が指すメモリ空間は7byteとなります。もちろん7byteより大きい値を書き込んだらダメです。
7byteより大きい書き込みがされる場合、g_title の最大長を決めて

#define TITLE_MAX_LENGTH  (100)

char g_title[TITLE_MAX_LENGTH + 1] = "title1";

という風に書いても良いです。
これだと g_title の領域は101byteが保証され、そこに初期値として”title1”(7byte)入れている感じになります。

おわりに

週間、ウチのネコ。第6号

トレーニングベンチを陣取る(部屋汚くてスミマセン)

現在の位置:サブスレッド ホーム > 技術ブログ > C言語のポインタでハマった昔話