• ベストアンサー

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付きの宣言が関数の外にあった場合は、どのように初期化されますか?

noname#2045
noname#2045

質問者が選んだベストアンサー

  • ベストアンサー
noname#2009
noname#2009
回答No.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; } ----------------------------------------------

noname#2045
質問者

お礼

ということは、質問に書いたとおりに初期化されるんですね。 また、(質問には書いてないですが)No.5の参考URLから、浮動小数点数は0.0に初期化されることもわかりました。 みなさん、ありがとうございました。

その他の回答 (8)

  • nagare
  • ベストアンサー率33% (280/831)
回答No.8

>こんなプログラムを作りましたが、NULLのビットパターンがわかるわけでもないし 0って全ビット0ですよね??? ビットの確認したいのであれば、print文で16進表示で確認できますよ (4桁表示にする)

noname#2045
質問者

お礼

>0って全ビット0ですよね??? そうですね。。。 締め切り後ですが、本に書いてあったことをさらっと書いておきます。 「整数0の内部表現=ビット構成は、全ビットが0であることが保証される。」(『C言語プログラミングの落とし穴』という本) ただし、これは【整数】に関する記述ですが…

  • nagare
  • ベストアンサー率33% (280/831)
回答No.7

こう覚えたらよいと思います ・staticで宣言された領域は起動時に初期値が入る ・初期値とは0である ・NULLも'\0'も0である 注意 ・関数の中でstaticを使った場合、1回目は0だが、一度書き換えて、もう一度その関数を呼んだ場合、書き換えた値のままである

noname#2045
質問者

お礼

>・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です。

noname#2045
質問者

補足

この質問は、つまるところ、static変数は全ビット0で初期化されるのか、それとも、ポインタはNULLで初期化され、数値は0で初期化されるのか、を聞いているわけです。 全ビット0ではないようですね。

  • Lara-Port
  • ベストアンサー率36% (12/33)
回答No.6

皆さん、ご指摘ありがとうございます。 最新の規格書にて再確認したところ、皆さんが仰る通り初期化されると書いてありました。 混乱を招きますので、管理者に私のコメントを削除をしてもらうよう、連絡しておきます。

noname#2045
質問者

補足

回答ありがとうございます。が、他の方の回答も ららぽーとさんのNo.1の回答を前提に書かれているものもありますし、、、 本当に混乱を避けるんであれば質問ごと削除する必要があるかと思いますが、それは困りますんで、 ご趣旨はわかりましたので、ご回答はもとのままにしておいてください。 私からのお願いです。

  • toysmith
  • ベストアンサー率37% (570/1525)
回答No.5

記憶クラスと初期化省略時初期値の関係は単純です。 auto:不定 その他:全てのビットが0である値 例外) extern:記憶領域を確保しない typedef:記憶領域の宣言ではない 例外であるextern記憶クラスとtypedef記憶クラスを除くと2パターンしかありません。

noname#2045
質問者

補足

static付きで宣言された場合、全ビット0で初期化されるが、その数値が0なのか(ポインタの場合NULLになるか)は決まっていないんですね。

回答No.4

他の方々が言われるように ANSI C ならstatic付の宣言では0で初期化されることが保証されます。 ただ、初期化されるのはメモリを確保したときのみなので、例ですとfuncを呼び出すたびに初期化では無く、プログラムの起動時に1回だけ0に初期化と成ります。 2回目以降のfuncの呼び出しではsi等の値は、前回の/* 何らかの処理 */ で代入された値のままに成ります。 Lara-Portさんが言われるような不定値には成りません。 と言うか、初期化と代入を混同しているように見受けられますが・・・

参考URL:
http://www.catnet.ne.jp/kouno/c_faq/c1.html#30
noname#2045
質問者

補足

参考URL見ました。「C言語 FAQ 日本語訳 」というものですね。 そこには、staticつきの場合、ポインタはヌルポインタに、浮動小数は0.0に初期化されると書いてありました。 しかし、気になることも書いてあります。私なりに解釈すると、 「calloc()によって確保された領域は全ビット0になるが、それが浮動小数の0.0やヌルポインタを意味するとは限らない。」 ということみたいです。 処理系によっては、static変数は、全ビット0で初期化されるとは限らないのでしょうか。

noname#2009
noname#2009
回答No.3

dtmさんが既に答えられてますが、 静的宣言は指定が無ければ全て0クリアされます。 >static int si; >static char sca[10]; >static char *scp; si ・・・ 0 sca[0] ~ sca[9] ・・・オール0 scp ・・・ NULL 宣言領域内が全部クリアされるだけなので、関数内・外、配列・ポインタ 等の区別は全て無いです。

noname#2045
質問者

お礼

ありがとうございます。 ご回答どおり、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(&not;_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)
回答No.2

