- ベストアンサー
static付き宣言の初期化
static付きの宣言をした場合の変数の初期化について教えてください。(ANSI-C) int func(void) { static int si; static char sca[10]; static char *scp; /* 何らかの処理 */ return 0; } このように関数内でstatic付きで宣言したとき、変数はどのように初期化されますか? siは0、sca[0]からsca[9]までは'\0'、scpはNULLで初期化されますか? また、このようなstatic付きの宣言が関数の外にあった場合は、どのように初期化されますか?
- みんなの回答 (9)
- 専門家の回答
質問者が選んだベストアンサー
- ベストアンサー
自信を持って取り消し。 質問に対しての ~~~~ >siは0、sca[0]からsca[9]までは'\0'、scpはNULLで >初期化されますか? A.はいそうです。 >static付きの宣言が関数の外にあった場合は、どのよう >に初期化されますか? A.同じです。 ~~~~ についての結論は変わりませんが、 根拠説明時の宣言領域内の全クリアについては、確かに 確実な回答ではないようです。 calloc は単にメモリのバイト単位クリアなので、値に対する 保障が無関係なのはその通りですが、静的領域のクリア にはコンパイラにより確実な処理が含まれているのかも 知れません。 事実上は ポインタ→NULL=0 数値変数→0 なのでどっちでも同じことですが、 ・値・・・0かつビット配列・・・0以外 の型定義が存在するとき ・NULLポインタのビット配列が0でない場合 現実的かどうかは別として、上記の条件のものがある場合の 動作保障については、よくわかりません。 参考までに構造体と列挙型の静的宣言確認を載せておきます ただしこれも、ビットクリアされているのか、メンバ単位に 数値変数=0・ポインタ=NULLとしてのコンパイラ処理されて いるのかの区別はつきません。 おかげでひとつ利口になった。 ---------------------------------------------- #pragma pack(1) // 境界整列用(処理系依存デフォルト4) struct TESTSTRUCT { double numd; int num; char chara[13]; void* ptr; }; enum TESTENUM { aaa = 2, bbb =356, ccc = 568 }; static struct TESTSTRUCT ts; static enum TESTENUM te; int main() { char* pdump; int i; int tssize, tesize; tssize = sizeof(struct TESTSTRUCT); tesize = sizeof(enum TESTENUM); // ダンプ printf("TESTSTRUCT size = %d\n", tssize); pdump = (char*)&ts; for(i = 0; i < tssize; i++) { printf("%02X ", pdump[i]); if((i % 8) == 7) printf(" "); if((i % 16) == 15) printf("\n"); } printf("\n"); printf("TESTENUM size = %d\n", tesize); pdump = (char*)&te; for(i = 0; i < tesize; i++) { printf("%02X ", pdump[i]); if((i % 8) == 7) printf(" "); if((i % 16) == 15) printf("\n"); } printf("\n"); // メンバ printf("TESTSTRUCT (double)ts.numd = %f\n", ts.numd); printf("TESTSTRUCT (int)ts.num = %d\n", ts.num); printf("TESTSTRUCT (char[])ts.chara[] = "); for(i = 0; i < sizeof(ts.chara); i++) printf("%02X ", ts.chara[i]); printf("\n"); printf("TESTSTRUCT (void*)ts.ptr = 0x%08X\n", ts.ptr); printf("TESTENUM te = %d\n", te); return 0; } ----------------------------------------------
その他の回答 (8)
- nagare
- ベストアンサー率33% (280/831)
>こんなプログラムを作りましたが、NULLのビットパターンがわかるわけでもないし 0って全ビット0ですよね??? ビットの確認したいのであれば、print文で16進表示で確認できますよ (4桁表示にする)
お礼
>0って全ビット0ですよね??? そうですね。。。 締め切り後ですが、本に書いてあったことをさらっと書いておきます。 「整数0の内部表現=ビット構成は、全ビットが0であることが保証される。」(『C言語プログラミングの落とし穴』という本) ただし、これは【整数】に関する記述ですが…
- nagare
- ベストアンサー率33% (280/831)
こう覚えたらよいと思います ・staticで宣言された領域は起動時に初期値が入る ・初期値とは0である ・NULLも'\0'も0である 注意 ・関数の中でstaticを使った場合、1回目は0だが、一度書き換えて、もう一度その関数を呼んだ場合、書き換えた値のままである
お礼
>・NULLも'\0'も0である そうそうNULLって、0なんだよなあ~~、って思い出しました。 こんなプログラムを作りましたが、NULLのビットパターンがわかるわけでもないし、質問とは直接関係ないし、大して意味はないし、どうでもいいようなプログラムですけど。 (繰り返しますが、質問とはそんなに関係ないです。) #include <stdio.h> int main(void) { #ifdef NULL puts("NULLマクロは定義されています。"); if(NULL==0) puts("NULLは0です。"); else puts("NULLは0ではありません。"); #else puts("NULLマクロは定義されていません。"); #endif return 0; } 実行結果 NULLマクロは定義されています。 NULLは0です。
補足
この質問は、つまるところ、static変数は全ビット0で初期化されるのか、それとも、ポインタはNULLで初期化され、数値は0で初期化されるのか、を聞いているわけです。 全ビット0ではないようですね。
- Lara-Port
- ベストアンサー率36% (12/33)
皆さん、ご指摘ありがとうございます。 最新の規格書にて再確認したところ、皆さんが仰る通り初期化されると書いてありました。 混乱を招きますので、管理者に私のコメントを削除をしてもらうよう、連絡しておきます。
補足
回答ありがとうございます。が、他の方の回答も ららぽーとさんのNo.1の回答を前提に書かれているものもありますし、、、 本当に混乱を避けるんであれば質問ごと削除する必要があるかと思いますが、それは困りますんで、 ご趣旨はわかりましたので、ご回答はもとのままにしておいてください。 私からのお願いです。
- toysmith
- ベストアンサー率37% (570/1525)
記憶クラスと初期化省略時初期値の関係は単純です。 auto:不定 その他:全てのビットが0である値 例外) extern:記憶領域を確保しない typedef:記憶領域の宣言ではない 例外であるextern記憶クラスとtypedef記憶クラスを除くと2パターンしかありません。
補足
static付きで宣言された場合、全ビット0で初期化されるが、その数値が0なのか(ポインタの場合NULLになるか)は決まっていないんですね。
- bug_master
- ベストアンサー率52% (10/19)
他の方々が言われるように ANSI C ならstatic付の宣言では0で初期化されることが保証されます。 ただ、初期化されるのはメモリを確保したときのみなので、例ですとfuncを呼び出すたびに初期化では無く、プログラムの起動時に1回だけ0に初期化と成ります。 2回目以降のfuncの呼び出しではsi等の値は、前回の/* 何らかの処理 */ で代入された値のままに成ります。 Lara-Portさんが言われるような不定値には成りません。 と言うか、初期化と代入を混同しているように見受けられますが・・・
補足
参考URL見ました。「C言語 FAQ 日本語訳 」というものですね。 そこには、staticつきの場合、ポインタはヌルポインタに、浮動小数は0.0に初期化されると書いてありました。 しかし、気になることも書いてあります。私なりに解釈すると、 「calloc()によって確保された領域は全ビット0になるが、それが浮動小数の0.0やヌルポインタを意味するとは限らない。」 ということみたいです。 処理系によっては、static変数は、全ビット0で初期化されるとは限らないのでしょうか。
dtmさんが既に答えられてますが、 静的宣言は指定が無ければ全て0クリアされます。 >static int si; >static char sca[10]; >static char *scp; si ・・・ 0 sca[0] ~ sca[9] ・・・オール0 scp ・・・ NULL 宣言領域内が全部クリアされるだけなので、関数内・外、配列・ポインタ 等の区別は全て無いです。
お礼
ありがとうございます。 ご回答どおり、static変数は全ビット0になるようです。 次のプログラムを動かしてみました。(意図したとおりに動いているかは自信ないけど。) #include <limits.h> #include <stdio.h> #include <string.h> void func(void) { static int si; static double sdouble; static char sca[10]; static char *scp; int not_static_i; int i; char null_char='\0'; char null_char_array[10]={ '\0' , '\0' , '\0' , '\0' , '\0' , '\0' , '\0' , '\0' , '\0' , '\0' }; puts("とりあえず前提として"); printf("1バイトのビット数は%dです。\n" , CHAR_BIT ) ; printf("siは%dバイトです。\n" , (int)sizeof(si) ); printf("sdoubleは%dバイトです。\n" , (int)sizeof(sdouble) ); printf("scaは%dバイトです。\n" , (int)sizeof(sca) ); printf("scpは%dバイトです。\n\n" , (int)sizeof(scp) ); puts("'\\0' のビットパターン "); for( i=CHAR_BIT-1 ; i>=0 ; i-- ) { printf("%d" , (null_char >> i) & 0x01); } printf("\n\n"); if( memcmp(¬_static_i , null_char_array , sizeof(not_static_i) ) ) { puts("not_static_iは0でないビットが含まれています。"); printf("ちなみにnot_static_iは%d\n", not_static_i ); } else puts("not_static_iは全ビット0です。"); printf("\n★★★それではstatic変数は…★★★\n"); if( memcmp(&si , null_char_array , sizeof(si) ) ) puts("siは0でないビットが含まれています。"); else puts("siは全ビット0です。"); if( memcmp(&sdouble , null_char_array , sizeof(sdouble) ) ) puts("sdoubleは0でないビットが含まれています。"); else puts("sdoubleは全ビット0です。"); if( memcmp(sca , null_char_array , sizeof(sca) ) ) puts("scaは0でないビットが含まれています。"); else puts("scaは全ビット0です。"); if( memcmp(&scp , null_char_array , sizeof(scp) ) ) puts("scpは0でないビットが含まれています。"); else puts("scpは全ビット0です。"); } int main(void) { func(); return 0; } 結果 とりあえず前提として 1バイトのビット数は8です。 siは4バイトです。 sdoubleは8バイトです。 scaは10バイトです。 scpは4バイトです。 '\0' のビットパターン 00000000 not_static_iは0でないビットが含まれています。 ちなみにnot_static_iは512 ★★★それではstatic変数は…★★★ siは全ビット0です。 sdoubleは全ビット0です。 scaは全ビット0です。 scpは全ビット0です。
- dtm
- ベストアンサー率37% (23/62)
最新の ANSI C を知らないので流そうかと思ったのですが、気になったので書かせていただきます。 私の知る限りでは、static 変数はゼロ(あるいはヌル)に初期化されたはずです。関数の外で宣言された場合も同じくゼロに初期化されます。どちらもプログラムが実行されるとすぐに割り当てられ初期化され、終了するまで維持されます。 これが ANSI C で定義されていないのであれば、処理系独自の拡張という事でしょうか? 初期化されることを前提として書かれたソースも多くあり、これが ANSI から外されるというのは少し疑問です。
補足
>これが ANSI C で定義されていないのであれば、処理系独自の拡張という事でしょうか? 自分でもやってみました。double型も付け加えました。 #include <stdio.h> int func(void) { static int si; static double sdouble; static char sca[10]; static char *scp; int i; printf("siは%dです。\n",si); putchar('\n'); printf("sdoubleは%fです。\n",sdouble); putchar('\n'); for(i=0 ; i<10 ; i++) { if(sca[i]=='\0') printf("sca[%d]は'\\0'です。\n", i ) ; else printf("sca[%d]は'\\0'ではありません。\n",i) ; } putchar('\n'); (scp==NULL) ? puts("scpはNULLです。") : puts("scpはNULLではありません。") ; return 0; } int main(void) { func(); return 0; } 結果 siは0です。 sdoubleは0.000000です。 sca[0]は'\0'です。 sca[1]は'\0'です。 sca[2]は'\0'です。 sca[3]は'\0'です。 sca[4]は'\0'です。 sca[5]は'\0'です。 sca[6]は'\0'です。 sca[7]は'\0'です。 sca[8]は'\0'です。 sca[9]は'\0'です。 scpはNULLです。 きちんと、int型は0、double型は0.0、sca[0]からsca[9]までは'\0'、scpはNULLに初期化されているようですね。 >初期化されることを前提として書かれたソースも多くあり、これが ANSI から外されるというのは少し疑問です。 そうですね。 もちろん、質問は、そういう処理系に依存する話ではなくて、規則としてそのように初期化されることが定まっているか、です。
- Lara-Port
- ベストアンサー率36% (12/33)
C言語では、ANSIの仕様・宣言する位置に限らず、変数は初期値を入れない限り、不定値となります。(偶然、割り当てられた領域が0である場合は初期化されているように見えますが…) ですので、初期値が必要な場合は、main()や各関数の前半で、初期化する習慣をつけましょう。 今回の関数の場合、以下のようにされればいいのではないでしょうか。(static変数の使い方を理解してると仮定して書きます) <関数の呼び出しが1回目のみ初期値をもつとき> int func(void) { static int si = 0; static char *scp = NULL; static char sca[10] ={ NULL,NULL,NULL,NULL,NULL, NULL,NULL,NULL,NULL,NULL }; /* 何らかの処理 */ return 0; } <関数を呼び出すたび初期値をもつとき> int func(void) { static int si; static char *scp; static char sca[10]; /* 初期化 */ si = 0; scp = (char*)NULL; memset( sca, 0, sizeof(sca)); /* 何らかの処理 */ return 0; }
お礼
ありがとうございます。 誤っているということなので、特にコメントはございません。
お礼
ということは、質問に書いたとおりに初期化されるんですね。 また、(質問には書いてないですが)No.5の参考URLから、浮動小数点数は0.0に初期化されることもわかりました。 みなさん、ありがとうございました。