C言語プログラム実行時には必要なメモリが割り当てられる。
起動に必要になるのは静的な場所とシンボルをもった場所。
実行中はシンボルだけのモノは中身が要らない。実行時にあればおkなのでその情報だけ要る。
とどのつまり、プログラムとプロセスで要る情報は違う。
宣言と定義、リンケージをてきとうに与えよう。
func(int arg) {...}
arg は宣言に近い。初期化や割り当ては呼び出し時で。
func はグローバル。
func(int arg)
{
char c;
int b;
}
arg は呼び出し元で確保されてるだろう。
c と b は呼び出し元に返るまで有効。
注目すべきは
int f1(int arg)
{
char c;
int b;
return b;
}
int f2(int arg)
{
int b;
char c;
return b;
}
これらは問題のない程度にちがう。
int が プッシュされてから char がプッシュされるか、その逆かだけだけど。
さまつなようだけど、次は事情がちがう。
int f(int arg)
{
int buff[SIZE];
int x;
return 0;
}
関数の呼び出しはまず、リターンアドレスが上位に。
そのしたに下位から上位アドレスの順に ローカルな引数 → ローカルな変数が積まれる。
[スタック]
上位
f のリターンアドレス
引数arg
buff
x
下位
配列が増えすぎるとリターンアドレスに届く・・・。
逆:
int f(int arg)
{
int x;
int buff[SIZE];
return 0;
}
buff[0] がスタックの成長方向にあり、buff[SIZE]が x の手前。
buff[SIZE+1] が x の場所(かもね~)。
これは、次のような配列の隣り合う要素を調べるのが問題なく行えると言えるだろう。
int i;
int buff[SIZE];
for(i=0; i<SIZE; i++) {
if(buff[i]==buff[i+1]) {...}
}
構造体を思い出すといい。
struct hoge
{
char x;
int y;
};
hoge のオブジェクトは char -> int の順。
そして
struct hoge ob;
struct hoge *p = &ob;
この場合 p の値とob.xのアドレスは同じ。
よって、次の場合。
char *cp = &(ob.x);
cp = cp + sizeof(int);
cp は ob.y の位置を指してるはず。
こういった事実を活かして構造体の末尾をノリシロにしてる構造体があるね!
struct X
{
int z;
char data[1]; /* 場合によって char *data もあり */
};