
皆様、いつもありがとうございます。
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);
}
納得のいく、説明を頂ければ幸いです。宜しくお願いいたします。
No.2ベストアンサー
- 回答日時:
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];
として宣言されているので、正しく動きます。
回答頂きましてありがとうございました。
詳細な説明、大変わかりやすく感謝いたしております。
strcat関数の意味を間違えていることが、そもそもの
原因でした。またstaticに置く場合のランタイムチェックルーチンの
有無も知るいい機械になりました!ありがとうございましたm(__)m
納得いたしました(回答もありがとうございました!)。
No.5
- 回答日時:
> どこかそのようになっておりますか?
ここですね。
local->tm_year +=1900;
local->tm_mon +=1;
このlocalポインタの参照先も、新規のものを返してくれるわけではなく、
内部のものの使いまわしが渡されますので、その結果を直接+=などすると破壊します。
変更したい場合やとっておきたい場合などは、自分で一度別の変数にコピーする必要があります。
# 一般に、CのAPI等で新規のポインタを返してくれるものはごくわずかです。
# その場合、呼び出し側でfreeの必要に迫られます。
回答ありがとうございました!
わかりました!云われてみて、確かにそうでした。。。
今後は、このあたりを注意しながら、今後のコーディングの
参考にさせて頂きます。大変勉強になりました。
今後とも宜しくお願いいたします。
No.4
- 回答日時:
再び#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足す前の値に戻っていることが確認できると思います。
No.3
- 回答日時:
よく読んでいませんが、その大仰な文字列処理の大半はstrftime()を使うとtime()関数とあわせて簡潔に書けてしまう気がします。
それから、localtime()が戻すstruct tm *は関数の内部領域ですので、そこを書き換えてしまうのは大変危険です。
また、同じ理由でlocaltime()はマルチスレッド環境では使えない関数です。
もう一つついでに、memset()の第二引き数はint型のパラメータを要求しますからポインタを表すNULLを使うのは意味的には間違いです。
回答頂きましてありがとうございました。
>localtime()が戻すstruct tm *は関数の内部領域ですので、そこを書き換えてしまうのは大変危険です。
ひとつ教えてください。私自身、このコーディング上で書き換えているという意識は無いのですが、どこかそのようになっておりますか?それとも一般的な話としてでしょうか?
>localtime()はマルチスレッド環境では使えない関数です。
知りませんでした、ありがとうございます。
>memset()の第二引き数はint型のパラメータを要求しますからポインタを表すNULLを使うのは意味的には間違いです。
云われてみてそうですね。今までずっとこのように記述していました。
ありがとうございました。またよろしくお願いします!
No.1
- 回答日時:
strcpy((char*)udate, strcat((char*)wk_date, (char*)wk_time));
このstrcat()の第一引数でwk_dateを渡しているので
wk_dateの文字列の後ろにwk_timeが連結されます。
wk_dateの領域が9バイトしかとっていないので連結を実行した
時点で確保した領域外を書きつぶしています。
それでエラーが出ているのでしょう。
#staticにするとエラーが出ないのは"たまたま"です。
回答頂きましてありがとうございました。
wk_dateの文字列の後ろに、wk_timeが連結されるのですね!
てっきりwk_dateとwk_timeを連結した結果のポインターを
返すものと思い込んでおりました。
納得いたしました(^^
お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!
似たような質問が見つかりました
- CGI perlで書いたcgiでsqliteの使い方を教えてください 2 2023/05/08 21:29
- C言語・C++・C# スタックフレームの消滅 6 2023/05/20 12:33
- C言語・C++・C# バイナリファイルをコピーするのにかかる時間を測りたいのですが実行するとFatel error:gli 2 2022/11/03 01:10
- C言語・C++・C# const char** p;のとき、free(p)でC4090エラーとなるのはなぜですか 3 2023/03/31 16:28
- UNIX・Linux cronの@rebootでのdateコマンドの実行につきまして 2 2023/06/11 16:23
- C言語・C++・C# c言語の問題の説明、各所ごとに 5 2023/07/26 11:03
- Oracle SQLの書き方について 1 2023/04/13 09:54
- ジャズ 渡辺貞夫さんで1番好きな曲を教えて下さい。 https://youtu.be/wk-H0A9mVS8 3 2022/07/16 02:19
- PHP 共通の処理をまとめる方法がわからないのでアドバイスお願いします。 1 2022/12/19 20:20
- C言語・C++・C# カードシャッフルのブログラムを使ってc言語でブラックジャックをしたい 2 2022/04/12 15:13
関連するカテゴリからQ&Aを探す
おすすめ情報
デイリーランキングこのカテゴリの人気デイリーQ&Aランキング
-
CStringからchar*への型変換に...
-
char*を初期化したいのですが
-
C言語のintとcharの違いってな...
-
char型にint型の数値を代入する。
-
C言語にて構造体のメンバがNULL...
-
fgetc( )の戻り値はなぜ整数??
-
char 文字列型 の表現範囲が-12...
-
fgetsで読み込んだ値のvector処理
-
2次元配列の文字"列"の初期化方法
-
C++17で、unsigned char * 配列...
-
Cの関数の引数のconst *charに...
-
wsprintf( ポインタ , "%d" , "...
-
バイト型データの参照
-
警告
-
構造体のメンバーの静的なサイ...
-
char AA[]{"全角文字"};から"全...
-
構造体・ビットフィールドのvol...
-
new charとnew char[N]の違いは?
-
SubStringの使い方について
-
ポインタとcharについて
マンスリーランキングこのカテゴリの人気マンスリーQ&Aランキング
-
CStringからchar*への型変換に...
-
char*を初期化したいのですが
-
C言語にて構造体のメンバがNULL...
-
C言語のintとcharの違いってな...
-
DWORDとcharの変換
-
C++17で、unsigned char * 配列...
-
char 文字列型 の表現範囲が-12...
-
new charとnew char[N]の違いは?
-
char型にint型の数値を代入する。
-
動的メモリの初期化方法について。
-
小数点入りの文字列をfloat型に...
-
文字型配列に格納した空白の切捨て
-
fstream型オブジェクトを関数の...
-
C++Builder 2009 テキスト...
-
文字列の途中から途中までを抽出
-
C言語の文字リテラル中の16進文...
-
エクセルのMID関数は、C言語では?
-
文字列のswap
-
void型へのポインタ
-
VC++ char[10]へのCString値の代入
おすすめ情報