最新の ANSI C を知らないので流そうかと思ったのですが、気になったので書かせていただきます。 私の知る限りでは、static 変数はゼロ(あるいはヌル)に初期化されたはずです。関数の外で宣言された場合も同じくゼロに初期化されます。どちらもプログラムが実行されるとすぐに割り当てられ初期化され、終了するまで維持されます。 これが ANSI C で定義されていないのであれば、処理系独自の拡張という事でしょうか? 初期化されることを前提として書かれたソースも多くあり、これが ANSI から外されるというのは少し疑問です。

noname#2045
質問者

補足

>これが 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)
回答No.1

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; }

noname#2045
質問者

お礼

ありがとうございます。 誤っているということなので、特にコメントはございません。

関連するQ&A

  • static宣言について

    現在、c言語とDXライブラリを使ってゲームを製作しています。 http://okwave.jp/qa/q8270456.html 前回、このような質問をさせていただき、原因らしき箇所を突き止める所までいきました。 と言いますのも、配列の要素を指定する箇所(分かり辛かったらすいません。要素数ではありません)に、static宣言された変数を入れるとなぜかプログラムが強制終了されてしまうのです。 加えて、DXライブラリのGetColor関数にも返ってきた色をStatic宣言された変数に格納すると、何故か指定された色と違う色が出てしまうことが分かりました。 なんとか、その関数が終わる度に値を格納したかったので、色々試しました。 直接Static宣言されていない変数を間に噛ませてみたり、同じcpp内にわざわざ別の関数を用意して、そちらでStatic宣言し、値を返してみたり、と色々とやったのですが上手くいきませんでした。 ****************************************** int hairetu[20]; Static int z = 0; int x = z-1+1 hairetu[x] = zahyouX; (static宣言された変数zでhairetu[]の[]内を指定しようとするとダメ) (上記のように意味のない計算をさせてstatic宣言されていない変数を間にかませてもダメ) ****************************************** 結局の所、別のcppを用意して、ただ一つのStatic変数を+1していくだけの関数をくみ上げた所、上手くいったのですが・・・。何故このような事が起きるのでしょうか。 Static宣言された変数の決まりごと、もしくは、このような状態になってしまった原因など、見当がつきましたら教えていただけると有難いです。よろしくお願いします。

  • 関数原型宣言について

    関数原型宣言について 下記のプログラムのfunc関数は、関数原型宣言 <func(int a, long b, char *c);>が述されていないのにfunc関数の仮引数の型longは、関数原型宣言が与えられるといると本に書かれていたのですが、何故でしょうか教えて頂きたい。 ******************************************** #include <stdio.h> /*--- 三つの引数を受け取る関数 ---*/ void func(int a, long b, char *c) { int x; long y; /* … */ } int main(int argc, char *argv[]) { int a = 1; char s[] = "abc"; func(a + 3, 2, s); return (0); } *************************************************************

  • クラス内の関数内static変数について

    クラス内の「staticではないメンバ関数内で定義される」static変数の初期化タイミングはいつでしょうか? 自分としてはクラスのインスタンス生成時に初期化されるものだと思っていたのですが、どうもそうでは無さそうだという現象に出会ったもので。 例えば以下のようなサンプルプログラムがあるとします。 --------------------------------------- class TA { public: void func(int i); }; void TA::func(int i) { static int d=0; d += i; std::cout << d << std::endl; } int main() { for(int i=1; i < 3;i++) { TA ta; ta.func(i); ta.func(i); ta.func(i); } } --------------------------------------- これを実行した時、自分としては 1 2 3 2 4 6 という結果を期待していた訳ですが、実際には 1 2 3 5 7 9 という結果になりました。 ということは、もしかしてメンバ変数ではなくともクラス内に現れるstatic変数はstaticなメンバ変数と同等ということなのでしょうか? 実際、上記ソースのforループ内にもう一つclass TAのインスタンスtbを追加してみると、 --------------------------------------- for(int i=1; i < 3;i++) { TA ta; ta.func(i); ta.func(i); ta.func(i); TA tb; tb.func(i); tb.func(i); tb.func(i); } --------------------------------------- 1 2 3 4 5 6 8 10 12 14 16 18 となりました。 (まぁstaticではないメンバ変数に置き換えれば一応解決するのですが、個人的に何か凄く気持ち悪く感じて・・・)

  • static変数の配列の初期値を空文字列にする一般的な方法

    いつもお世話になっております。 static変数の配列の初期値を空文字列(各要素が'\0')にしたいです。 static変数なので放って置いても各要素に'\0'が入りますが、 初期値として空文字列にしている事を明記したいです。 そこで以下の様な方法を考えてみました。 (1)初期値に何もせず、コメントを書いておく (2)初回起動フラグを持ち、フラグが立っていれば空文字列にする (3)初期値にnull文字をSTR_LENの数だけ書く (4)初期値の先頭のみnull文字にし、残りは省略する どの様な方法が一般的でしょうか? 上記以外にもあれば、教えていただければ幸いです。 また、以下は私が考えたそれぞれの方法のソースです。 -------------------------------------------------------------------------------- #include <stdio.h> #include <string.h> #define STR_LEN 16 #define FLAG_ON (1) #define FLAG_OFF (0) -------------------------------------------------------------------------------- /* 初期値に何もせず、コメントを書いておく */ -------------------------------------------------------------------------------- void clear_1(void) { static char hoge[STR_LEN]; /* 空文字列 */ } -------------------------------------------------------------------------------- /* 初回起動フラグを持ち、フラグが立っていれば空文字列にする */ -------------------------------------------------------------------------------- void clear_2(void) { static char hoge[STR_LEN]; static int first = FLAG_ON; if(first == FLAG_ON){ strncpy(hoge, "\0", STR_LEN); first = FLAG_OFF; } } -------------------------------------------------------------------------------- /* 初期値にnull文字をSTR_LENの数だけ書く */ -------------------------------------------------------------------------------- void clear_3(void) { static char hoge[] = {'\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0'}; } -------------------------------------------------------------------------------- /* 初期値の先頭のみnull文字にし、残りは省略する */ -------------------------------------------------------------------------------- void clear_4(void) { static char str[STR_LEN] = {'\0', }; } --------------------------------------------------------------------------------

  • static宣言 について教えてください

    C言語の初心者です。内部的なstatic配列の理想的な応用例だと教科書に書いてありますが、static宣言しなくてコンパイルしても、普通に動きます。どうしてでしょうか。 char *month_name( int n ) { static char *name[] = { "Illegal month", "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" }; return ( n<1 || n>12 ) ? name[0] : name[n]; } ちなみに下のように書き換えると、static宣言しないとうまく動かなくなります。この二つの違いがわからないです。お願いします。 char *month_name2( int n ) { static char name[13][10] = { "NG", "1st", "2nd", "3rd", "4th", "5th", "6th", "7th", "8th", "9th", "10th", "11th", "12th" }; return ( n<1 || n>12 ) ? name[0] : name[n]; }

  • static変数とマルチスレッド

    よろしくお願いします。 マルチスレッドとstatic変数の関係で悩んでいます。 マルチスレッドでグローバルstatic変数は __declspec(thread) static int kensu; ってしないと、マルチスレッド間で同じものになってしまうのはわかっているのですが ファンクション内の場合はどうだったのかわかりません。 ---------------------------- static int aa; multi_th() { func(); } func { static int bb; } ---------------------------- この場合、変数aaはマルチスレッド同士で同じものとなると思うのですが 変数bbはマルチスレッド同士で同じものになってしまうのでしょうか よろしくお願いします。

  • staticのスコープについて

    //main.c #include "myfunc.h" #include <stdio.h> static char hoge[10]; int main(void){ myfunc(hoge); printf("%c",*hoge); } ------------------ //myfunc.h void myfunc(char *hoge) { *hoge = 'a'; } 上記のようなプログラムの場合、 a と表示されると思いますが、この動作は保証されているのでしょうか? つまり、staticにて宣言したモジュール内の変数は、他モジュールにポインタを渡しても、内容は保証されるのかどうかと言う事なのですが。 よろしければ教えてください。

  • 初期化

    int Func_read(void) { char*prtn1; char*prtn2; char szdat1[81]; char szdat2[81]; int nrtn; prtn1=fgets(szdat1,81,Fp1); prtn2=fgets(szdat2,81,Fp2); if(prtn1==NULL||prtn2==NULL) return(9); nrtn=memcmp(szdat1,szdat2,81); if(nrtn!=0) return } というプログラムで領域のすべてを比較するため、領域を0で初期化しなけれればなりません。どこで初期化すればよいでしょう?

  • 関数外からstatic変数を再度初期化できるのでしょうか?

    以下の静的変数?についてA,Bのメモリは別でしょうか? test関数外からBの変数を再度初期化できるのでしょうか? staticの仕組みがわからないのでstaticのメモリの初期化のタイミングとメモリアクセス方法について知りたいです。 static x = 1; // ---A int test() { static int x = 0; // --- B } またexternの働きも知りたいです。

  • staticで初期化した変数(?)を使って初期化

    次の3つのプログラム、1番目はコンパイルエラーになりますが、下2つはエラーになりません。1番目のプログラムのエラーメッセージ:初期化子が定数ではありません。 処理系にかかわらずそうだとしたら、その理由はなんでしょうか。 ---------------------------------- int main(void) { static int a=5; static int b=a; return 0; } ---------------------------------- int main(void) { static int a=5; int b=a; return 0; } ---------------------------------- int main(void) { int a=5; int b=a; return 0; }