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

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

ただいまの
回答率

88.91%

C言語でポインタを使わないで参照する構造体のメンバを切り替える方法

解決済

回答 3

投稿 編集

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

ParaParaDance

score 22

C言語でポインタを使い、構造体メンバを指定する方法はありますが、構造体のメンバの型や入出力型が同じではないと、読み書きが難しいです。

ポインタを使わないで構造体メンバを切り替える方法があればベストですが、やはりC言語では無理でしょうか。 良い方法があればアドバイスをお願いします。

動かないソースではありますが、イメージとしては以下のようです。

typedef struct{
uint32_t  a;
uint8_t  b;
uint16_t  c; 
}hoge_m;

uint32_t hoge_func(hoge_m *hoge_para, uint8_t select){

 uint32_t out;

 out = hoge_para->( select );  
//selectのところにポインタではなく、引数を当てて切り替える。

 return out;

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

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

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

    クリップを取り消します

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

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

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

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

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

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

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

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

    質問の評価を下げる

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

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

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

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

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

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

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

    詳細な説明はこちら

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

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

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

質問への追記・修正、ベストアンサー選択の依頼

  • radian

    2020/07/02 18:57

    構造体のメモリオフセットを自力で計算すれば出来なくはないでしょうが、多分普通に構造体参照するより遥かに面倒くさいです。
    C#ならば、リフレクション経由で構造体のメンバを列挙する事も可能なので、C#も検討してみてください。

    キャンセル

  • thkana

    2020/07/02 21:15

    既にでていますが、全部uint32_tにしてしまったらいけないのでしょうか?

    struct typedef{
    uint32_t a;
    uint32_t b;
    uint32_t c;
    }hoge_m;

    typedef union{
    unit32_t hoge_m[3];//あまり綺麗な気がしないけど、気持ち的にはこういうことをしたいのか、と
    hoge_m hoge;
    } hoge_u;

    とすると不都合がありますか?

    キャンセル

  • ParaParaDance

    2020/07/03 13:39

    問題はcoltex ARMってことです。
    メモリの制限もあるので、全て32bitにしたらその分無駄になります。
    色んなご意見有難うございました。

    キャンセル

回答 3

checkベストアンサー

+1

こんにちは。

C言語の場合は、hoge_para_uint32(), hoge_para_uint8(), hoge_para_uint16()的な関数を用意して注意深く呼び分けるのが一般的と思います。(暗黙の型変換に要注意)

C++は、それのような要求に対するサポートがあります。関数のオーバーロードと呼ばれています。
ただし、暗黙の型変換による落とし穴もあるので使い方は要注意です。特に整数型同士での呼び分けは意外に深い落とし穴が開いています


【追記】
「質問への追記・修正」依頼欄を見落としていました。
uint32_t型で全て返すことが目的なのですね。1, 2, 3的な番号(メンバ変数ID?)を指定して、該当のメンバの値を返したいと。
番号→メンバ変数変換が必要になりますので、switch-caseを使わざる得ないと思います。(番号が定数なら他にも考えられますが、定数でよいなら変数名でも良いはずなので意味はないです。)

そこでstructメンバの値を返すルーチンを共通化したいわけです。

その共通化ルーチン内でswitch-caseするしかないだろうと思います。

投稿

編集

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

+1

、メンバ数分、caseをつくって返す処理になるため、同じ動作をルーチンがたくさんできてしまいます。
そこでstructメンバの値を返すルーチンを共通化したいわけです。

マクロ展開するのが妥当かと思います。

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

+1

こんなのでお茶を濁すとか.

typedef struct {
    uint32_t  a;
    uint8_t  b;
    uint16_t  c; 
}hoge_m;

uint32_t Get_a( hoge_m *h ){    return h->a;    }
uint32_t Get_b( hoge_m *h ){    return h->b;    }
uint32_t Get_c( hoge_m *h ){    return h->c;    }

typedef uint32_t (*pMemGetFunc)( hoge_m * );

//
//※selectの値は {0,1,2} →メンバの {a,b,c} に対応
//
uint32_t hoge_func(hoge_m *hoge_para, uint8_t select)
{
    static const pMemGetFunc Funcs[3] = { Get_a, Get_b, Get_c };
    return Funcs[select]( hoge_para );
}

int main( void )
{
    hoge_m H;
    H.a = 4;
    H.b = 15;
    H.c = 77;

    for( int i=0; i<3; ++i )
    {   printf( "%u\n", hoge_func( &H, i ) );   }

    return 0;
}

別の.

uint32_t Get_Member_value_as_uint32( uint8_t *pStructHead, const size_t *SizeMap, uint8_t select )
{
    uint8_t *pos = pStructHead;
    for( uint8_t i=0; i<select; ++i ){  pos += SizeMap[i];  }

    switch( SizeMap[select] )
    {
    case 4: return *( (uint32_t*)pos );
    case 2: return *( (uint16_t*)pos );
    case 1: //fall through
    default:    return *pos;
    }
}

//-----

#pragma pack(push,1)  //※環境次第
typedef struct {
    uint32_t  a;
    uint8_t  b;
    uint16_t  c;
}hoge_m;
#pragma pack(pop)  //※環境次第

//↑のstructを実装した人間は,
//せめて,このような関数もセットで書くくらいの労力を費やしてもよいのではなイカ?
//(あるいはもう機械的に何かで出力すればよくね?)
uint32_t hoge_func(hoge_m *hoge_para, uint8_t select)
{
    static const size_t MemSize[] = { 4, 1, 2 };  //各メンバのサイズ
    return Get_Member_value_as_uint32( (uint8_t*)hoge_para, MemSize, select );
}

//-----

投稿

編集

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2020/07/03 15:13 編集

    メンバ数分switchを書きたくないという事なので、メンバ数分の関数ポインタを定義しなければならず、多分switchより手間が掛かってしまうのではないかと。C言語の範疇では、難しいでしょうねぇ。

    キャンセル

  • 2020/07/03 16:17

    オフセットとサイズの表くらいは作らないと無理な気がする.
    それを作るのは人手でなくても良いと思うし.
    (というか,switchのコードを吐くやつを用意すればよくね?感.)

    キャンセル

  • 2020/07/03 16:21

    構造体のオフセットとデータサイズを、ビルド時に構造体定義から自動的に計算してテキストファイルに出力する別のプログラム走らせて下の方法で読ませる、なら多少は省力化できそうですね

    キャンセル

  • 2020/07/03 16:34

    追加した側,メンバ配置をみっちり詰めてるんだから,メンバのサイズじゃなくてオフセットのテーブルにした方が良いですね.
    サイズはテーブル要素から引き算で求まるので.

    キャンセル

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

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

関連した質問

同じタグがついた質問を見る