プロが教えるわが家の防犯対策術!

c言語でスタック領域の使われ方について質問です。

メイン関数{

if(true){

} //if文終わり


サブ関数1()
サブ関数2()




このときのスタックのプッシュ、ポップの行われ方について教えてください。


自分の予想では以下のようになると考えていますが自信が全くありません・・・・

1 まずメイン関数をプッシュ
2 if文をプッシュ     ???   <???が特に自信ないです。
3 if文をポップ      ???
4 サブ関数1をプッシュ
5 サブ関数1をポップ   ???
6 サブ関数2をプッシュ
7 サブ関数2をポップ   ???
8 メイン関数をポップ

もし知っていたら教えてください。
よろしくお願いします。

A 回答 (7件)

>if(x){


>int y=1;
>}
>y=4;
>こうしたときエラーが出る理由はどうしてなのでしょうか?
スコープの関係です。
「int y」はif文の{}の中で宣言されている為、{}内でしか使用できません。
上記であれば、if文よりも前({}の外)に定義している必要があります。
「エラーが出た」で終わらず、「エラーメッセージの内容」を理解することも行ってください。
    • good
    • 0
この回答へのお礼

回答ありがとうございます。。
>>スコープの関係です。
>>「int y」はif文の{}の中で宣言されている為、{}内でしか使用できません。


"variableの存在範囲外でそれを使おうとしただけ”であって、
別にpopされたから使えないというわけではないんですね。
とても助かりました。

それと、質問の内容がとても初歩的でかつ勘違いしたものになっていて、
本当に申し訳ないです。。

それでも、一応、私が知りたかったことは理解できたので、
回答してくださった皆さん本当にありがとうございます。

お礼日時:2010/01/27 18:53

>if(x){


>int y=1;
>}
>y=4;
>こうしたときエラーが出る理由はどうしてなのでしょうか?

コンパイラの動作と実行時のスタック動作をゴチャ混ぜに考えるとそうなりますね。
ともかくこれはC言語の文法の問題です。変数のスコープと言って、ブロック({}←これ)内で宣言された変数の寿命はブロック外に出るまでと取り決められているためです。
{
int y=1; ← ここでyが有効になる。
} ← ここでyが無効になる。
実際問題、コンパイラの実装の問題なので変数yのメモリ実体は消えていないかも知れませんが、C言語の規約でそうなっている以上はエラーです。
    • good
    • 0

処理系を特定せずにスタックといわれても困ります。


それに、予想を見ると、プログラムの動作ではなく構文解析時の動作のようにも見えます。
何が知りたいのか、そして想定している処理系は何か、あわせて補足してください。
    • good
    • 0

#2の補足についてです


エラーってのは y が定義されてないってエラーじゃないですか?
原因は y のスコープに問題があるからです

そのソースプログラムでは yがif文の条件に引っかかったとき
しか定義されません

コード的に見れば絶対ifの条件に引っかかりますが
コンパイラは其処までのチェックは行いません

if文の前に int y; を書いてみてください
    • good
    • 0

スタックがメモリ領域であることはご存知ですか?


プッシュがメモリへの登録、ポップがメモリからの取り出しであることを理解していますか?
メモリに登録したり、取り出したりする理由は何か分かっていますか?
>if文をプッシュ/ポップ
 if分は条件を判断して分岐したり、しなかったりする制御文です。何をメモリに記録するんですか?
 必要ないですね。よって、「if文をプッシュ/ポップ」などと今後は言わないようにしましょう。
>サブ関数1をプッシュ/ポップ
 意味不明。但し、関数を呼び出す場合、その関数が終わる時に帰り先が分からないと困るので、
 呼び出し側(メイン)から見て、関数の次の命令のアドレスをスタックにプッシュします。
 呼び出される側(関数の方)はreturnする時にスタックから帰り先アドレスを取り出し、そこに
 制御を移します。つまり、帰り先アドレスをプッシュ/ポップするのです。
 尚、関数のパラメータもスタックにプッシュされます。このデータはポップされません。関数から
 帰る時に、呼び出し側で始末する(PASCAL方式)か、呼び出し元で始末(標準)されます。

自動変数(関数の中で特に記憶域クラスを指定しない変数)はスタック上に領域が取られます。
勿論、帰り先アドレスを壊さないように確保されますが、ポインタや配列のインデックスを間違うと、
これらの領域が破壊され、プログラムが「暴走」することになります。また、関数のパラメータの
後始末は既に述べたように二通りあるので、呼び出し元と関数側で方式が異なると、スタックが
ズレてやはり「暴走」することになります。スタックの使用例は以下の通りです。

main() {
   関数(long P1,long P2)
   ★
   次の命令
}

(1)P2をスタックにプッシュ
(2)P1をスタックにプッシュ
(3)「★」のアドレスをスタックにプシュ
(4)「関数」に制御を移す(分岐命令)
(5)「★」P1,P2を破棄するためSPに8を加算して始末する
(6)次の命令が実行される

(3)(4)は実際には1命令(動作は2ステップ)です。
★は標準の場合の後始末で、関数がPASCAL方式の場合は生成されないコードです。
尚、CPU、関数形式によってはP1、P2のスタックへの積み込み方が逆の場合もありますが、
後退法のCPU、標準形式ではこの順で行われます。
これで分かるように、関数のパラメータはスタックに呼び出し元変数のコピーが作られ、それが
渡されています。関数側でパラメータ内容を変更しても、それはコピーをイジッているのであって、
呼び出し側の変数自体は何も変化しません。
    • good
    • 0

if分単体でPush/Popされることはありません


#1さん書かれたURL見た方が分かりやすいと思いますが
基本的にPush/Pop(スタック)が使われるのは関数呼び出し時と
その引数の受け渡しのときのみとなります
また関数呼び出しのときにスタックに格納されるのは
その関数ではなく戻りアドレスとなるメイン関数(呼び出し元)の
アドレスとなります
また関数側では自分で使うレジストリなどを関数開始時に
Pushして関数終了時にPopします

スタックされる順番などは呼び出し規約によって異なってきますし
必ずしもスタックが使われるわけでもありません
(場合によってはレジスタ渡しで終わることもあります)

アセンブラコード自分で書かないのであればそこらへんは
コンパイラが自動でやってくれますので
コンパイラがどのようにアセンブラコードに変換しているか
見てみるのが勉強になるでしょう

自前でアセンブラコード書くのであれば気をつけないと
簡単にアプリケーションが暴走します

この回答への補足

早速の回答ありがとうございます。

やはり、大きな勘違いをしていたみたいです。。。
(まだ完全に理解できてません)

>>if分単体でPush/Popされることはありません

これについて質問があるのですが、
int x=1;

if(x){
int y=1;
}
y=4;
こうしたときエラーが出る理由はどうしてなのでしょうか?

エラーが出たので、
ifもpush/popされるのではないかと考えてしまいました・・・。

もし、この理由を知っているかたがいたら、教えてください。

よろしくお願いします。

補足日時:2010/01/27 17:29
    • good
    • 0

C言語をアセンブルしたときにどうなるか、ということでしょうか?



C言語をアセンブルした時にpushが使われる代表的な例としては関数の呼び出し時に引数がpushされるとかでしょうかね(ただし、呼び出し規約によって変わるので一概にはなんとも…)。
http://ray.sakura.ne.jp/asm/9.html

私も大して詳しくはないので、あとは専門家にお任せします。
    • good
    • 0

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!