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

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

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

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

Q&A

解決済

9回答

1108閲覧

番地の増え方の理由をおしえてください.

hikaru_love_n

総合スコア16

C

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

1グッド

5クリップ

投稿2019/10/16 03:08

aの番地は 0xffffcbfc
bの番地は 0xffffcbf8
xの番地は 0xffffcbf0
yの番地は 0xffffcbe8

番地がどのように増えていっているかを考察しないといけないのですが、
なぜこのように増えていくのかがわかりません。

ai_2013_dev👍を押しています

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

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

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

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

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

rubato6809

2019/10/16 03:38

これらの番地はプログラムが表示したはずですし、そのプログラムを質問者は知っているはずです。 そのプログラムを質問に追記してください。 分かればで良いですが、使っているコンパイラの種類も書いてください。Visual何とかとかGCCとか、そういった名前です。
Zuishin

2019/10/16 05:55 編集

C のレベルのみで説明できるという主張なのにコンパイラの種類関係ありますか?
rubato6809

2019/10/16 07:41

あらら(笑)。 1. そうですね。コンパイラの種類がわからなくても回答は可能です(でも、コードが示されないなら私は答えるつもりはないし、皆さんが答えれば私が答える必要は無いわけだし)。 2. コンパイラの種類は、アセンブリコードに直す云々と違い、初心者でも把握可能な範疇の情報です。聞いても大したハードルにはならないだろう。 3. しかも「わかればで良い」=わからなければ答えなくても構わない。 4. もしコンパイラの種類がわかれば、固有の事情を何か具体的に語れるかもしれない(=わかっても語れないかもしれない)。 5. 応じ方次第で、質問者がどんな状況にあるか何か手がかりになりうる(=ならないかもしれない笑)。 ・・・といった気持ちです。
Zuishin

2019/10/16 08:12 編集

皆さんの回答がそろうまでの時間稼ぎですか? 説明のためのコードは自分で作れるでしょう。言い訳する間に回答をどうぞ。
tmp

2019/10/16 09:18

課題の回答は、人から見せてもらったけど、考察は同じだとばれるから自力で書けよっていわれて、ここにかいたのかな? 説明の範囲での正しい回答は得られるかもしれんが、一緒に講義受けた人ぐらいしか、課題の出題者の期待する答えは得られないとおもう。 ここでは多くの人は実務などで有効な回答をするが、学校などでは実務ではありえないが、講義を受けて理解しているかの回答を期待するので、運が悪いと講義を聞いていなかったということで、減点になったりするよ
hikaru_love_n

2019/10/17 03:01

#include<stdio.h> int main(void){ int a, b; double x, y; a = 12; b = 34; x = 5.6; y = 7.8; printf("aの番地は %p\n", &a); printf("bの番地は %p\n", &b); printf("xの番地は %p\n", &x); printf("yの番地は %p\n", &y); return 0; } プログラムはこちらです。課題の解答等すべて自分で出しています。 コンパイラの種類はgccです。 返答までに時間がかかり申し訳ありません。
rubato6809

2019/10/17 03:13

まずは応答ありがとう。 そのコードを(ここでなく)質問文に貼り付けることはできますか?コードの貼り付け方を理解していますか?
guest

回答9

0

アセンブリコードに直してから研究してください。でなければどの変数がどこにどのように割り当てられるかは不定です。レジスタに割り当てられることもありますし、もしかしたらパッキングされることもあるでしょう。

C言語のコードをアセンブラ出力を確認しながら最適化する | 組込みエンジニアの思うところ

これは gcc ですが、コンパイラによってやり方は違います。また出力されるコードもコンパイラや最適化オプションによってそれぞれですから、同じコードなのに別のアドレスに割り当てられることもあります。

追記

C

1#include <stdio.h> 2#include <stdlib.h> 3 4int main(int argc, char* argv[]) { 5 int a = 0; 6 int b = 0; 7 char c = 0; 8 char d = 0; 9 int e = 0; 10 printf("a:%X, b:%X, c:%X, d:%X, e:%X\n", (unsigned)&a, (unsigned)&b, (unsigned)&c, (unsigned)&d, (unsigned)&e); 11 return 0; 12}

Visual Studio の cl で上記ソースをコンパイルし、実行した結果です。

a:6757FD6C, b:6757FD68, c:6757FD61, d:6757FD60, e:6757FD64

御覧の通り、一番最後に宣言された e が b と c の間にあります。c と d は 1 バイトなので、その直後(スタックはアドレス番号が小さいほど後になります)に int 型を割り当てると速度的に不利になるため、アライメントを合わせるために前に置かれているのです。このアライメントというのは、たとえば 16 ビットのコンピューターなら 16 ビット毎、64 ビットなら 64 ビット毎にある境界のことで、データをこの境界に合わせて置かないと読み書きが遅くなります。
ですから、char 型が 4 つあった場合、対象コンピューターが 16 ビットなのか 64 ビットなのかによって配置も変わる可能性があります。

