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

皆様、いつもありがとうございます。
C言語で教えてください。
プログラムを実行すると、下記内容のエラーが発生します。
Run-Time Check Failure #2 - Stack arround the variable 'wk_date' was corrupted

発生場所までは、突き止めましたが、原因が釈然としません。
wk_dateとwk_timeをstaticをつけて定義すると、このエラーが
出ないことから、strcpyの関数でのポインターのトラブル?だと
思うのですが、下記のロジックでおかしい箇所はあるでしょうか?
ロジックは、現在の日付と時刻をudateに入れて返すだけです。

static char udate[15];


init()
{
char wk_date[9];
char wk_time[7];

time_t current;
struct tm *local;

time(¤t); /* 現在の時刻を取得 */
local = localtime(¤t); /* 地方時の構造体に変換 */
local->tm_year +=1900;
local->tm_mon +=1;
memset((char*)wk_date, NULL, sizeof(wk_date));
memset((char*)wk_time, NULL, sizeof(wk_time));
sprintf((char*)wk_date, "%04d%02d%02d", local->tm_year, local->tm_mon, local->tm_mday);
printf("%s\n", wk_date);
sprintf((char*)wk_time, "%02d%02d%02d", local->tm_hour, local->tm_min, local->tm_sec);
printf("%s\n", wk_time);

memset((char*)udate, NULL, sizeof(udate));
strcpy((char*)udate, strcat((char*)wk_date, (char*)wk_time));
return(0);
}
納得のいく、説明を頂ければ幸いです。宜しくお願いいたします。

A 回答 (5件)

strcatを行う際に、文字列を結合した結果が収まるだけの充分な領域がありません。

領域が足らずにメモリを破壊しています。

strcpy((char*)udate, strcat((char*)wk_date, (char*)wk_time));
の行でstrcatを行う場合、結合先、つまりwk_dateに連結後の文字列が収まるだけの充分な領域が必要です。

strcat((char*)wk_date, (char*)wk_time)
を行うには、wk_dateには、年4桁、月2桁、日2桁、時2桁、分2桁、秒2桁、ターミネータの'\0'が入るだけの、つまり、15バイトの領域が必要です。

ところが、
char wk_date[9];
と、9バイトしか確保されていません。これではwk_dateの後ろ10バイト目以降のメモリが破壊されます。

宣言部で
char wk_date[15];
のようにwk_dateに充分な領域を与えて下さい。

なお、wk_dateをstaticに置くと「スタック上のワークや関数の戻り先を破壊して致命的暴走を引き起こすようなプログラムが実行されないようにする、ランタイムチェックルーチン」が呼ばれない為、単純にスタティック用データメモリの一部を破壊して終るだけなので、ランタイムエラーは起きません。未使用のメモリか、他の何かが使っているメモリを壊すだけなので、多分、バグに気付かないでしょう。

宣言部を
char wk_date[9];
のままにしたい場合は、

strcpy((char*)udate, strcat((char*)wk_date, (char*)wk_time));

strcpy((char*)udate,(char*)wk_date);
strcat((char*)udate,(char*)wk_time);
に直しましょう。

udateはstrcatが可能なだけの領域が
static char udate[15];
として宣言されているので、正しく動きます。
    • good
    • 0
この回答へのお礼

回答頂きましてありがとうございました。
詳細な説明、大変わかりやすく感謝いたしております。
strcat関数の意味を間違えていることが、そもそもの
原因でした。またstaticに置く場合のランタイムチェックルーチンの
有無も知るいい機械になりました!ありがとうございましたm(__)m
納得いたしました(回答もありがとうございました!)。

お礼日時:2006/11/24 12:58

> どこかそのようになっておりますか?



ここですね。
local->tm_year +=1900;
local->tm_mon +=1;
このlocalポインタの参照先も、新規のものを返してくれるわけではなく、
内部のものの使いまわしが渡されますので、その結果を直接+=などすると破壊します。
変更したい場合やとっておきたい場合などは、自分で一度別の変数にコピーする必要があります。

# 一般に、CのAPI等で新規のポインタを返してくれるものはごくわずかです。
# その場合、呼び出し側でfreeの必要に迫られます。
    • good
    • 0
この回答へのお礼

回答ありがとうございました!
わかりました!云われてみて、確かにそうでした。。。
今後は、このあたりを注意しながら、今後のコーディングの
参考にさせて頂きます。大変勉強になりました。
今後とも宜しくお願いいたします。

お礼日時:2006/11/24 13:50

再び#3です。


localtime()の戻す値は内部領域を指すポインタです。
struct tm * localは、ポインタ変数です。
ポイント変数にポインタを代入しているだけですから、
localはlocaltime()の内部領域を指してしまいます。
つまり、local->tm_year += 1900は内部領域を書き換えています。
struct tm * local, local2;
local = localtime(& current);
local->tm_year += 1900;
local2 = localtime(& current);
printf("local:%p, local2:%p, local->tm_year:%d\n", local, local2, local->tm_year);
とすると、localとlocal2が同じ値になっていて、local->tm_yearが1900足す前の値に戻っていることが確認できると思います。
    • good
    • 0

よく読んでいませんが、その大仰な文字列処理の大半はstrftime()を使うとtime()関数とあわせて簡潔に書けてしまう気がします。


それから、localtime()が戻すstruct tm *は関数の内部領域ですので、そこを書き換えてしまうのは大変危険です。
また、同じ理由でlocaltime()はマルチスレッド環境では使えない関数です。
もう一つついでに、memset()の第二引き数はint型のパラメータを要求しますからポインタを表すNULLを使うのは意味的には間違いです。
    • good
    • 0
この回答へのお礼

回答頂きましてありがとうございました。

>localtime()が戻すstruct tm *は関数の内部領域ですので、そこを書き換えてしまうのは大変危険です。
ひとつ教えてください。私自身、このコーディング上で書き換えているという意識は無いのですが、どこかそのようになっておりますか?それとも一般的な話としてでしょうか?

>localtime()はマルチスレッド環境では使えない関数です。
知りませんでした、ありがとうございます。

>memset()の第二引き数はint型のパラメータを要求しますからポインタを表すNULLを使うのは意味的には間違いです。
云われてみてそうですね。今までずっとこのように記述していました。

ありがとうございました。またよろしくお願いします!

お礼日時:2006/11/24 13:08

strcpy((char*)udate, strcat((char*)wk_date, (char*)wk_time));


このstrcat()の第一引数でwk_dateを渡しているので
wk_dateの文字列の後ろにwk_timeが連結されます。
wk_dateの領域が9バイトしかとっていないので連結を実行した
時点で確保した領域外を書きつぶしています。
それでエラーが出ているのでしょう。

#staticにするとエラーが出ないのは"たまたま"です。
    • good
    • 0
この回答へのお礼

回答頂きましてありがとうございました。
wk_dateの文字列の後ろに、wk_timeが連結されるのですね!
てっきりwk_dateとwk_timeを連結した結果のポインターを
返すものと思い込んでおりました。
納得いたしました(^^

お礼日時:2006/11/24 12:53

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