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

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

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

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

GCC

GCCはGNU Compiler Collectionの略です。LinuxのC言語コンパイラのデファクトスタンダードであり、数多くの他言語やプラットフォームサポートもします。

Q&A

解決済

3回答

540閲覧

構造体の使い方について

strike1217

総合スコア651

C

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

GCC

GCCはGNU Compiler Collectionの略です。LinuxのC言語コンパイラのデファクトスタンダードであり、数多くの他言語やプラットフォームサポートもします。

1グッド

0クリップ

投稿2017/07/22 08:02

編集2017/07/22 08:07

__builtinin_frame_address()を使用しました。

typedef struct layout{ void *n; void *ret; }layout; void func(){ layout *rbp = __builtin_frame_address(0); void *p = __builtin_frame_address(0); printf("%p, %p, %p\n", rbp->ret, rbp->n, p); } int main(){ void (*pp)() = func; pp(); printf("%p\n", pp); return 0; }

結果は、、、
0x555555554708, 0x7fffffffd5b0, 0x7fffffffd590
0x5555555546b0

となりました。

この関数はフレームポインタを返します。

*layout rbp = __builtin_frame_address(0);
普通ならこの行でWarningが出てきますが、ビルトイン関数の場合ではでてきません。
なぜです??

この場合、**rbp->n = __builtin_frame_address(0);**と同義でしょうか?

#include<stdio.h> typedef struct lay{ int *p; int *t; }layout; int main(){ int re = 10; layout *p = &re; printf("%p, %p, %p\n", &re, p->p, p->t); }

こちらはWarningが出現します。

0x7fffffffd594, 0xffffd5940000000a, 0x5555470000007fff
となります。
見てもわかりますが、値がずれています。

p->t には何も入れていませんがなにやらアドレスのようなものが出現しています。
なんですか?これは・・・

(__builtin_frame_address()の時もですね。)

どちらの場合も、ポインタのポインタとなっているのかな・・・?と思いますが
入れてもいないメンバ変数になんでアドレスが入っているのでしょうか?

アセンブリ言語で見ると・・・
mov %rsp %rbp をしたあとのスタックフレームを取得しているようです。
なので、
rbp->n = %rbp = %rsp という関係かと思います。
その前に、push %rbpがあるので、rbp->n はそのスタックのアドレスが入っているわけですよね。
その前は、call命令により関数のリターンアドレスが入っています。

linux 64bit Debian系 gccです。
どなたか教えてもらいないでしょうか??

ちなみに・・・
*p には rbp->nと同じ値が格納されていました。
???

rbp->nとpが同じ結果になっていない理由がわかりません。

mpyw👍を押しています

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

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

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

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

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

guest

回答3

0

ベストアンサー

C では void* は他のポインタ型と暗黙の型変換で自動的に変換されます。 たとえば malloc の返却値が void* であり、様々な形に使えることを思い出してください。 (C++ ではもう少し厳しい条件になっているようです。)

なので void* から layout * 型には勝手に変換されてコンパイル時にはエラーにはなりません。 ただし、そのメモリにアクセスした結果について保証があるかは別問題です。 __builtin_frame_address が返したメモリの構造、つまりスタックフレームがどうなっているかはコンパイラのドキュメントをよく読んでそれに一致するように操作するしかありません。 型システムによる保護は何も期待できないのです。

投稿2017/07/22 09:17

SaitoAtsushi

総合スコア5444

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

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

strike1217

2017/07/22 11:19

layout *rbp = [アドレス] という書き方を普通は使いますか??
strike1217

2017/07/22 11:23

layout *rbp = [アドレス]を明示的にキャストするとwarningが消えました。
yumetodo

2017/07/23 12:07

それお使いのコンパイラがC99対応していない・・・?(もしかして:MSVC)
strike1217

2017/07/23 12:10

あー。 すいません。環境書き忘れていました。 gcc です。64bitです。 MSVCではC99に対応していなんですか? gccは対応していますか?
yumetodo

2017/07/23 12:11

gccのバージョンにもよりますが、-std=c99ないし-std=c11をつけてコンパイルすると安心です。
yumetodo

2017/07/23 12:11

(ところで[アドレス]の型はvoid*型ですよね?)
strike1217

2017/07/23 12:14

1つ目はvoid*型ですね。 2つ目の方は見ての通り違います。
guest

0

よく見ると、書いてる事がメチャクチャだね。C言語、さっぱりわかって無かったんだね。

__builtinin_frame_address()を使用しました

逆に聞きたい。
どこから、こんな関数が出てきたのか?
なにゆえ、この関数を使いたいのか?
最初のプログラムで何を確かめようとしているのか?
__builtin_frame_address(0) のプロトタイプ宣言の在り処を見つけたのか?