そして、今回は後に置かれていますが、コンパイラによっては後に置かずアドレス調整のみで対応する場合もあるかもしれません。この場合、扱うデータによってはスタックの使用量で不利になります。

また、これはローカル変数なのでスタックに割り当てられていると推測されますが、必ずそうなるとは限りません。レジスタに割り当てられた場合、& でアドレスを取得できないので、アセンブリコード必須になります。

ソースレベルで判断できるなど笑止千万です。そんなものは対象の環境によってもコンパイラによっても最適化によっても変わってくる話です。推測は可能ですが、それはあくまでも推測にすぎません。

また今回はアドレスを出力させていますが、本番のコードでは必ずしも出力させるとは限りません。出力させるコードとさせないコードではコンパイル結果も変わるので、ソースコードをいじることでいじらなかったコードの状態を判断することはできません。

追記

アセンブリコードが無いので、「その結果から推測されること」しか考察できません。スタックが使われていることも、そのスタックが後ろからプッシュされていることも推測です。
double が 8 バイトで int が 4 バイトのメモリを使用してしるようです。
スタックに順に保存されていると推測するのが妥当でしょう。

変数開始終了バイト数
affffcbfcffffcbff4
bffffcbf8ffffcbfb4
xffffcbf0ffffcbf78
yffffcbe8ffffcbef8

このように割り当てられていると思います。まずはスタックポインタが ffffcc00 を指しているところに 4 バイトの a がプッシュされたので、スタックポインタは 4 減って ffffcbfc を指し、そこからの 4 バイトに a の内容が書き込まれます。次に 4 バイトの b がプッシュされたのでスタックポインタは 4 減って ffffcbf8 を指し、そこからの 4 バイトに b の内容が書き込まれます。

このように宣言された順にプッシュされているようです。

しかし、既に書いたようにこれは「たまたま」です。結果を見た上で「最適化の影響はないんだな、ローカル変数にはスタックを使ってるんだな」という想像はつきますが、常にこうなるわけではありません。

ターゲットが 64 ビットであればまた順序が変わったかもしれません。

そもそもスタックに一つ一つプッシュされたのではなく、計算でアドレスを決められた可能性の方が高いかもしれません。

考察せよと改めて言われた場合、全てを推測で片付けたのではレポートとしてお粗末なものになるのではないでしょうか。

投稿2019/10/16 03:31

編集2019/10/17 04:44
Zuishin

総合スコア28656

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

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

rubato6809

2019/10/16 03:44

ハードルを高くするのって、初心者をいじめているように見えますね。アセンブリ言語にしなくても、Cのレベルで十分アドバイスできるんですから。
Zuishin

2019/10/16 04:04 編集

それが本当に可能だと思うならやってみてください。
maisumakun

2019/10/16 08:32

極論、Cコンパイラは最適化の過程で変数を抹消しても「同じ動作をするならば」構わないわけですし、具体的な割付が状況依存としか言えないのは間違いないです。
rubato6809

2019/10/16 08:39

なるほど、回答に追加したのね。 すごーいむずかしい考察ですね。私も大変勉強になりました。ありがとう。 でも、今の質問者が知りたいことはそういうことなんですかね?って思いました。
Zuishin

2019/10/16 08:41

そういうことでなく他のことを知りたいという確固たる信念があるなら、それに従った回答をどうぞ。無いなら低評価を外すのが筋では?
rubato6809

2019/10/16 08:50

低評価は外しません、少なくとも今は。質問者がコードを示してない段階で私は判断しないので。
Zuishin

2019/10/16 08:52

では私も質問者がコードを示していない、意図がわからない質問の回答に低評価をつけてまわることが許されるということですか?
rubato6809

2019/10/16 09:01

それは違うんじゃないかな。 質問者がコードを示して、私の予想が外れたとわかったら低評価は外します。
Zuishin

2019/10/16 09:02

当たったとわかった時に低評価したらどうですかね?
Zuishin

2019/10/16 09:04

その予想が何なのかもわからないので後出しし放題ですが。
fana

2019/10/16 09:08

初心者相手にわざわざ難読コード貼ってるような回答パターンとは違って,この回答は普通に有用な情報の提示ではないでしょうか. 読み手(質問者を含む)側は欲しい考察のレベル(?)に合わせて,情報を取捨選択すれば良いと思うし.
pepperleaf

2019/10/16 11:47

> 出力させるコードとさせないコードではコンパイル結果も変わる 確かに、、。 昔使った組み込み系のコンパイラは、 &でアドレス取らないと、レジスタ割り当てし、アドレス取った途端、スタックに領域確保....なんて。 で、レジスタ割り当ても、関数の前と後ろで別の変数だったりとか。 設問に問題有りで、実用上の意味は何でしょう? お勉強か。
Q71

2019/10/16 13:28

質問が質問の体を成していないので、「考察云々の前に条件示さんかい」としか言いようがない。 実例を出してある良い回答だと思います。
ikadzuchi

2019/10/17 01:24

具体的な配置になった理由を考察する質問に対して別の配置になる場合についてのみ書き連ねるの、ちょっと意味わかりませんね。
Zuishin

2019/10/17 01:30

間違っている回答に間違っている点を教えてくれるならありがたいことですが、最近、そうではなく、ただわけのわからないイチャモンをつける人が湧いていますね。「空気中では浮力は無視できる」のような、何世紀の生まれだよっていうイチャモンとか。 意味わからないなら勉強すればいいんじゃないでしょうか。 > アセンブリコードに直してから研究してください。でなければどの変数がどこにどのように割り当てられるかは不定です。 この回答の趣旨は、研究・考察にはアセンブリコードが必要だということで、追記はその意味がわかってない人への解説です。
tacsheaven

2019/10/17 04:45

考察というならば、他の条件は同じにして、変数宣言だけ変えた(宣言の順番を変える、型を変える、変数名を変えるなど)複数のコードで試してみて、情報を増やすのも有りですかね。 宣言の順番を変えたのにアドレスの並び方は変わらなかったとすれば、コンパイラが最適化をしているという考察が成り立ちますし。 これならばアセンブリまで行かなくてもある程度は(あくまでもある程度です)考察できるかもしれません。
Zuishin

2019/10/17 04:51 編集

色々とパターンは考えられると思いますが、必ずしも宣言や定義の順番に割り当てられるわけではないことを示すには一例で十分だと考えていました。きりがないのでこのくらいにしときます。考察しなきゃいけないのは私ではなく質問者さんなので。
ikadzuchi

2019/10/17 13:18

> 「空気中では浮力は無視できる」 私への当てつけかと思いますが、ソースも示さず別ページの私の言葉をあなたの勝手な思い込みで捻じ曲げて伝えるのをやめてもらえませんか? > 意味わからないなら勉強すればいいんじゃないでしょうか。 あなたの言動を勉強するのですか? やっているといえばやっていますね現に今。もうやめたいですけど。 > この回答の趣旨は、研究・考察にはアセンブリコードが必要だということで アセンブリコードの要否についてはともかくとして、 現に存在するある1つの具体的な割り当てについての質問に対して「どの変数がどこにどのように割り当てられるかは不定」という事についてのみ書いているのを問題視しています。
ikadzuchi

2019/10/17 13:21

でそれはそれとして、2つ目の追記の内容が適切だと思うので低評価取り消し&高評価します。
Zuishin

2019/10/17 13:23

その前と同じことしか書いてませんけどね。 ソース追加に合わせてちょっと具体的になっただけで。
ikadzuchi

2019/10/17 13:35

> 「その結果から推測されること」しか考察できません。 「その結果から推測されること」を考察しているのが大きな違いですね。
Zuishin

2019/10/17 13:37

その前もしてますが。まあ何にせよ具体化されたことで理解が追い付いたようでよかったです。
ikadzuchi

2019/10/17 14:05

してますか…? ああ、探せば確かに「これはローカル変数なのでスタックに割り当てられていると推測されます」の部分のみは結果に対する考察ですね。 「ほとんど考察していなかったのが大いにするようになった」と訂正します。
Zuishin

2019/10/17 14:09

御覧の通り、一番最後に宣言された e が b と c の間にあります。c と d は 1 バイトなので、その直後(スタックはアドレス番号が小さいほど後になります)に int 型を割り当てると速度的に不利になるため、アライメントを合わせるために前に置かれているのです。このアライメントというのは、たとえば 16 ビットのコンピューターなら 16 ビット毎、64 ビットなら 64 ビット毎にある境界のことで、データをこの境界に合わせて置かないと読み書きが遅くなります。 ですから、char 型が 4 つあった場合、対象コンピューターが 16 ビットなのか 64 ビットなのかによって配置も変わる可能性があります。 そして、今回は後に置かれていますが、コンパイラによっては後に置かずアドレス調整のみで対応する場合もあるかもしれません。この場合、扱うデータによってはスタックの使用量で不利になります。 また、これはローカル変数なのでスタックに割り当てられていると推測されますが、必ずそうなるとは限りません。レジスタに割り当てられた場合、& でアドレスを取得できないので、アセンブリコード必須になります。 と書いてますが、長すぎて読み飛ばしましたか?
ikadzuchi

2019/10/17 14:14

1段落目、質問の内容と別のコードに対する考察 2段落目、質問の結果と異なる場合があるという内容 3段落目、先述の部分除き、質問の結果と異なる場合があるという内容 であり、「その結果から推測されること」でないです。
Zuishin

2019/10/17 14:21

あなたの中では考察と推測がイコールなのですか?
ikadzuchi

2019/10/17 14:37

似たようなものですがイコールではないです。 "「『その結果から推測されること』に対する考察」でない"と書いたほうが適切でしたね。
Zuishin

2019/10/17 14:41

「まともに考察するにはアセンブリコードが必要」という回答なので、それは当然ですね。
tacsheaven

2019/10/17 23:47

最近は16byte境界(DDR3 SDRAM におけるメモリアクセス単位)や64byte境界(x86 系の一次キャッシュ単位)なんてものまでありますね。
Zuishin

2019/10/17 23:55

大きいですね。そのうちいつかメインメモリの境界もそうなるかもしれませんね。
tacsheaven

2019/10/18 00:02

メインメモリを2枚単位(あるいは3枚、4枚単位)で増設するのも、ある意味そうですよね。一枚で増設することもできますが、速度を出すためには複数枚にしてアクセスを並列化してますし。
rubato6809

2019/10/18 00:26

> まともに考察するにはアセンブリコードが必要 ということは、 > ソースレベルで判断できるなど笑止千万です。 という回答(考察?)は、まともとはいえない、テキトーなレベルの考察結果であった、という理解でよろしいですか?
Zuishin

2019/10/18 00:53

推測でしかないとさんざん書いてありますが?
Zuishin

2019/10/18 00:54

推測は「すいそく」と読みますから、辞書を引く時の助けになれば。
rubato6809

2019/10/18 00:59

では、是非ともあなたが示されたコード、 int a = 0; int b = 0; char c = 0; char d = 0; int e = 0; .... というコードに対して、推測ではなく、まともな考察をしてみせていただきたいのですが。
Zuishin

2019/10/18 01:03

> 「まともに考察するにはアセンブリコードが必要」という回答なので
rubato6809

2019/10/18 01:33

あれ、それで終しまいですか? 初心者に対して「アセンブリコードに直してから研究してください」と答えたからには、こんな風に研究も考察もできるんだよ、とまともな考察のお手本を見せてくれて良さそうなものですが。 まさか、自分にはできない事を初心者には要求した、とか?Zuishinさんほどの手練れが、そんな無責任な回答しませんよね〜?
fana

2019/10/18 01:40

(このサイトの人達は何でこんなに喧嘩腰なんだろう…) 実際にアセンブリコードを示してみせてどうのとか,仮にそこまでやるにしても それこそ,「実際に質問者がそこまで知りたいという意思を示したのを確認してから」でよいような.
Zuishin

2019/10/18 01:49

〇の国の人の言うことですから、我々とは少し考え方が違うんでしょう。
rubato6809

2019/10/18 02:29 編集

ということはほぼ結論出たようですね。 少なくともこの問いに対してアセンブリ言語まで降りる必要は無かったのです。 繰り返しますが、私が低評価をつけたのは初心者に対してアセンブリ言語を要求するのは良くないと思ったからです。Cの質問をしたつもりの初心者が、アセンブリ言語を見なければならないとしたら、どう感じるでしょうか。この質問者に限らず、ここで質問することをためらう人がいるかもしれない、そうなったら残念なことです。初心者にアセンブリ言語を強要しても詮無いこと。 質問者それぞれの状況に応じた寄り添い方があるだろうし、そうしたことを想像できるのが経験者じゃないのかなと。
maisumakun

2019/10/18 02:31

> ということはほぼ結論出たようですね。 すみませんが、その結論を得た論理がわかりません。
rubato6809

2019/10/18 02:34

・質問者はアセンブリ言語レベルの解説を求めているようにはみえない ・アセンブリ言語で研究せよ、Cソースレベルで判断できるなど笑止千万、と回答したZuishinさん自身、一行たりともアセンブリコードを持ちだしていないし、もちだそうともしていない。
maisumakun

2019/10/18 03:01 編集

Zuishinさんの回答は「実際にコンパイルした環境でのアセンブリについての情報がなければ、『考察』ではなく『推測』しかできない」、という点で一貫していると思うのですが、rubato6809さんの解釈は違うのでしょうか。
tacsheaven

2019/10/18 02:59

Cで「ポインタのアドレス」なんてものを持ち出してくる時点で、アーキテクチャに強依存しますから、アセンブリ言語まで下りなきゃ詳しい考察なんてできませんよ。 ましてや質問者は元となった C のソースコードすら出していないのですから、「あのような形に出力されるであろう C のソースコード」は、無限と言っていいほどのバリエーションがあり得ます。最低限、元となった C のソースコードと、コンパイラと、コンパイルオプションと、ターゲットのアーキテクチャが分からなければ、絞り込むことすらできないのです。
rubato6809

2019/10/18 04:07

