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

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

ただいまの
回答率

90.00%

構造体の使い方について

解決済

回答 3

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 943

strike1217

score 577

__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が同じ結果になっていない理由がわかりません。

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

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

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

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 3

checkベストアンサー

+2

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

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

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/07/23 21:11

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

    キャンセル

  • 2017/07/23 21:11

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

    キャンセル

  • 2017/07/23 21:14

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

    キャンセル

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 の中身はアドレスではなく要素でした。
(アドレスかと思っていました。わかりにくですね。)

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

-1

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

__builtinin_frame_address()を使用しました

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

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

void func()
{
    layout *rbp = __builtin_frame_address(1);
    void   *p   = __builtin_frame_address();
は、次のようにコンパイルされた。
func:
    pushq    %rbp
    movq    %rsp, %rbp
    subq    $16, %rsp    # ここまででスタックフレームが出来た
    movq    %rbp, -16(%rbp)    # *rbp = __builtin_frame_address(0);
    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;

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

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

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

やっとわかりました

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

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/07/23 23:09 編集

    これは・・・自分の持っている本に書いてあったサンプルです。
    すこしわかりにくいのでここに書いてあるのは自分が改良したものです。

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

    キャンセル

  • 2017/07/23 23:13

    > わかりにくいのでここに書いてあるのは自分が改良した

    自分が何をしてるか、正しく理解せずに修正すれば、それは改良ではなく改悪。

    キャンセル

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

  • ただいまの回答率 90.00%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる