みんなの「教えて(疑問・質問)」にみんなで「答える」Q&Aコミュニティ

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

締切り済みの質問

配列から構造体へデータコピー

配列から構造体へデータのコピーをしたいのですが、
構造体のメンバがビットフィールドで構成されている時の処理がわかりません。
--------test.c-----------
#include <stdio.h>
#include <string.h>

typedef struct{
unsigned char aaa :1;
unsigned char bbb :1;
unsigned char ccc :1;
unsigned int ddd :13;
unsigned char eee :2;
unsigned char fff :2;
unsigned char ggg :4;
}test_t;

int main(void)
{
test_t test_t;
unsigned char data[]={0x5F, 0xFE, 0x1C};

memcpy(&test_t, data, 4);

printf("aaa = %X\n", test_t.aaa);
printf("bbb = %X\n", test_t.bbb);
printf("ccc = %X\n", test_t.ccc);
printf("ddd = %d\n", test_t.ddd);
printf("eee = %X\n", test_t.eee);
printf("fff = %X\n", test_t.fff);
printf("ggg = %X\n", test_t.ggg);

return 0;
}
------期待出力---------
aaa = 0
bbb = 1
ccc = 0
ddd = 1FFE
eee = 0
fff = 1
ggg = 12
「test.c」を実行した時に「期待出力」のような出力を期待していたのですが、実際には
aaa = 1
bbb = 1
ccc = 1
ddd = 1
eee = 0
fff = 0
ggg = 0
と表示されてしまいます。
ビットフィールドで構成された構造体に、配列の値をそのままあてる事は出来ないのでしょうか?
出来るだけ、マスクやシフト演算を使用しないで、配列からビット単位で値を抽出したいのですが・・・

投稿日時 - 2009-03-17 00:00:48

QNo.4803001

すぐに回答ほしいです

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

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

回答(4)

ANo.4

「data は 3バイトしかないのに memcpy で 4バイト目にアクセスしてる」って問題もあるんだけど....
そもそもビットフィールドを使った時点で「処理系依存」の嵐です:
・_Bool と (signed/unsigned) int 以外の型が使えるかどうかは処理系定義
・ビットをどの順に割り当てるかは処理系定義
・当該ビットフィールドをすべて入れることができれば, どのような記憶単位を割り当ててもいい (そのアラインメントは指定しない)
・ビットフィールドを順に詰めていって余裕がなくなったときに「とりあえず入るところだけ入れて後は別の記憶単位にする」か「詰めるのをあきらめて新しい記憶単位にする」かは処理系定義
つまり, sizeof(test_t) は 3~12 まで考えられます. ま, 「11」とかになることはないけど.
さらに, エンディアンの問題も発生するのでこのような方法は十分に処理系を理解し, かつ「その処理系と心中する」意気込みがなければ避けるべきでしょう.

投稿日時 - 2009-03-17 13:59:08

ANo.3

>出来るだけ、マスクやシフト演算を使用しないで、配列からビット単位で値を抽出したいのですが・・・
ビットフィールドを使ったとしても、結局のところコンパイラが代わりにマスクやシフト演算をするコードを出力するだけなので、できあがるプログラムはそんなに変わらないですよ。
ビットの並びとかを思うように制御したいのなら、ビットフィールドは使わずにマスクやシフトを使うほうが確実です。マクロやインライン関数などをうまく使えば、実行効率もほとんど問題ないはずです。

投稿日時 - 2009-03-17 09:41:17

ANo.2

処理系依存ですのでマクロを組むか事前に条件を確認する必要があります。

1)パディングサイズ
2)ビィットフィールドの並び(LMBから並ぶのかHMBから並ぶのか)
aaaがバイトフィールトの1ビット目に取られるのか8ビット目にとられるのか。
ちなみにMaicroSoftのCコンパイラでx86プラットフォームの場合は0ビット目に取られます。
質問の期待値を得るには::パディングサイズ1でx86系の場合::
unsigned char data[]={0x03,0xFE,0x1F,0xC4};になると思います。
※MS-Cでは13ビットにするとintじゃなくてshort intになるみたいです。

ざっくり、こんなコード書けば簡単に確認できます。
パディングサイズはコンパイルオプションでも変わるので決めておかないと困ったことになります。

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

typedef struct{
unsigned char aaa :1;
unsigned char bbb :1;
unsigned char ccc :1;
unsigned int ddd :13;
unsigned char eee :2;
unsigned char fff :2;
unsigned char ggg :4;
}test_t;

int main( )
{
int n,l ;
unsigned char *data ;
test_t *test ;

n = sizeof(test_t) ;
printf("sizeof(test_t) = %d\n",n) ;

data = malloc(n) ;
memset(data,0,n) ;
test = (test_t *)data ;

test->aaa = 1 ;
test->eee = 3 ;

for (l=0;l<n;l++) {
printf("%02d: %X\n",l,data[l]) ;
}
}

投稿日時 - 2009-03-17 04:41:59

ANo.1

ビットフィールドでデーターを詰めたい場合、型をむやみに変えてはいけません。
(通常の構造体でも同じようなことは言えますがパディングが起こります)

あと、ビッグエンディアンの配置を期待されているようですが
実行結果でみると、リトルエンディアンのようです。
配置の取り方を変えましょう。

以下サンプル(期待出力が意味不明だったので %d の位置を変更しました)

#include <stdio.h>
#include <string.h>

typedef struct
{
unsigned aaa:1;
unsigned bbb:1;
unsigned ccc:1;
unsigned ddd:13;
unsigned eee:2;
unsigned fff:2;
unsigned ggg:4;
} test_t;

int
main (void)
{
test_t test_t;
unsigned data[] = {0x00c4fff2};

memcpy (&test_t, data, sizeof data);

printf ("aaa = %X\n", test_t.aaa);
printf ("bbb = %X\n", test_t.bbb);
printf ("ccc = %X\n", test_t.ccc);
printf ("ddd = %X\n", test_t.ddd);
printf ("eee = %X\n", test_t.eee);
printf ("fff = %X\n", test_t.fff);
printf ("ggg = %d\n", test_t.ggg);

return 0;
}

投稿日時 - 2009-03-17 02:44:54