まず、こちらから。 > 質問者は元となった C のソースコードすら出していない それは違います。質問への追記・修正依頼に応じて、Cコードを提供しました。それがあるから、「double が 8 バイトで int が 4 バイトのメモリを使用してしるようです」というZuishinさんの回答、私の回答、そして raccy さんの回答があります。 > 『考察』ではなく『推測』しかできない」、という点で一貫している 質問者のCコードを、質問者のコンパイラでコンパイルしたアセンブリコードが無いのだから無理からぬこと、と仰りたいのかな? 私はコードを見て私なりに回答できました。予想通り、アセンブリコードに言及する必要はありませんでした。そこに誤りや論理の飛躍ががあるならご指摘ください。私もZuishinさんも、doubleは8バイト、intは(まず間違いなく)4バイトを前提としました。それは間違いでしょうか? 表示されたアドレスは、そのアドレスに変数が割り当てられたという、紛れもない確実な証拠です。それすら「推測」なのでしょうか? 質問者のアセンブリコードが無いとしても、Zuishinさんはご自身で作った int a = 0; int b = 0; char c = 0; char d = 0; int e = 0; ... というコードと、表示されたアドレスから(アセンブリコードへの言及無しに、推測なのか考察なのか)何かを語っていますよね。 繰り返しになるけど、(Zuishinさんはご自分のコードからアセンブリコードを得られるのだから)「ソースレベルで判断できるなど笑止千万」と結論づけるなら、「推測」ではなくアセンブリコードで「考察」してみせて欲しい、できるはずでしょう、と言いたい(しかし、そうはしないようである)。 コンパイラがそのような変数割付けをすることは多くの人が経験的に知ってます。そうする理由(アラインメントとかキャッシュとか)も大抵は想像がつくはずです。 でもその時、それはアセンブリコードが無いと理解できない事でしょうか? むしろ私には、Zuishinさんの「ソースレベルで判断できるなど笑止千万」こそ根拠薄弱な飛躍に思えます。
tacsheaven

2019/10/18 04:39

同じソースでもコンパイラやコンパイルオプションやターゲットが違えば、別の結果が出るんですけど? だからソースからでは判断できないんです。 複数のソースを食わせてみて(変数宣言の順番を変えるなど)、その結果を並べてみれば辛うじて分かりますけど、ソースが1本だけなら判断のしようがありません。 ※複数のソースを食わせることで、上の分からない情報がある程度絞り込めるから、です。本質的には上記の情報があった方がよいことは言うまでもないです
rubato6809

2019/10/18 04:50

> 同じソースでも・・・別の結果が出る Cを学び始めた人、或いはこの質問者に対して、まず答えるべきことは0xffffcbfc〜0xffffcbe8と表示された、その意味するところを説明することだと思います。それがわかれば、次は仰るようなことも伝えたほうが良いでしょうね。 アセンブリコードは必要ですか?
Zuishin

2019/10/18 05:51 編集

アセンブリコードの必要性については十分な説明があるので繰り返しませんが、要するにそれが無くても自分の説明で十分だと言いたい訳ですね。 内部の動作が知りたい時にアセンブリコードを出力するのは常識なのですが、この回答があなたに早すぎたのは理解しました。 しかしそれは質問者さんにとっても早すぎる根拠にはなりません。質問者さんにとって早すぎたかどうかはこれからわかるでしょう。それをあなたが判断するのはおこがましい気がします。
ikadzuchi

2019/10/19 18:48

> maisumakunさん 推測することは考察のうちでしょう。 > tacsheavenさん > 同じソースでも~別の結果が出る > だからソースからでは判断できない 「ソース」と「結果」が揃っている時、なぜその結果が出たかは推測/考察することができますが、何が判断できないというのでしょうか。 どうも「考察」という言葉の定義がずれているように思います。
Zuishin

2019/10/19 22:36 編集

まあ考察は何の根拠もない推測だけで十分だと思うならそれでいいんじゃないですか? 場合分けもせず、色々と仮定を真とした上での推測ならこの回答にも書いていますが、それで十分ならそこだけ読んでも回答として成り立つでしょう。
guest

0

ベストアンサー

なぜこのように増えていくのかがわかりません。

