• ベストアンサー

構造体へのポインタ変数

9バイトのデータがあって、先頭の1バイトの値によって、残り8バイトが2バイトデータ4つの場合と、4バイトデータ2つの場合があります。 つまり、 struct typeA { char a; int s,t,u,v;}; struct typeB { char a; long x,y}; char *p; pがそのデータの先頭を指している場合、 typeAならば、p->s とアクセスし、 typeBならば、p->x とアクセスしたいのですが、エラーになってしまいます。 思いついた対処法は、  struct typeA pa;  struct typeB pb; を定義しておいて、  pa = p; pa->s  pb = p; pb->x としてアクセスする方法ですが、新たに変数に代入するのが無駄(実際はコンパイラの最適化で問題ないとは思いますが)なので、もっと直接的に p->s とアクセスする方法ありませんか?

質問者が選んだベストアンサー

  • ベストアンサー
  • titokani
  • ベストアンサー率19% (341/1726)
回答No.9

>9バイトデータはgivenです。通信回線からの受信データを想定しています。バッファから1パケット分のデータを受け取り、それを加工するプログラムです。char *p は、これから処理すべきデータの先頭(パケットの先頭ではありません)を指しています。 それでしたら、そもそも構造体は使えません。 アライメントの問題もそうですが、エンディアンの問題もあります。 char[9]で受け取って、1バイトづつ並び替える関数を作るのが現実的ですね。

usatan2
質問者

お礼

回答ありがとうございます。 今回、通信相手も同じCPUですので、エンディアンの問題はありませんが、アドバイス感謝します。 >それでしたら、そもそも構造体は使えません。 Cでの記述が無理なら、あからさまに、該当CPU限定の記述になりますが、インラインアセンブラで記述することにします(苦笑)

その他の回答 (9)

  • titokani
  • ベストアンサー率19% (341/1726)
回答No.10

#9です。 Cでは無理だなんて書いてません。 構造体が使えないというだけです。 例えば、 inline int get_type(const char *p) { return p[0]; } inline int get_s(const char *p) { return p[1]|(p[2]<<8); } inline int get_t(const char *p) { return p[3]|(p[4]<<8); } inline int get_x(const char *p) { return p[1]|(p[2]<<8)|(p[3]<<16)|(p[4]<<24); } といった感じですね。

usatan2
質問者

お礼

再度、回答ありがとうございます。 >Cでは無理だなんて書いてません。 >構造体が使えないというだけです。 そうでしたね(笑)、失礼しました。 今回はCで書くより、インラインアセンブラのほうが素直かと思っただけです。

  • Tacosan
  • ベストアンサー率23% (3656/15482)
回答No.8

C99 いわく「正しくアラインされていないポインタを使ったらどうなってもしらないよ」だそうです. つまり, short が 2バイトだと仮定すると short *p = 0xdeadbeef; short x = *p; は未定義動作になるので「何が起きてもおかしくない」ということです. 変なデータが得られるかもしれないし, (時間がかかるだけで) 想定したデータが得られるかもしれないし, 異常終了しちゃうかもしれない. ということで, 「どんなシステムでも絶対確実に動かす」ということだと struct typeA { char a; union { struct { int s, t, u, v; }; struct { long x, y; }; }; }; として, 得られたデータをこの構造体に入れる (アンパックする) くらいしかないんじゃないかなぁ? もちろんこのようにすると処理時間やメモリが無駄になる (可能性がある) ので, 使うシステムが限定されていて「変更はありえない」ということであればさっさとキャストしてしまうという方針もあります. あとで何かあっても知らないけど.

usatan2
質問者

お礼

回答ありがとうございます。 今回、想定しているCPUは固定(MB91FV310A)ですので、「どんなシステムでも絶対確実に動かす」必要はありませんが、アドバイス感謝します。

  • noocyte
  • ベストアンサー率58% (171/291)
回答No.7

> たとえばshort(2バイト)のデータが2の倍数のアドレスでないと > 「効率が悪い」のではなく、「動作しない」処理系があるということでしょうか? あります.(言語処理系ではなく,CPU の話ですが.) CPU を特定しないのなら,それが普通と考えるべきです. http://www5d.biglobe.ne.jp/~noocyte/Programming/Alignment.html

usatan2
質問者

お礼

回答ありがとうございます。 想定しているCPUは固定なので、そのCPUで1度試してみます。

  • tadys
  • ベストアンサー率40% (856/2135)
回答No.6

