Cプログラミング初心者のものです。
課題でわからないところがあるので質問いたします。
以下ソース
typedef struct comp{
int val1;
int val2;
}*comp_t,comp_val;
comp_t first();
void second();
int main(int argc, char **argv){
comp_t ret;
ret=first(val);
second();
printf("Val1=%d,Val2=%d\n",ret->val1,ret->val2);
}
comp_t first(){
comp_val comp;
comp.val1=10;
comp.val2=20;
return ∁
}
void second(){
int a=-100;
int b =-200;
}
実行すると
Val1=-200,Val2=-100
と出ます。ローカル変数が消滅し、second()で新たなintを二つ定義しているので、キレイに上書きされて結果が出たことはわかるのですが、
それなら普通、-100,-200の順に出るのではないのでしょうか。
なぜ裏返ってしまったのでしょう?
また、それはソースを見ただけでわかるものなのでしょうか。
返信お願いします。
No.4ベストアンサー
- 回答日時:
思いっきり処理系依存な話ですが、一般的なC言語処理系というかコンパイラの出力では、そのような答えになる場合が多いと思います。
なぜそうなるのかはC言語のソースではなく、アセンブラ出力を見れば確実にわかりますが、
・C言語のたいていの処理系(コンパイラ)では、自動変数は「スタック」を使って実現しています。
・大抵の実行環境(CPU)では、スタックはアドレスの「後ろ」から順に使っていくようになっています。
以下、int が4バイトとして書きます。
・first関数
スタックの末尾に8バイトを自動変数 comp 用に確保します。
その8バイト内では、構造体のメンバが val1 、val2 の順に並んでますから、
val1 のアドレス: スタック末尾-8バイト(変数の確保位置)+0バイト(構造体内での位置)
val2 のアドレス: スタック末尾-8バイト(変数の確保位置)+4バイト(構造体内での位置)= スタック末尾-4バイト
になります。
・second 関数
変数a、変数bの順にスタック末尾から4バイトずつ確保されます。
a のアドレス: スタック末尾-4バイト
b のアドレス: aのアドレス-4バイト = スタック末尾-8バイト
結果、comp.val1 とb、comp.val2 とa がスタック上の同じアドレスに割り当てられることになります。
main 関数内で、 printf("&argc=%p, &argv=%p, &ret=%p\n", &argc, &argv, &ret);
first 関数内で、printf("&comp=%p, &comp.val1=%p, &comp.val2=%p\n", &comp, &(comp.val1), &(comp.val2));
second 関数内で、 printf("&a=%p, &b=%p\n", &a, &b);
というコードを追加してみれば、どんな感じでスタックが消費されてアドレスが変わっていくのかが少しは見えると思います。
No.3
- 回答日時:
>ソースを見ただけでわかるものなのでしょうか
わかりません.
ただ,処理系依存なので,処理系を併記すればわかる人はわかります.
関数を呼ぶときは,スタックに制御情報が積まれて,引数が積まれて,ローカル変数が積まれる.
(スタックだと思ってください.
| firstのローカル変数
| firstの引数
| firstの制御情報
| mainの情報
first関数が終わってmainに戻ったら
|
|
|
| mainの情報
となるが,firstの情報は削除されることなく残っている.
ローカル変数のポインタを取得しておけば
後に積まれることになるであろうsecondのローカル変数の位置がわかる.
で,secondを呼び出すと
| secondのローカル変数
| secondの引数
| secondの制御情報
| mainの情報
ここで,secondから戻ってきた直後は,secondのローカル変数の値は保持されている.
firstを読んだ時に,ローカル変数へのポインタわかっているので,プログラムのように出力が可能.
処理系依存なのですべてのコンパイラで可能なわけではありません.
このあたりのことをふまえた上での課題ですかね?
違うなら,前の回答者さんがおっしゃってる通り
ローカル変数へのポインタ参照は危ないのでやめましょう
ってことで終わりです.
上のようなことを考えてなら,次のような理由だと思います.
aとbでは,スタックにa,bの順番で積み
| b = -200
| a = -100
| 制御情報
| mainの情報
と積まれるが
comp_val は構造体なので
| comp_val.val1
| comp_val.val2
| 制御情報
| mainの情報
comp_valという1つの塊としてスタックに積まれてしまったため順番が逆になったものと思います.
大変遅くなってしまいましてごめんなさい、解答ありがとうございます。
構造体と普通の値ではスタックの積み方が異なるんですね...詳しい説明ありがとうございました。
No.2
- 回答日時:
バグはNo.1さんのおっしゃる通りとして、
多分、わざと危険なことをしてローカル変数を潰す
実験だと思います。
それで、No.1さんのおっしゃる通り、
”いつ解放されるかわからないローカル変数のポインタを返してはいけません。”
がプログラマの鉄則で、こういうプログラムは通常存在しません。
あくまで、ローカル変数のメモリ領域への上書きの実験的意味合いが強いのでしょう。
順序が逆になる点は、多分、Cのコンパイルプログラムの作りの問題で、structのときだけ、たまたま、メモリの割りあて順序を逆にしているというだけで、そのような規則はないと思いますよ。
したがって、”それはソースを見ただけでわかるものなのでしょうか。”についてはわからないと思います。
ちなみに言えば、Cのコンパイルプログラムの作りによって、
ローカル変数は、上記プログラムで、必ず潰れるとも限らないと思います。
大変遅くなってしまいましてごめんなさい、解答ありがとうございます。
有名なプログラミング言語でも、規則通りに作らないとランダム性が出てしまうんですね。
No.1
- 回答日時:
> ret=first(val);
変数valの宣言がどこにもありませんね、というかfirstは引数を取らないんでは。
> comp_t first(){
> comp_val comp;
> comp.val1=10;
> comp.val2=20;
> return ∁
> }
いつ解放されるかわからないローカル変数のポインタを返してはいけません。メモリ領域を破壊しかねない危険行為です。
> void second(){
> int a=-100;
> int b =-200;
> }
これではsecond()も新たにローカル変数を作ってそこに値を入れてるだけなので、ret(first()内のcomp)のメンバーには影響を与えません。
本当にこのソースで完全(first()に絡む間違いはさておき)なのであれば、「たまたまval1,val2(がかつて存在したアドレス)にその値が入っていた」だけにしか見えません。
この回答への補足
すみません。first(val)はfirst()の間違いでした。
このソースで、printfがどのように表示されるか、というのが課題です。
ローカル変数のポインタを返しているのはわざとだと思われます。
お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!
関連するカテゴリからQ&Aを探す
おすすめ情報
- ・漫画をレンタルでお得に読める!
- ・【大喜利】【投稿~11/12】 急に朝起こしてきた母親に言われた一言とは?
- ・好きな和訳タイトルを教えてください
- ・うちのカレーにはこれが入ってる!って食材ありますか?
- ・好きな「お肉」は?
- ・あなたは何にトキメキますか?
- ・おすすめのモーニング・朝食メニューを教えて!
- ・「覚え間違い」を教えてください!
- ・とっておきの手土産を教えて
- ・「平成」を感じるもの
- ・秘密基地、どこに作った?
- ・【お題】NEW演歌
- ・カンパ〜イ!←最初の1杯目、なに頼む?
- ・一回も披露したことのない豆知識
- ・これ何て呼びますか
- ・チョコミントアイス
- ・初めて自分の家と他人の家が違う、と意識した時
- ・「これはヤバかったな」という遅刻エピソード
- ・これ何て呼びますか Part2
- ・許せない心理テスト
- ・この人頭いいなと思ったエピソード
- ・牛、豚、鶏、どれか一つ食べられなくなるとしたら?
- ・あなたの習慣について教えてください!!
- ・ハマっている「お菓子」を教えて!
- ・高校三年生の合唱祭で何を歌いましたか?
- ・【大喜利】【投稿~11/1】 存在しそうで存在しないモノマネ芸人の名前を教えてください
- ・好きなおでんの具材ドラフト会議しましょう
- ・餃子を食べるとき、何をつけますか?
- ・あなたの「必」の書き順を教えてください
- ・ギリギリ行けるお一人様のライン
- ・10代と話して驚いたこと
- ・家の中でのこだわりスペースはどこですか?
- ・つい集めてしまうものはなんですか?
- ・自分のセンスや笑いの好みに影響を受けた作品を教えて
- ・【お題】引っかけ問題(締め切り10月27日(日)23時)
- ・大人になっても苦手な食べ物、ありますか?
- ・14歳の自分に衝撃の事実を告げてください
- ・架空の映画のネタバレレビュー
- ・「お昼の放送」の思い出
- ・昨日見た夢を教えて下さい
- ・ちょっと先の未来クイズ第4問
- ・【大喜利】【投稿~10/21(月)】買ったばかりの自転車を分解してひと言
- ・メモのコツを教えてください!
- ・CDの保有枚数を教えてください
- ・ホテルを選ぶとき、これだけは譲れない条件TOP3は?
- ・家・車以外で、人生で一番奮発した買い物
- ・人生最悪の忘れ物
- ・【コナン30周年】嘘でしょ!?と思った○○周年を教えて【ハルヒ20周年】
- ・あなたの習慣について教えてください!!
- ・都道府県穴埋めゲーム
デイリーランキングこのカテゴリの人気デイリーQ&Aランキング
-
VB.netでDLLを読み込んで実行す...
-
スタック領域変更
-
エラー?メッセージ
-
ubuntuで デイスク/deb/loopと...
-
プログラムの規模を表す単位「k...
-
パソコンでインターネット接続...
-
ライン数とステップ数の違いに...
-
AUTO-MDIX機能の無効化
-
hdmiはパラレル?シリアル?
-
ステップ数について
-
タイピングと
-
Native VLANの変更
-
この二つの問題とける人いませ...
-
MOS EXCEL エキスパートに出て...
-
ステップ数によるスケジュール
-
一つのスッチングハブに異なる...
-
SP領域とはなんですか?
-
ステップ数??
-
第一級陸上特殊無線技士
-
ブロック化因数(ブロッキング...
マンスリーランキングこのカテゴリの人気マンスリーQ&Aランキング
-
VB.netでDLLを読み込んで実行す...
-
最大スタックサイズを大きくす...
-
エラー?メッセージ
-
printf / sprintf のスタック消...
-
gccでスタックサイズを変更する...
-
関数のプロローグとエピローグ...
-
スタック領域変更
-
スタックとキューの使い所
-
pthreadのスタックサイズ設定取...
-
リストを使った逆ポーランド記...
-
c言語でスタック領域の使われ方...
-
スタックの伸張方向
-
WINDOWSなどのOSを構成している...
-
関数呼び出しでのスタック消費量
-
ゆゆにゃ。
-
消滅したローカル変数のメモリ...
-
GCCで関数の引数が渡らない
-
キューとスタックの問題です、...
-
コンパイラオプション
-
cloneのスタック管理
おすすめ情報