いや、減ってるじゃん!ってツッコミを誰もしないので私がしておきますが、細かく説明するとそう簡単なことではありません。かいつまんで述べると次のような事を知っておく必要があります。

  • ローカル(自動)変数がメモリ上に領域を確保する場合は、スタック領域またはスタック用としてコンパイラが指定した領域にスタックとして詰まれる。
  • x86やx86_64等の主に使われているCISCのCPUでは、スタック領域は仮想メモリ空間の後ろに配置され、前の方向に向かって詰まれていく(詰まれた分だけ番地は減る)。つまり、スタックにpushすればスタックの先頭を指し示すレジスタ上のスタックポインタは前に進み(減る)、popすればスタックポインタは後ろに進む(増える)。(RISCのCPUではそもそもスタック領域やスタックポインタが用意されていなかったり、スタックが終わりの方向に詰まれる(詰まれた分だけ番地は増える)というのもあるらしい)
  • 最適化を有効にしない(最適化無し、GCCであれば-O0をつけるようなもの)場合は、ほとんどのコンパイラや環境では、ローカル変数は定義した順番でスタックに詰まれていく。(最適化無効でもスタックに詰まれる順番が定義した順になるとは限らないという場合もあるようです。コメントを参考にしてください。)
  • 変数に対して、アドレス演算子など変数のアドレスそのものに対する処理が含まれる場合は、最適化が有効でも必ずメモリ領域上に確保される。(n1570のどこかに書いてあったはず。逆に言うとそうでは無い変数はレジスタ上に現れるだけだったりするということ)
  • 変数は環境および型によってサイズが決まっていて、sizeof演算子で取得できる。x86およびx86_64環境では、intは4、doubleは8である。各変数ではこのサイズが十分入る分の領域が確保される。
  • 変数は環境および型によって先頭のメモリが取れる位置が決まっており、char等1バイトの型を除けば、飛び飛びになる。これをアライメントといい、_Alignof演算子(stddef.hをincludeしていればalignofでも可能)で取得できる(_AlignofはC11から)。x86およびx86_64環境では、intは4、doubleは8である。
  • スタックへ変数は連続して詰まれるが、アライメントによりその場所に置けない場合は、置ける場所までずれる。
    ※ 質問のコードからちょっと変更を加えた物ですが、 https://godbolt.org/z/Ch2Uzwhttps://godbolt.org/z/hKTXFR を見比べればわかると思います。intはrbpとの差が4の倍数であれば置けますが、doubleは8の倍数の所にしか置けないため、二つ目のコードのxについて[rpb-12]ではなく[rbp-16]になっています。
  • 実際の所、x86_64のほとんどのコンパイラでは、ローカル変数毎にpushするのではなく、関数開始時にローカル変数が十分入る大きさのスタック領域を確保し、その領域に対してローカル変数を割り当てると言うことをしている。ただし、上から割り当てるのか、下から割り当てるのかはコンパイラによって異なる。GCCは上から下に向かって割り当てていく(pushで実装していた時代の名残と思われますが、ここら辺の歴史に詳しい人がいたら教えてください)。
    ※ 前の項でどちらも、変数毎にスタックへpushするのでは無く、最初に32バイトのスタック領域が確保するようにしているのがわかると思います。ただ、なぜ一つ目のコードが24バイトでは無く32バイトなのかはわかりません。x86_64でのスタック領域確保は16バイト単位とか縛りがあるんでしょうか?詳しい人教えてください。

上記のことからx86_64で、GCCで最適化オプション無しの場合(ただし一部のOSでは異なる場合がある)、上のアセンブリの結果も参考にすると、

  1. aはサイズ4なのでベースポインタ(関数がベースとするスタックの位置)より4少ない位置に配置される。この位置は4の倍数なのでアライメントも満たす。
  2. bはサイズ4なのでaより4少ない、ベースポインタより8少ない位置に配置される。この位置は4の倍数なのでアライメントも満たす。
  3. xはサイズ8なのでbより8少ない、ベースポインタより16少ない位置に配置される。この位置は8の倍数なのでアライメントも満たす。
  4. yはサイズ8なのでxより8少ない、ベースポインタより24少ない位置に配置される。この位置は8の倍数なのでアライメントも満たす。
  5. 関数自体は上の処理を見越して、あらかじめ32(なぜ24ではないかは私にはわかりません)少ない位置にスタックポインタを進めている。

となります。異なるアーキテクチャ、OS、コンパイラ、コンパイラオプション、定義の順番が異なるコードでは違う動作になります。例えば、clangに変更しただけでも4,12,8という差になります。clangだと最初に4バイト確保しているようで、その分ズレて、xの時にアライメントにあわせてズレてしまったため、bxの差がGCCと異なったと言うことです。この最初の4バイトは何かというと・・・すいません、調べてもわかりませんでした。


以上ですが、わからないと言っているところばかりで申し訳ありません。もっと詳しいことはコンパイラを作れるぐらいのCとCPUに関する知識が無いと難しいと思います。残念ながら、私にはそこまでの知識は無いので、かいつまんだ内容になってしまい申し訳ないです。

投稿2019/10/17 12:23

編集2019/10/19 03:00
raccy

総合スコア21733

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

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

rubato6809

2019/10/18 00:15

> いや、減ってるじゃん! (ツッコめなかったのがくやしい笑) > 最適化無効でもスタックに詰まれる順番が定義した順になるとは限らないというコンパイラがあれば それは私の手元のコンパイラの事じゃないかな。OSともども3年以上そのまま使ってます。 $ cc --version cc (Ubuntu 5.4.0-6ubuntu1~16.04.11) 5.4.0 20160609 Copyright (C) 2015 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. $ cc -O0 bbb.c $ ./a.out axby aの番地は 0x7ffff982f810 bの番地は 0x7ffff982f814 xの番地は 0x7ffff982f818 yの番地は 0x7ffff982f820 "axby" という表示は変数の定義順を確認できるように、こうしているから。 int a = 12; double x = 5.6; int b = 34; double y = 7.8; printf("axby\n"); printf("aの番地は %p\n", &a); printf("bの番地は %p\n", &b); printf("xの番地は %p\n", &x); printf("yの番地は %p\n", &y);
fana

2019/10/18 01:11

> いや、減ってるじゃん! 「-8だけ増えた」とか言えばOK(どうでもいい)
Zuishin

2019/10/18 01:14

昇順に並べなおして「増えた」って言ってる人がいますね。
fana

2019/10/18 01:32

そうか! 0xffffcbfcとかを符号付だとして見れば増えたように見えるんだ! (アドレスをintで扱うコードが今まさに目の前にやってきた偶然!)
raccy