処理系によってはshort(2バイト)データのアドレスが2の倍数、long(4バイト)データのアドレスが4の倍数でないと動作しないものがあります。 その場合、9バイトデータと、1バイト+2バイト*4と、1バイト+4バイト*2ではデータ構造が異なるのでunionで共存させる事はできません。 共存させる事が可能な処理系が有るかもしれませんが互換性の欠けたものになります。 例えば、 struct typeA { char a; short s,t,u,v;}; struct typeB { char a; long x,y}; とすると sizeof(typeA) は10、sizeof(typeB) は12となり9バイトデータとは適合しません。 sのアドレスは a+1 では無く、a+2になります。 xのアドレスは a+1 では無く、a+4になります。 コンパイラのオプションの設定によっては9バイトデータに適合させられるかもしれませんが処理速度が低下する恐れがあるのでお勧めできません。 現実的な解決策はstruct typeB { char a; long x,y};のデータに統一する事です。 struct typeAB{  char a ; /* long にしてもデータのサイズは変わらない */  union{   short s[4] ;   long l[2] ;  } ; } ; /* sizeof(typeAB) は12になります */ 9バイトにこだわるのであればバイト毎に取り出して別の変数に代入する方法になります。

usatan2
質問者

お礼

失礼ながら、皆様のお礼、まとめて書かせていただきます。 皆様、分かりやすい回答ありがとうございます。 アラインメントの問題がからんでくるため pa = p; pa->s でアクセスしてもだめな場合があるということ良く分かりました。 >現実的な解決策はstruct typeB { char a; long x,y};のデータに統一する事です。 以下の事情で教示いただいた解決策は使えません(悲)。 9バイトデータはgivenです。通信回線からの受信データを想定しています。バッファから1パケット分のデータを受け取り、それを加工するプログラムです。char *p は、これから処理すべきデータの先頭(パケットの先頭ではありません)を指しています。 ここで新たな疑問が発生しました。たとえばshort(2バイト)のデータが2の倍数のアドレスでないと「効率が悪い」のではなく、「動作しない」処理系があるということでしょうか? 引き続きよろしくお願いいたします。

  • Oh-Orange
  • ベストアンサー率63% (854/1345)
回答No.5

★補足。 ・C言語だとタグ名を省略できないかも。  C++なら省略可能。

  • Oh-Orange
  • ベストアンサー率63% (854/1345)
回答No.4

★アドバイス ・2バイトのデータ型をshort  4バイトのデータ型をlong  として回答します。 サンプル1: short *pA; long *pB; char *p; p = (char *)data; ←9バイトのデータをセット if ( *p == 2 ){ ←2バイト×4個の場合  pA = (short *)(p + 1); } else if ( *p == 4 ){ ←4バイト×2個の場合  pB = (long *)(p + 1); } ・pA[0]~pA[3]でアクセス  pB[0]、pB[1]でアクセス サンプル2:共用体を利用 typedef struct tagDATA {  char type;  union {   short word[4];   long dword[2];  }; } data_t; data_t *p = (data_t *)data; ←9バイトのデータをセット if ( p->type == 2 ){ ←2バイト×4個の場合  p->word[0]    :  p->word[3]をアクセス } else if ( p->type == 4 ){ ←4バイト×2個の場合  p->dword[0]    :  p->dword[1]をアクセス } ※回答者No.1さんはタグ名に ta、tb を使っていますが  共用体のタグ名を省略できます。  付けても問題ありません。

  • koko_u_
  • ベストアンサー率18% (459/2509)
回答No.3

int が 2バイト で long が 4バイトとは限らないことにも注意した方がよいでしょう。

  • asuncion
  • ベストアンサー率33% (2127/6289)
回答No.2

余計なお世話でしたら聞き流してください。 int型の大きさは、いまどきのコンパイラでしたら たいていは4バイト以上だと思います。 2バイトの整数型を扱うのでしたら、shortと書かれる方が よいのではないかと思います。 また、アラインメントの問題がからんでくるために、 当該の2つの構造体の大きさが異なることが考えられると思います。 大きさをそろえるためにダミーの領域を用意する必要が 出てくるかもしれません。

  • notnot
  • ベストアンサー率47% (4900/10358)
回答No.1

unionを使います。 インデントを残すために行頭に : を書いています。 :typedef struct { : char a; : union { struct {int s,t,u,v;} ta; : struct {long x,y;} tb; : }; :} typeAB; :typeAB *p; :p=(*typeAB)malloc(sizeof(typeAB)); :if(p->a=='?') { : sub1(p->ta.s); :}else{ : sub2(p->tb.x); :}

関連するQ&A