こんにちはゲストさん。会員登録(無料)して質問・回答してみよう!

解決済みの質問

構造体へのポインタ変数

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 とアクセスする方法ありませんか?

投稿日時 - 2008-04-08 22:03:23

QNo.3933334

困ってます

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

>9バイトデータはgivenです。通信回線からの受信データを想定しています。バッファから1パケット分のデータを受け取り、それを加工するプログラムです。char *p は、これから処理すべきデータの先頭(パケットの先頭ではありません)を指しています。

それでしたら、そもそも構造体は使えません。
アライメントの問題もそうですが、エンディアンの問題もあります。
char[9]で受け取って、1バイトづつ並び替える関数を作るのが現実的ですね。

投稿日時 - 2008-04-09 16:24:12

お礼

回答ありがとうございます。
今回、通信相手も同じCPUですので、エンディアンの問題はありませんが、アドバイス感謝します。

>それでしたら、そもそも構造体は使えません。
Cでの記述が無理なら、あからさまに、該当CPU限定の記述になりますが、インラインアセンブラで記述することにします(苦笑)

投稿日時 - 2008-04-10 23:13:28

このQ&Aは役に立ちましたか?

22人が「このQ&Aが役に立った」と投票しています

回答(10)

ANo.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);
}
といった感じですね。

投稿日時 - 2008-04-11 09:35:52

お礼

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

投稿日時 - 2008-04-11 19:00:44

ANo.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;
};
};
};
として, 得られたデータをこの構造体に入れる (アンパックする) くらいしかないんじゃないかなぁ?
もちろんこのようにすると処理時間やメモリが無駄になる (可能性がある) ので, 使うシステムが限定されていて「変更はありえない」ということであればさっさとキャストしてしまうという方針もあります. あとで何かあっても知らないけど.

投稿日時 - 2008-04-09 13:59:37

お礼

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

投稿日時 - 2008-04-10 23:06:46

ANo.7

> たとえばshort(2バイト)のデータが2の倍数のアドレスでないと
> 「効率が悪い」のではなく、「動作しない」処理系があるということでしょうか?

あります.(言語処理系ではなく,CPU の話ですが.)
CPU を特定しないのなら,それが普通と考えるべきです.
http://www5d.biglobe.ne.jp/~noocyte/Programming/Alignment.html

投稿日時 - 2008-04-09 12:38:11

お礼

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

投稿日時 - 2008-04-10 23:02:51

ANo.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バイトにこだわるのであればバイト毎に取り出して別の変数に代入する方法になります。

投稿日時 - 2008-04-09 09:58:35

お礼

失礼ながら、皆様のお礼、まとめて書かせていただきます。
皆様、分かりやすい回答ありがとうございます。

アラインメントの問題がからんでくるため
pa = p; pa->s
でアクセスしてもだめな場合があるということ良く分かりました。

>現実的な解決策はstruct typeB { char a; long x,y};のデータに統一する事です。
以下の事情で教示いただいた解決策は使えません(悲)。
9バイトデータはgivenです。通信回線からの受信データを想定しています。バッファから1パケット分のデータを受け取り、それを加工するプログラムです。char *p は、これから処理すべきデータの先頭(パケットの先頭ではありません)を指しています。

ここで新たな疑問が発生しました。たとえばshort(2バイト)のデータが2の倍数のアドレスでないと「効率が悪い」のではなく、「動作しない」処理系があるということでしょうか?
引き続きよろしくお願いいたします。

投稿日時 - 2008-04-09 10:50:39

ANo.5

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

投稿日時 - 2008-04-09 00:15:01

ANo.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 を使っていますが
 共用体のタグ名を省略できます。
 付けても問題ありません。

投稿日時 - 2008-04-09 00:07:27

ANo.3

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

投稿日時 - 2008-04-08 23:06:28

ANo.2

余計なお世話でしたら聞き流してください。

int型の大きさは、いまどきのコンパイラでしたら
たいていは4バイト以上だと思います。
2バイトの整数型を扱うのでしたら、shortと書かれる方が
よいのではないかと思います。

また、アラインメントの問題がからんでくるために、
当該の2つの構造体の大きさが異なることが考えられると思います。
大きさをそろえるためにダミーの領域を用意する必要が
出てくるかもしれません。

投稿日時 - 2008-04-08 23:05:44

ANo.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);
:}

投稿日時 - 2008-04-08 22:27:13