2019/10/18 12:06 編集

> rubato6809さん 私の手元でも確認しました。私の確認している範囲ではUbuntuのGCCだけがそうなるようです。16.04LTSと18.04LTSの公式レポジトリ上のGCCで-O0でも定義順では無いことを確認しました。対して、同じUbuntuでもclangの場合や同じLinuxのGCCではCentos7の場合は定義順に詰まれて行ってました。(いずれもx86_64) -O0をつけても全ての最適化オプションが無効にされるわけでは無い 参考: https://stackoverflow.com/questions/33278757/ という話かと思い`gcc -Q -O0 --help=optimizers`の結果の見比べもしましたが、これだという最適化オプションは見つけられませんでした。 同じGCCですし、CPUのアーキテクチャも同じですし、それでも違うと言うことは、ディストリビューション特有のなんかあるんだろうとは思います。ただ、clangではそうでないことから、ディストリビューションとして必須では無いのかも知れません。こういう面に詳しい人がいたら教えて欲しいところです。
pepperleaf

2019/10/19 11:52

> なぜ一つ目のコードが24バイトでは無く32バイトなのかはわかりません。 私も知りません。 ただ、昔、見たコンパイラの出力では、ユーザーが定義した変数以外にもコンパイラが内部作業変数を確保する場合があります。そしたら、当然、その分、増えます。... おっと、戻り用とかのスタックを保持するなんてのもあるか、まあ、アセンブラ出力を見れば、一目瞭然。C言語でアドレス出力しただけでは分からない事は多数です。(もっともアドレスだって、今は、論理アドレス)
guest

0

番地がどのように増えていっているかを考察しないといけないのですが、

前提条件が少なすぎます。これ以外に、与えられたコードはないのですか?

abxyが変数なのか、構造体のメンバなのか、はたまたそれ以外のものかすらわからない状況では、何も言えません。構造体のメンバは順番を入れ替えたり勝手に消したりが許されませんが、ローカル変数の場合は特に問題ありません)

投稿2019/10/16 08:36

maisumakun

総合スコア145121

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

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

guest

0

回答としてはダメかもですが…

考察しないといけないのですが

という言い回しの雰囲気から察するに,
「授業(講義?)か何かで説明があった話に照らし合わせた範疇での」考察とやらをすればよい状況なのかな,という雰囲気を感じます.

いきなり何の事前説明もなく「おら,何でこうなるのか考えてみせろや!」とか無茶を言われているわけではないでしょうから,
例えば「スタック領域がどうの」的な説明等があったりしたのではないかな?と推測します.
そういった事前説明された際に出てきた言葉について調べてみるとよいのではないでしょうか.

投稿2019/10/16 06:41

fana

総合スコア11632

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

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

hikaru_love_n

2019/10/17 03:02

説明等は全然なかったです。
fana

2019/10/17 03:17

うーん,どうすべきかは求められている考察の程度次第でしょうから, 回答群の中でそれに見合った物を参考にして調べてみればどうでしょう. (同じ課題を課せられている人がいるなら,そこらへんの雰囲気の探りを入れてみると良いかも) とりあえず int と double のサイズを調べて,a,b,x,yがどのようにアドレス空間を占めているかを絵に描いて整理してみて,あとは「C言語 スタック領域」とかでググるなりした結果と合わせるとかすれば,とりえあずは最低レベルの「考察」になるのかも?
guest

0

どのようなコードを書いて調べた結果か書かないと、考察しようがないですよ?

多分こうだろうという推察はできますが、必ずしもそれが正しいわけではないので。

投稿2019/10/16 03:18

編集2019/10/16 03:18
tacsheaven

総合スコア13703

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

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

0

コンパイラのマニュアルに変数の割付に関する規則が書いてありませんか?

基本的には、どの変数をどう割り当てるかはコンパイラの(コンパイラの作者の)自由です。
「こうなるのはなぜか?」の正解はコンパイラの作者に聞かなければ分かりません。

アセンブラで書いたプログラムとCで書いたプログラムをリンクするような場合を想定している
コンパイラなら、変数をどう割り付けるかをアセンブラレベルで説明してあると思います。

お使いのコンパイラでそのような説明を書いたマニュアルが見つからなければ、
「そういうもんだ」
と思う以外にありません。

投稿2019/10/16 05:42

nob.

総合スコア711

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

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

0

ここ

メモリサイズとアドレスの関係を理解するみそは「型」です。

投稿2019/10/16 03:16

WathMorks

総合スコア1582

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

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

Zuishin

2019/10/16 09:32 編集

アドレスの差が一定ではありません。質問ではそのことをたずねているのだと思いますが、私の回答に書いたようにアライメントの最適化などの問題があり、型だけでは説明できません。
guest

0

変数はメモリ上に配置されるのが原則です。そして&演算子は変数のアドレスを取得する演算子であることはご存じでしょう。
変数のアドレスが「増えて」いくのは、

  • それぞれの変数がメモリ上に割り当てられているから
  • それぞれの変数はメモリ上で必要なバイト数を必要とするから

