次のように関数を書いたとします。
void foo()
{
char *strArr[] = {"AA", "BB", "CC"};
...
}
strArr はどこに割り当てられますか? いつ初期化されますか?
何らかの理由で、そのような配列はスタックではなく静的メモリ空間に割り当てられ、プログラムの開始時に初期化されることを覚えています。偽りの記憶ですか?
次のように関数を書いたとします。
void foo()
{
char *strArr[] = {"AA", "BB", "CC"};
...
}
strArr はどこに割り当てられますか? いつ初期化されますか?
何らかの理由で、そのような配列はスタックではなく静的メモリ空間に割り当てられ、プログラムの開始時に初期化されることを覚えています。偽りの記憶ですか?
自動ストレージがあるため、関数のスタックに割り当てられます。要素はこの自動ストレージに属しますが、文字列リテラル自体は永続的な読み取り専用領域に格納されます。
配列はローカル オブジェクトとして割り当てられます。一般に、すべてのローカル オブジェクトのメモリは、関数の実行が開始されたときに早期に割り当てることができます。または、制御がオブジェクト宣言でネストされたブロックに入ると、「オンデマンド」で割り当てることができます。言語は正確な瞬間を特定していません。
特定の例では、配列は関数の「プライマリ」ブロックで宣言されているため、関数の実行が開始されると、配列のメモリが割り当てられます。
初期化に関しては... CのC89/90バージョンでは、すべての集約初期化子は定数式でなければなりません。つまり、集約初期化プロセスは実行時の値に依存できません。初期化は、コンパイルされたプログラムにハードコードすることができ、配列が割り当てられた直後に実行できます。
C99 では、集約初期化子で実行時の値を使用できます。つまり、実際の初期化は、制御が宣言を通過する時点まで遅延する必要がある場合があります。
あなたの例では、配列は定数式で初期化されています。つまり、割り当て直後に初期化できます。コンパイラは、実際には事前に配列の静的コピーを準備し、コントロールが関数に入るたびにそれを単にスタックに「貼り付ける」ことができます。
PS「静的メモリ」への参照は、実際に"AA"
は、配列の要素を初期化するために使用する文字列リテラル(など)に適用されます。実際、文字列リテラルは静的メモリに存在します。ただし、文字列リテラルは完全に独立したオブジェクトであり、strArr
配列も完全に独立したオブジェクトです。
あなたの例には実際には「文字列配列」がないことに注意してください。あなたが持っているのは、文字列へのポインタの配列です。あなたstrArr
はローカルメモリに住んでいますが、文字列(strArr
ポイントの要素)は静的メモリに住んでいます。
サンプルプログラムを逆アセンブルしました。
#include <stdio.h>
void foo()
{
char *strArr[] = {"AA", "BB", "CC"};
}
int main()
{
foo();
return 0;
}
文字列リテラルは.rodata
セクションに配置されるようです。
.file "sfsfs.c"
.section .rodata
.LC0:
.string "AA"
.LC1:
.string "BB"
.LC2:
.string "CC"
.text
.globl foo
.type foo, @function
foo:
.LFB0:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
.....
.....
ただし、strArr はスタックにのみ割り当てられます。それは自動性質だからです。