- ベストアンサー
C言語のグローバル変数の初期化について
C言語において int a = 1; // 動的グローバル変数 static int b = 2; //静的グローバル変数 funcA(){ int c = 3; // 動的ローカル変数 static int d = 4; //静的グローバル変数 ・ ・ ・ } 上記のように各種変数を初期化したとします。 "c"のような動的ローカル変数であれば、funcA()が呼ばれたときに毎回初期化されますよね? では ・"d"のような静的ローカル変数は、初めてfuncA()が呼ばれたときに初期化されるのですか? ・"a","b"のyほうなグローバル変数は、どのタイミングで初期化されるのですか? 以上2点について伺いたいと思います。 ちなみに、組み込み機器むけのソフトウェアを想定しています。
- みんなの回答 (15)
- 専門家の回答
質問者が選んだベストアンサー
No.11 = Interest です。 > コンパイル時に"a","b","d"の領域が.dataセクションに確保される。 コンパイル時ではなく、リンク時です。リンク前は、これらのデータがどこに配置されるかまだ決まっていません。 > コンパイル時にスタートアップルーチンが自動生成され(?) これ、違います。 スタートアップルーチンが自動生成されるとすれば、統合開発環境などを使ってプロジェクトを生成したときです。統合開発環境を使ってなければ、スタートアップルーチンを手で書くこともあります。(どこかからコピーしてくるほうが楽ですが。)コンパイル時に自動生成されるものではありません。 > main関数実行前に、 これは間違いではありませんが、注意が必要です。 CPUに電源が供給されて一番最初に走り始めるプログラムがスタートアップルーチンです。スタートアップルーチンが「いわゆるmain関数」を呼び出します。すでにご存じと思いますが、組み込みの世界では必ずしも main関数があるわけではないので、「main関数実行前に」という考えかたは適切ではありません。 > スタートアップルーチンが変数をROM→RAMへとコピーする際に、.dataセクションの変数を初期化する。(.bssセクションは0初期化する) 「変数をROM→RAMへとコピーする」ことと、「.dataセクションの変数を初期化する」ことは同じことです。言い換えれば「.dataセクションの変数を初期化するために、ROMに格納されている初期値をRAMへとコピーする」ってこと。
その他の回答 (14)
- notnot
- ベストアンサー率47% (4900/10361)
#2です。 C言語の質問への回答としては、書いたとおり、「mainの実行直前」で、それ以上区分けは出来ません。 補足での質問はすでに、C言語の枠を越えてOSの範囲に入ってきています。 これ以上の質問は、OSを限定してCコンパイラも限定して~例えばWindowsのVisual C++とか、Linux-Fedoraのgccとか~別途適当なカテゴリで質問し直すべきでしょう。組み込みの環境でもいろいろOSがありますし、OSの無い環境もありますし。
お礼
そうですね。 深入りして本質とは離れていきそうなので ここで終了とします。
- Interest
- ベストアンサー率31% (207/659)
No.11 = No.13 = Interest です。 > リンク時にデータ領域の確保とともに初期化していまえば > わざわざスタートアップ時に初期化する必要がなく > 起動時間の短縮につながるのでは?と思いました。 ご存じのとおり、「リンク」は、ビルド(コンパイル -> リンク)の流れで、普通はPC上の開発環境で実行します。このときはまだビルドしてできたプログラム(hoge.motとか、hoge.sとか hoge.hex とか)がターゲットとなるCPUのROMに書き込まれていませんよね。 できたプログラムをターゲットCPUをbootモードなどに設定してROMに書き込んで、そのターゲットCPUをuserモードに戻してから再起動して、初めてプログラムが動き始めます。 CPUに内蔵されているRAMを使う場合はバスの設定が不要ですが、外部RAMを使用する場合はCPUが起動してからバスの設定が必要です。バスの設定が終わって初めてその外部RAMにアクセスできるわけですから、当然、バス設定前の外部RAMにはアクセスできませんし、外部RAMの初期化もできません。 もっといえば、CPUに電源が入ってないとRAMの値は保持されないわけで。 「リンク時にRAMを初期化」というのが変なことを言っているのが分かっていただけましたでしょうか?
お礼
何度もありがとうございます。 ようやく理解できました。 今後ともよろしくお願いいたします。
- tadys
- ベストアンサー率40% (856/2135)
a,b,d については main() が呼ばれる前に初期化されます。 組み込みシステムといっても最近は規模が大きいものもあってパソコンと変わりの無いものもありますがそうでないものということで話します。 初期値付きの変数の初期値はROMに格納されています。 その初期値はmain関数を呼ぶ前にスタートアップルーチンによりRAMにコピーされるのです。 初期値のない変数はスタートアップルーチンによってゼロクリアされます。 スタックやヒープはクリアされないのが普通ですが初期化する場合もあります。 組み込みシステムのROMにはオブジェクトコードと初期値付き変数の初期値が格納されています。 システムによっては、オブジェクトコードをROM上で実行するシステムとRAM上にコピーして実行するシステムがあります。 RAMがコストアップの要因になるシステムではROM上で実行します。 ROMでは実行速度が問題になる場合はすべてをRAMにコピーしてから実行します。一部だけRAMというのもあります。 前者の場合、スタートアップルーチンが変数の初期値をRAMにコピーし、初期値なしの変数をクリアした後にmain関数を実行します。 後者の場合は、スタートアップルーチンがコードと初期値をRAM上にコピーし、初期値なしの変数をクリアした後にmain関数を実行します。 コンパイラはROM上のどの部分をRAM上のどこにコピーすればいいかという情報を作成しますのでスタートアップルーチンはそれを利用してコピーするようにします。 メーカーの開発環境ではこの部分は自動的に生成してくれますので気にする必要は無いのですがそのような環境が無い場合、あるいはカスタマイズする必要がある場合などは自前で用意する必要があります。
補足
>みなさま かなり詳しいご説明ありがとうございます。 いろいろなご意見があるようで、正直混乱しております。 現在のわたしは ・コンパイル時に"a","b","d"の領域が.dataセクションに確保される。 ・コンパイル時にスタートアップルーチンが自動生成され(?)、main関数実行前に、そのスタートアップルーチンが変数をROM→RAMへとコピーする際に、.dataセクションの変数を初期化する。(.bssセクションは0初期化する) という理解なのですが、これで正しいのでしょうか?
- Interest
- ベストアンサー率31% (207/659)
cygwinはコンパイラじゃないっす。cygwin使ってるならGCCでは? CPUが立ち上がった直後の世界なので、OSも関係なし。 ルネサステクノロジのSH2で、グローバル変数が初期化されるところの具体例出します。コンパイラはKPIT GCCです。 --- 以下、start.asm から抜粋 --- !load data section from ROM to RAM only if ROMSTART is defined #if ROMSTART ! initialise sections mov.l edata,r1 ! edata in r1 mov.l mdata,r2 ! mdata in r2 mov.l data,r0 ! data in r0 cmp/eq r0,r1 bt start_1 nop start_l: mov.b @r2,r3 !get from src mov.b r3,@r0 !place in dest add #1,r2 !inc src add #1,r0 !inc dest cmp/eq r0,r1 !dest == edata? bf start_l nop start_1: #endif // ROMSTART --- 抜粋ここまで --- startup.asm は、ごらんのとおりアセンブリ言語で書かれたスタートアップルーチンです。ご質問の外部変数の初期化は、スタートアップルーチンの中で行っています。上記のアセンブリコードでは、 ROM上のmdataセクションに外部変数の初期値が入っており、これをRAM上のdataセクションにコピーしています。bss(初期値なし)セクションのゼロクリアは上記アセンブリコードより後で行っています。 参考: http://mobile-robots.way-nifty.com/daily_report/files/apsh2f9a_rom_20071005.ppt
- jacta
- ベストアンサー率26% (845/3158)
a, b, dのメモリ領域はコンパイル時に割付けられます。より厳密に言えば、具体的なアドレスが決定するのはリンク時です。 静的記憶域期間を持つ初期値付きオブジェクトは、一般的には、.dataセクションに配置され、スタートアップでROM→RAMに転送されます。ただし、今回の場合は、.textセクションのプログラムと一緒にブート時に転送されることになると思います。 ゼロクリアされるのは.bssセクション(つまり初期値無しオブジェクト)だけです。 dに関しての誤解は、C++における定数式以外の初期化子との混同ではないかと思います。例えば、 void func() { double y = std::sqrt(3.0); ... } のyは、funcが呼び出され、最初にyの宣言位置に実行パスが差し掛かったときに初期化されます。それ以前はゼロクリアされることになります。
- Oh-Orange
- ベストアンサー率63% (854/1345)
★アドバイス >1."a","b","d"ともコンパイル時にメモリに領域が確保されると同時に初期化される。 コンパイル時ではありません。 実行されたときに初期化されます。 >2."a","b","d"ともスタートアップルーチン時にメモリに領域が確保されると同時に初期化される。 これです。 BSSセクションをすべてゼロで初期化。 その後に指定された『値』1とか2などで初期化。 この順です。 >3."a","b"はスタートアップルーチン時、"d"は該当関数が呼ばれた時にメモリに領域が確保されると同時に初期化される。 これは間違いです。
- jacta
- ベストアンサー率26% (845/3158)
a, b, dはいずれも静的記憶域期間を持ちます。初期化は通常スタートアップで行います。 ちなみに、aは外部結合、bは内部結合、dは無結合になります。 cは自動記憶域期間を持ちますので、funcAが呼び出され、その宣言部分に実行パスが差し掛かるたびに初期化されます。 これに関しては、組込みであろうがなかろうが関係ありません。ただし、main関数云々の話はフリースタンディング環境では通用しません。
補足
>みなさま 丁寧なご回答ありがとうございます。 "c"の自動ローカル変数に関しては、関数呼び出し時に初期化でOKのようですが "a","b","d"に関しては意見が分かれるようですね。 まとめると 1."a","b","d"ともコンパイル時にメモリに領域が確保されると同時に初期化される。 2."a","b","d"ともスタートアップルーチン時にメモリに領域が確保されると同時に初期化される。 3."a","b"はスタートアップルーチン時、"d"は該当関数が呼ばれた時にメモリに領域が確保されると同時に初期化される。 の3種類の意見に分類できそうですが、これは環境によって初期化の方法が違うのでしょうか? 私の場合は、Winマシンでcygwinというコンパイラでコンパイルし 組み込み機器のROMにファームを焼き 機器の電源ONと同時にブートローダによってファームをROMからRAMに移す といった処理をしています。 ちなみに機器のOSはLinuxです。
- Oh-Orange
- ベストアンサー率63% (854/1345)
★アドバイス 組み込み系は詳しくありませんので一般論で回答しておきます。注意。 ・次のリンクの 『セクションとか.textとか』 『初期化してない変数とか.bssセクションとか 』 を読んでみて下さい。 http://www.ertl.jp/~takayuki/readings/info/→『「infoを読め」と言われたら...』 さらに詳しく知りたい場合は『BSS セクション』で検索してみましょう。 >"c"のような動的ローカル変数であれば、funcA()が呼ばれたときに毎回初期化されますよね? ↑ 自動変数だね。 毎回初期化されます。 >"d"のような静的ローカル変数は、初めてfuncA()が呼ばれたときに初期化されるのですか? ↑ これは『a』『b』と同じくスタートアップ・ルーチンで初期化されます。 スタートアップ・ルーチンはmain()関数が呼ばれる前の処理です。 プログラムが起動した最初の1回しか初期化されません。 >"a","b"のyほうなグローバル変数は、どのタイミングで初期化されるのですか? ↑ これも『d』と同様にスタートアップ・ルーチンで初期化されます。 ・最後にもう一つ次のリンクを読んでみて下さい。 http://www.ertl.jp/~takayuki/readings/c/no10.html→『初めてのC言語 - 第10回 三度目のスタートアップ』
お礼
大変参考になるサイトご提示いただき ありがとうございました。
- jx-word
- ベストアンサー率40% (38/94)
組み込み向けですよね。 どのコンパイラか分からないので一般論ですが、通常はデータセクションに最初から埋め込まれています。 データセクションはROM領域に乗るのが普通なので、何らかの方法でRAMにコピーして使います。大抵はブートコードでコピーするはずです。 どのタイミングといわれれば、コンパイル時かブート時ということでいいんじゃないでしょうか。
- asuncion
- ベストアンサー率33% (2127/6290)
> dはプログラムが開始されたときに領域確保+初期化され、 これも正しくないですね。 dはあくまでも関数内のローカル変数ですから、 当該関数の実行時に領域確保と初期化を行ないます。 仮に、当該プログラムが一度も当該関数を実行しないとすると、 dのための領域は当該プログラムの実行中確保されません。 staticが付いているから、一度領域を確保すると、 当該プログラムの実行中ずっとその内容を保持している、というだけです。
- 1
- 2
補足
返事が滞っており申し訳ありません。 非常にわかりやすく説明いただきありがとうございます。 処理の流れは理解できたのですが >言い換えれば「.dataセクションの変数を初期化するために、ROMに格納されている初期値をRAMへとコピーする」ってこと。 リンク時にデータ領域の確保とともに初期化していまえば わざわざスタートアップ時に初期化する必要がなく 起動時間の短縮につながるのでは?と思いました。 度々申し訳ございませんが よろしくお願いいたします。