です。
質問者は、表示結果をアドレス順に並べ直してみましたか?並べ直すと、こうなります。

yの番地は 0xffffcbe8 (~0xffffcbef)
xの番地は 0xffffcbf0 (~0xffffcbf7)
bの番地は 0xffffcbf8 (~0xffffcbfb)
aの番地は 0xffffcbfc (~0xffffcbff)

念の為:例えばこういった関係になってますね。

  • 0xffffcbe8(yの番地)+ 8 = 0xffffcbf0(xの番地)
  • 0xffffcbf8(bの番地)+ 4 = 0xffffcbfc(aの番地)

これは実は

  • double 型の変数 y, x がそれぞれ8バイト(64bit)の領域を占めている
  • int 型の変数 b, a がそれぞれ4バイト(32bit)の領域を占めている
  • y, x, b, a がメモリ上で連続(密着)している

ことを示しています(括弧内に書き加えたアドレスは、各変数に割り当てられた、最後のメモリのアドレス)。

というのは、この8バイト、4バイトという値(大きさ)は、double, int という、それぞれの型の変数に必要なメモリの大きさだからです。これらの大きさは sizeof() で確認できますので、質問者も次のようなコードで確かめてみると良いです。

C

1 printf("size of double = %d\n", sizeof(double)); 2 printf("size of int = %d\n", sizeof(int));

変数 y, x, b, a は関数内のローカル変数なのでスタック領域に割り当てられているはずです。そのアドレスが0xffffcbe8~0xffffcbffなのだから、おおよそこの辺りがスタック領域となっているようだとわかります(スタックという言葉がわからなければコメントなり新たな質問をたてるなりしてください)。スタック領域がどこになるか、いつも同じではありません。コンパイラによって、OSによって、プログラムによって、様々な条件で大きく変わります。

注意すべきは y, x, b, a という順序はコンパイラが勝手に決めてよいことになっているので、常にこの順序になると思ってはいけない事です。この場合はたまたまそういう順序になったのです。
上の例は「密着」していますが、いつも密着しているとも限りません。~~この場合はたまたま密着したのです。~~この場合は、double型変数が2つ、int型変数が2つという組み合わせは密着が可能だということになります。密着しなかった=隙間ができた実例がZuihsinさんの回答にあります。ただ、あのコードなら必ず隙間ができるかというと、そうでもありません。質問者がご自分で試してみるとよいと思います。

もうひとつ簡単な実験をしてみるのも面白いと思います。それは変数を定義する順序を変えてみることです。例えば今

double x, y; int a, b;

となっている順序を

int a, b; double x, y;

としてみる、とかです。今、私の手元に3種類のCコンパイラがありますけど、

  • 定義する順序を変えたらメモリ上の並び順が変わったコンパイラ
  • 定義する順序を変えてもメモリ上の並び順が変わらないコンパイラ

があります。3つのうち2つはGCCですが(どちらもGCCなのに)この振る舞いが異なりました。ことほどさように、変数がメモリ上にどう配置されるかはコンパイラ次第です。アドレスは確かめてみないとわからないのです、もっともそんなことを気にしてプログラムを書くことは少ないですが。

投稿2019/10/17 04:42

編集2019/10/19 12:51
rubato6809

総合スコア1380

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

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

Zuishin

2019/10/17 04:49

そりゃまあ昇順に並び替えたら「増えて」いくことになるでしょうね。結果を都合のいいように捻じ曲げているだけです。
rubato6809

2019/10/19 12:47

打ち消し線で消した「たまたま密着」という一文は、回答した時から心にひっかかりがあったので、修正しました。他にも軽微な追加・修正をしました。
rubato6809

2019/10/19 12:52

「念の為」部分を移動(苦笑)
rubato6809

2019/10/19 13:05

いつの間にか6名から低評価。この数は初めてです。Zuishinさん以外5名の理由は何でしょうか?コメントしなくても低評価を付けられるとは言え、理由を語らないのは理由を言語化できないまま低評価した、ということかしら。こそこそ陰口言ってるようなアンフェアーな人が5名はいそうですね。
guest

0

タグがCなのでC言語を前提にお話しします。

C言語の標準機能では、メモリアドレスを絶対番地で知ることや操作することはほぼ無理です(NULLとかは例外)。基準点からの相対値情報を用いることになります。

一方、基準点や刻み幅は状況によって決まります。つまりC言語の中だけでは具体的なことは定まりません。

例えばint型の配列int x[10]を使用すると。x[0], x[1], x[2]・・・x[9]に充たるメモリ領域が用意されます。これはx[0]を基準に追番が使用されますが、この基準となるx[0]のアドレス&x[0]はOSが割り振りますので指定できません。また、各要素領域の大きさも決められてはおりません。4バイトかもしれませんし8バイトかもしれません。

投稿2019/10/16 14:59

HogeAnimalLover

総合スコア4830

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

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.50%

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

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

質問する

関連した質問