質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

Q&A

解決済

5回答

2644閲覧

C言語のローカル変数はいつどこにできるのでしょうか

teireken

総合スコア19

C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

0グッド

0クリップ

投稿2019/01/20 03:30

gcc4.8.5使用です。
以下のコード

C

1#include <stdio.h> 2 3int a(){ 4 int x; 5 printf("aのx=%d &x=%p\n",x,&x); 6} 7int b(){ 8 int x; 9 printf("bのx=%d &x=%p\n",x,&x); 10} 11 12int main(void){ 13 printf("&a=%p\n",&a); 14 printf("&b=%p\n",&b); 15 a(); 16 b(); 17 a(); 18return 0; 19}

を実行してみたところ、
&a=0x561d9f4ec6aa
&b=0x561d9f4ec6f2
aのx=22045 &x=0x7fff1c391c84
bのx=22045 &x=0x7fff1c391c84
aのx=22045 &x=0x7fff1c391c84
と表示されました。
質問1:
以下の解釈で合っていますでしょうか?
(1)関数a(),b()内のローカル変数xは関数呼び出し時に生成されるのではなく、同じアドレスを使いまわしている。
(2)関数a(),b()はプログラムがロードされたときに固定したアドレスに配置されている。
質問2:
上記解釈の(1)に関連した質問として、a(),b()内のローカル変数xはいつ生成されたのでしょう。
関数a()のアセンブルは以下のようですが、どこでローカル変数xが生成されているのでしょうか?

a: push rbp mov rbp, rsp sub rsp, 16 mov eax, DWORD PTR [rbp-4] lea rdx, [rbp-4] mov esi, eax mov edi, OFFSET FLAT:.LC0 mov eax, 0 call printf leave ret

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答5

0

言語仕様にはローカル変数をどこにどう作るかなんて一言も書いてなく、コンパイラ屋の勝手です。
※ とはいえほとんどの処理系(コンパイラ)はスタック上にローカル変数を確保しますが。

投稿2019/01/20 06:58

episteme

総合スコア16614

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

teireken

2019/01/20 07:25

スタックを使うには利点があるからなのでしょうね。勉強します!
guest

0

関数a(),b()内のローカル変数xは関数呼び出し時に生成されるのではなく、同じアドレスを使いまわしている。

結果的には使い回されていますが、まあ一般的には関数呼び出し時に生成(という言い方も語弊があるが)されていると解釈されるのではないかと思います。

関数a(),b()はプログラムがロードされたときに固定したアドレスに配置されている。

違います。関数が呼び出されるたびに配置…といいますか、アドレスが決まります。

a(),b()内のローカル変数xはいつ生成されたのでしょう。
どこでローカル変数xが生成されているのでしょうか?

「生成」というものがおそらくあなたの思っているようなものと違います。
今回のようなプログラムをコンパイルして実行する間に行われることは次のようなものです。

・コンパイル時、ローカル変数を(スタックポインタからの相対値で)どこに置くかを決める。「置く」というのは、より正確に言うと、「どのメモリ番地のメモリを当該変数として使うかを決め、その番地を読み書きする機械語を生成することにする」ということです。
・コンパイル時、変数の場所として決めた場所をその変数として読み書きする機械語を生成する。
・実行時、関数に入るとスタック上の決められた場所を読み書きする機械語が実行され、その場所が読み書きされる。
・なお、「スタック上の決められた場所を読み書きする」際には大抵、「「「とあるレジスタ(=スタックポインタ)の値」に固定値を加算して生成された値」で指定されたアドレスに対して読み書きする」機械語が使われます。

さてどこがローカル変数の「生成」かですが、よく分かりません。スタックポインタをずらしてローカル変数分の空きを作るのが「生成」でしょうかね。
高級言語で考えているような概念と実際に機械語が動いている原理は異なるのであまりはっきりと対応付けすることはできないのではないかと思っています。

投稿2019/01/20 11:55

ikadzuchi

総合スコア3047

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

既に回答が付いていますが、

言語仕様にはローカル変数をどこにどう作るかなんて一言も書いてなく、コンパイラ屋の勝手です。

とは言うものの、全てがスタックではなく、レジスタの場合もあります。(特に組込み系) この場合、使いまわされて、同じレジスタが、前半では、X という変数を意味するが、後半では、Y という変数になる事も。
(ただし、アドレスを取ると、スタックとかの場所に置かれる)

あと、固定領域に置かれた場合、再帰呼出し時に大変な事(面倒な処理)になります。そういう意味でもスタックに置くのが楽。

投稿2019/01/20 08:35

pepperleaf

総合スコア6383

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

teireken

2019/01/20 09:13

なるほど! >>あと、固定領域に置かれた場合、再帰呼出し時に大変な事(面倒な処理)になります。そういう意味でもスタックに置くのが楽
guest

0

ベストアンサー

ローカル変数は関数の呼び出し時にスタックにその領域が確保されます
ということで、たまたまアドレスが一緒になることはありますが、それだけの話しです

質門1 間違ってます

質門2

push rbp

mov rbp, rsp sub rsp, 16

ここでrspに16減算してます。
これがローカル変数領域になるんですね
どういう動作になるか自分で追っかけてみましょうねっ

投稿2019/01/20 03:36

編集2019/01/20 03:39
y_waiwai

総合スコア87749

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

teireken

2019/01/20 03:46

>>どういう動作になるか自分で追っかけて そうですね。自分でできるようになりたいです。 具体的には、デバッガgdbを使えばできるのでしょうか?やってみます。 あと、異なる内容の質問が混じってしまいましたが、質問1の 「(2)関数a(),b()はプログラムがロードされたときに固定したアドレスに配置されている。」 のほうはあっているでしょうか?
y_waiwai

2019/01/20 03:47 編集

ああ、関数自体のアドレスなら固定ですね
y_waiwai

2019/01/20 03:49 編集

スタックというのが何か、スタックポインタというのはどういうふうに動作するのか、を調べてみましょう。 そこらへんを理解しないとローカル変数はややこしいですね
teireken

2019/01/20 04:05

なるほど!そういえば、スタックってどこにあるのか、知りませんでした。 メモリ内の特別な領域・・・空想です
guest

0

次のプログラムを調査してみてください。
main での a(),b() 呼び出し時と、 subX()/subZ() での a(), b() の結果が異なっています。
subX() と subY() での a(), b() の結果は同じです。

関数呼び出しするとスタックの状態が変わるし、
関数中でのローカル変数の有無でもスタックの状態が変わるからです。

a.c

c

1#include <stdio.h> 2 3void a() { 4 int x; 5 printf("aのx=%d &x=%p\n",x,&x); 6} 7 8void b() { 9 int x; 10 printf("bのx=%d &x=%p\n",x,&x); 11} 12 13void subX() { 14 printf("subX\n"); 15 a(); 16 b(); 17} 18void subY() { 19 printf("subY\n"); 20 a(); 21 b(); 22} 23void subZ() { 24 int z[100]; 25 printf("subZ\n"); 26 a(); 27 b(); 28} 29 30int main(void) { 31 printf("&a=%p\n",&a); 32 printf("&b=%p\n",&b); 33 a(); 34 b(); 35 a(); 36 37 subX(); 38 subY(); 39 subZ(); 40 return 0; 41}

実行例
イメージ説明

参考情報

  • ヒープとスタック

https://www.uquest.co.jp/embedded/learning/lecture16.html

投稿2019/01/20 08:38

katoy

総合スコア22324

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問