プロが教える店舗&オフィスのセキュリティ対策術

1. #include <stdio.h>
2. int main(void){
3. static char *wday[3]={
4. "sunday"
5. ,"monday"
6. ,"tuesday"
7. };
8. static char **p;
9. p=wday;
10. while(*p){
11. printf("%s\n",*p);
12. ++p;
13. }
14. return 0;
15. }

上記で、3行目と8行目のstaticを外すとsunday monday tuesdayの後にゴミを出力するのは何故なのか。原因と対策を知りたい。

A 回答 (3件)

これは「staticを外すとsunday monday tuesdayの後にゴミを出力する」のではなく、「staticを付けたらたまたまうまく動いた」のです。



10行目の判定は *p が指している先が NULL の時に偽になります。*p が指している先は 9 行目で wday の先頭に設定され、12 行目でひとつづつ次の要素に移動します。
しかし、"tuesday" を指しているときに次の要素へ移動しようとしたとき、C言語では何も文句を言わずに wday の領域を超えた箇所を指してしまいます。
static を指定した時はたまたまその箇所が NULL だったのでしょう。

一番簡単な対策は、wday の宣言を以下のように変えることです。
static char *wday[4]={
"sunday"
,"monday"
,"tuesday"
, NULL
};
上記なら static を外しても意図した通りの動作になります。
    • good
    • 0
この回答へのお礼

有難う御座います。まさか記憶クラスの違いでエラーが出たり出なかったりする物ですから視点が泳いでいました。そこ迄は気が付きませんでした。

この辺についてはコンパイラー次第でかなりの差が有るみたいです。これから本格的に使うつもりです。この辺についてはコンパイラーのオプション次第ではかなりのエラーの精度が変わる様な指定と言うのが有るのかも知れません。

これに続いて、今後はこれを参考にしてコンパイルする時のコンパイラーのオプションにも気をつけたいと思います。

今迄はコンパイラーは通常gccを使っていましたが。余りにもコマンドラインのオプションが多いために敬遠をしていて一番簡単なパターンで起動していました。この様な事が有るので今後はきちんとオプションも見直したいと思います。

果たして本当に見直した所で正しい正解が有るのかは分かりませんが。一つの今後の私の新しい勉強のきっかけには成りました。有難う御座いました。

お礼日時:2015/08/18 05:39

C言語をやる以上は、配列とポインタは必須なので、しっかり理解するようにしましょう。




char *wday[3]
で、char * 3つ分の領域が確保され、
wday[0] = "sunday" の先頭アドレス
wday[1] = "monday" の先頭アドレス
wday[2] = "tuesday" の先頭アドレス
となります。

これを
> while(*p){
で、p=&( wday[0] ),&( wday[1]), &( wday[2]) とループを続けます。
このとき、
*p= wday[0] , wday[1], wday[2]
となって、文字列のある領域へのポインタになっている、すなわち 0 ではないので、ループが続きます。

そして、
p= wday[3] となります。
さて、 char *wday[3] と宣言したときに、 wday[3] はどうなっているでしょうか?


static の場合は、たまたま wday[3]に相当する領域が 0 になっていたのでwhile ループが終了した。


staticが無い場合、たまたま wday[3]に相当する領域が 0 ではないのでループが継続し、 wday[3]の領域に書かれている何かを「アドレス」と判断して、その「アドレス」にある「文字列」を出力します。
続いて、 wday[4],wday[5]...と、 0 になるまでループを繰り返します。
※ または、途中でOSの保護領域へアクセスする等して、エラーになって止まったり、ハードディスク破壊とかも有り得ます。


対策は、「不定な領域へアクセスしないこと」です。
○ *p でループ判定するのではなく、ループ回数を指定する。
for( i=0 ; i<3; ++i ){〜 等

○最後に「終り」の印を入れる
char *wday[]={ // []の中を省略すると、指定された要素数に合せて自動で設定される
"sunday"
,"monday"
,"tuesday"
, NULL // wday[3]にNULLを指定する
};
    • good
    • 0
この回答へのお礼

有難う御座いました。普通の例えばchar *p="sunday"とした場合は何もnullを意識して定義はしませんよね。だから、この場合もその例に従う物と思っていました。

だから、動かない場合と言うのはこの辺のチェックも欠かせないと言う点で参考になりました。貴重な意見有難う御座いました。

お礼日時:2015/08/18 05:29

staticが付いている場合、正しい動作をしているわけではありません。


たまたま、あなたの環境で、エラーが発生していないというだけです。
私の環境では、ststicを付けても正しく動作しませんでした。
(私の環境はwindows7 mingw32(4.7.2))
原因は、while(*p)で、"tuesday"の次のポインターがNULLになっていないため、
処理を終了せずに、ゴミデータを処理することに有ります。
3.char *wday[3]={

char *wday[4]={
として、
6.,"tuesday"の次の行に、
,NULL
を追加してください。
そうすれば、staticをはずしても正しい動作をします。(もちろん、staticを付けてもOKです)
    • good
    • 0
この回答へのお礼

コンパイラーはgccです。この手のサンプルでは私が書いた様な記述が有るのも事実です。また、意図してwhile文の中でnullをチェックしているのもあります。説明では、統一性と言うのは有りません。

私は最初に、sunday monday tuesdayとデータを定義した時に最後のtuesdayを定義したら次は自動的にnullを書き込むと思っていましたが。そうでは無いのですね。偶々gccでは記憶クラスがstaticの時はnullを書き込むがそうで無い記憶クラスは利用者任せと言う事でしょうか。

この辺がコンパイラー次第と言うのは互換性の件でバグが混入する危険があるのでまずいですね。然し、こう言うのは殆ど初めて触る人にとっての説明はどの本にも書いて無い様な気がします。

参考になりました。有難う御座いました。

お礼日時:2015/08/18 05:25

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!