後述の通り、通常のC言語の文法も意味も理解できていないくせに、これでは動作確認になりませんよ。

C

1void func() 2{ 3 layout *rbp = __builtin_frame_address(1); 4 void *p = __builtin_frame_address(); 5は、次のようにコンパイルされた。 6func: 7 pushq %rbp 8 movq %rsp, %rbp 9 subq $16, %rsp # ここまででスタックフレームが出来た 10 movq %rbp, -16(%rbp) # *rbp = __builtin_frame_address(0); 11 movq %rbp, -8(%rbp) # *p = __builtin_frame_address(0);

ここに示したアセンブリコードの最後の2行で、スタック(rbp変数とp変数)に代入している%rbpが、_builtin_frame_address(0)です。
__builtinin_frame_address()は、その名の通り、フレームポインタ(具体的には %rbp)の値を取得するもの(らしい)です。

layout *rbp = __builtin_frame_address(0);
普通ならこの行でWarningが出てきますが、

「普通」って、どういうのが普通なんでしょうね?

ビルトイン関数の場合ではでてきません。なぜです??

さあ?__builtinin_frame_address()が返す型が void * だからかな。

rbp->n = __builtin_frame_address(0);と同義でしょうか?

違います。
「普通」のC言語の文法やら何やら、理解していれば、違うことはわかるはずです。それがわからないくせに、って思います。

func() の中に取った変数は、どちらもポインタ変数です。わかってますか?
どこにも構造体変数を確保していません。強いて言えば、スタック領域を強制的にlayout構造体だとみなすわけですが・・・
2つのポインタをフレームポインタ %rbp で初期化すれば、スタック領域をポイントするから、セグメンテーションフォールト等は起きないけど、パズルのような事をして、君の理解レベルじゃ身の程知らずな事をやってるとしか・・・(苦笑。

p->t には何も入れていませんがなにやらアドレスのようなものが出現しています。 なんですか?

当たり前じゃないですか。
変数(メモリ)は常に何らかの値を持ちます(ゴミと呼ばれる場合もある)。それだけの事。こんなこともわかってないのか?って。未初期化変数を、そのまま使って思いもよらない結果になる…よくあるバグですよね。

こちらはWarningが出現します。

int re;
layout *p = ®

当たり前。変数 re は layout 型の変数じゃないのだから。

rbp->n = %rbp = %rsp という関係かと思います

ここも間違い。少なくとも %rbp == %rsp ではありません。
rbp 変数に %rbp を代入したんだから、rbp == %rbp です。

やっとわかりました

の中身も、なにやらアヤシイ話が連続します(呆)。少し頭を冷したら、いかが。

投稿2017/07/23 13:50

編集2017/07/23 13:58
rubato6809

総合スコア1380

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

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

strike1217

2017/07/23 14:10 編集

これは・・・自分の持っている本に書いてあったサンプルです。 すこしわかりにくいのでここに書いてあるのは自分が改良したものです。 C言語の文法に本来則っていないような記述の仕方をしているので困っていたんですが・・・ よく考えるとかなり無理やりな書き方です。 あとはsaitoさんの仰られている通りですね。 メモリレイアウトがちゃんと一致するように書かれています。
rubato6809

2017/07/23 14:13

> わかりにくいのでここに書いてあるのは自分が改良した 自分が何をしてるか、正しく理解せずに修正すれば、それは改良ではなく改悪。
guest

0

やっとわかりました。
正確なメモリレイアウトは面倒なので省略しますが、この構造体はメモリ領域として合計16Bのメモリ領域を必要としています。(2番目の構造体)

なので、実体のない構造体アクセスを行おうとすると(printf("%p, %p, %p\n", &re, p->p, p->t); )
実体が確保されているメモリ領域は int型+pointer型で12Bしかありませんので
メンバ変数のアクセスは先頭から8Bとして int型(4B)+ポインタ型(4B)にアクセスしてしまいます。
次の8Bは残りのポインタ型4Bとオーバーフローを起こします。

ある意味、共用体のようなものですね。(少しだけずれていますが・・・)

最初のint型reを long long intとして8B確保すると値はズレずに正確に16Bと一致します。

いや〜〜〜。とんでもなくわかりにくですね!!

__builtin_frame_address(0)の方はポインタ型に代入していますが、構造体としての実体がありません。
ポインタの前には何も確保されていないので、先頭からオフセットが一致しています。

構造体のポインタは実際の構造体とは別物でしたね。
実体のない構造体のメンバ変数からアクセスを行うとすでに確保されているメモリ領域へのアクセスが行われるようですね。

実体のないメンバ変数 p->p の中身はアドレスではなく要素でした。
(アドレスかと思っていました。わかりにくですね。)

投稿2017/07/23 11:37

編集2017/07/23 12:46
strike1217

総合スコア651

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

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問