Navigation Log - なびろぐ -
2001/01/26

...X680x0 spirit inside...

この日記はGNSで生成しています。
この日記の画像はOPTPiX webDesigner Ver.4で生成しています。

hauN
Go amazon.co.jp

■ご注文リストから■ [一覧]
■Amazonギフト券■ [購入]
信長の忍び 12巻 TVアニメDVDつき初回限定版 (ヤングアニマルコミックス) [本]
信長の忍び 12巻 ...
Amazonほしい物リスト


2001/01/26 (金)

static

staticの話。この場合、「static char *monthname[]」のstaticが目的としているものは、「char const *monthname[]」と同義です。やり方が違うけど、塩兄ちゃんとこも参照。

_ ROM化まで考えるなら、constを使うほうが正しいです。それも「char const * const monthname[]」と書くべきです。こう書くことで、「"Jan"・・・」とそれを指すポインタの配列の両方がROMに置かれ、アクセスはそのROMアドレスをアクセスするだけになる、というコードが出ます。

_ ですが、手持ちのコンパイラ(bcc32とcl)で試したところconstでは目的のコードは出なかったんです。どっちもROM化は考慮されていないから当然かも知れんけど(笑)。・・・と思ったら、monthstr[]の定義を関数内スコープじゃなくてグローバルスコープに置かないといけないんですな。

_ 種明かしを。staticがない場合、関数は「monthname[]に各ポインタ値を再ロードする」という初期化をまず実行します。定義付きローカル変数は関数の開始時に初期化されますから、配列の中身も毎回初期化されるということです。

_ 具体的には、monthname[]は、

  1. "Jan"・"Feb"・・・は書き換え可能データセグメント(a)に置かれる。
  2. (a)への各ポインタを収めた配列初期化データは書き換え可能データセグメント(b)に置かれる。
  3. 関数呼び出し時に、char *monthname[13]のための領域(c)がスタックから確保される。
  4. (b)を(c)にコピーし、初期化する。
  5. 以後、領域(c)をアクセスする。

_ というアクセスのされ方をします。staticが付くと、

  1. "Jan"・"Feb"・・・は書き換え可能データセグメント(a)に置かれる。
  2. (a)への各ポインタを収めた配列データは書き換え可能データセグメント(b)に置かれる。
  3. 以後、領域(b)をアクセスする。

_ となります。実行時間・メモリとも当然消費量が減ります。

_ グローバル変数として「char const * const monthname[]」と書いた場合は、

  1. "Jan"・"Feb"・・・は書き換え不可データセグメント(a)に置かれる。
  2. (a)への各ポインタを収めた配列データは書き換え不可データセグメント(b)に置かれる。
  3. 以後、領域(b)をアクセスする。

_ となります。誤って「char const *monthname[]」と書いた場合は、

  1. "Jan"・"Feb"・・・は書き換え不可データセグメント(a)に置かれる。
  2. (a)への各ポインタを収めた配列初期化データは書き換え不可データセグメント(b)に置かれる。
  3. リンク時に、char *monthname[13]のための領域(c)が書き換え可能データセグメントに確保される。
  4. ランタイム起動時に、(b)のデータを(c)にコピーし、初期化する。
  5. 以後、領域(c)をアクセスする。

_ となってしまいます。「書き換え可能データセグメント」をRAM、「書き換え不可データセグメント」をROMと読み替えると、よくわかるかと。

_ ポインタでなくstrcpyして返したのは、「引数で渡されたchar*型にstrcpyした」と書かれていたのを仕様として従ったまでです。本チャンのコードなら、必要に応じて添字範囲チェックやバッファオーバーフローチェックをする(strncpyを使う)のは当然のことです。ただ、「実処理コードよりチェックコードの方が大きくなって本来の内容がぼやけてしまう」のはよくあることで、今回はまさにそれで、内容からするとチェックコードを入れる必要はないだろうという判断で省きました*1

*1: でも、「配列を12個確保して添字を[n-1]にする」というセコい方法を今回とらなかったのは、ポカミス回避のひとつの方法。この手の関数の月を示す引数に誤って0を渡してしまうのはよくあるミスで、このとき[n-1]を叩いてしまうと配列外を叩くコードになってしまう。しかもこの手のバグはコードが大きくなってくると発見が困難になりがち。それなら添字1つ分余計に確保して確実に空文字列を返すほうがまだ安全。
添字チェックをちゃんとすればこんなことは考えなくてもいいんだけど、それと同時に「同程度の打鍵数でよりバグを出しにくい、あるいは出ても発見しやすいようにコードを書く」という手抜き技も重要。
実際のところ、チェックコードを的確に入れるのはかなり面倒で、わし自身は「わし以外が作用する可能性がある」部分、つまり入出力UIやpublicな関数の引数にしかチェックコードを入れないことがほとんど。もちろんライブラリやデバイスドライバのような(一般アプリでない)コードには大量のチェックコードが入ることになる。

_ しかし、最近はデータ領域どころかスタックさえ知らずにCで開発する人も少なくないからなぁ・・・^^;。\


[URL] [View Log()] [Trackback]
Name: Comment:



メールはこちらへ...[後藤浩昭 / Hiroaki GOTO / GORRY / gorry@hauN.org]

「表紙へ戻る」
「会議室」 「最新版」 「なびろぐindex」 「GNSソースを読む」