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

解決済みの質問

構造体を型の異なる構造体に代入

C言語初心者です。
今回の質問は入力された構造体のメンバのデータを型の異なる構造体に代入したいのですが、毎回コンパイラにおこられてしまいます(汗)具体的には
typedef struct MSG{
longint type;
int flg;
int Dt[64];
}t_msg;
このDt[64]を以下の構造体に代入します。
typedef struct SC_MSG{
char a;
char b;
short c;
char d;
char e;
short f;
}t_sc_msg;

その際、異なる関数で処理するため、
main(){
foo(&t_msg);
};

void foo(t_msg *pt_msgdt){
t_sc_msg = (*pt_msgdt+8);

ココがエラーになってしまいます。
何か、根本的な間違いをおかしている気がします。
ご指導の方、宜しくお願いします。
ちなみにOSはLinuxでコンパイラーはgccです。

投稿日時 - 2007-12-24 12:03:50

QNo.3623426

すぐに回答ほしいです

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

>intの配列の中身はなんとゆったら良いかわかりませんが、0x00000001といったようなint型のリトルエンディアン型のデータです。

なるほど、バイナリデータを扱っているのですね。
実際のデータが分からないので、下記のように仮定します。

Dt[0] = 0x01234567;
Dt[1] = 0x89ABCDEF;

メモリ内のイメージは、リトルエンディアンなら、下記。

 67 45 23 01
 EF CD AB 89

構造体との対応を下記と仮定します。(Lは下位バイト、Hは上位バイト)

 a b cL cH
 d e fL fH

構造体に代入する処理は下記。

void foo(t_msg *pt_msgdt){
t_sc_msg sc_msg;
sc_msg.a = pt_msgdt->Dt[0] & 0xFF;
sc_msg.b = (pt_msgdt->Dt[0] >> 8) & 0xFF;
sc_msg.c = (pt_msgdt->Dt[0] >> 16) & 0xFFFF;
sc_msg.d = pt_msgdt->Dt[1] & 0xFF;
sc_msg.e = (pt_msgdt->Dt[1] >> 8) & 0xFF;
sc_msg.f = (pt_msgdt->Dt[1] >> 16) & 0xFFFF;
}

上記の様に、バイナリデータのフォーマットに従って、
一つ一つ地道に代入するのが確実だと思います。
(構造体から配列へ戻す関数も作る必要があるでしょう。)

===========================================================
別回答ですが、バイナリデータのフォーマットが、
実際の構造体の構造と一致するようでしたら、
型変換で解決するかもしれません。
あまり自信はないので、参考程度にして下さい。

パディングを無効化する必要があると思いますが、
gccには詳しくないので、下記の部分が gcc で通るかは不明です。

#pragma pack(1)
#pragma pack()

------------------------------------------------------------
#include <stdio.h>

typedef struct MSG{
long type;
int flg;
int Dt[64];
}t_msg;

//アライメントの制御
#pragma pack(1)
typedef struct SC_MSG{
char a;
char b;
short c;
char d;
char e;
short f;
}t_sc_msg;
#pragma pack()

void foo(t_msg *pt_msgdt){
t_sc_msg *sc_msg;
//型変換し、強引に代入(^^;
sc_msg = (t_sc_msg *)(pt_msgdt->Dt);

printf("%x \n",(int)sc_msg->a);
printf("%x \n",(int)sc_msg->b);
printf("%x \n",(int)sc_msg->c);
printf("%x \n",(int)sc_msg->d);
printf("%x \n",(int)sc_msg->e);
printf("%x \n",(int)sc_msg->f);

}

void main(){
t_msg msg;

msg.Dt[0] = 0x01234567;
msg.Dt[1] = 0x89ABCDEF;

foo(&msg);
}
------------------------------------------------------------
動作確認は下記のコンパイラで行いました。
Borland C++ 5.82 for Win32 Copyright (c) 1993, 2005 Borland

投稿日時 - 2007-12-24 18:30:01

お礼

回答ありがとうございます。
上記のシフトの方でこれから組んでみようと思います。
とても親切に教えていただきありがとうございます。

投稿日時 - 2007-12-24 23:37:28

ANo.9

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

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

回答(11)

ANo.11

★アドバイス
・以下のようなキャストをすればコピー可能と思います。
 ただし、リトルエンディアン、ビッグエンディアンをきちんと考えて下さい。

// 変数の宣言(これ重要)
t_msg g_msg = { 0 };
t_sc_msg g_sc_msg = { 0 };

// メイン関数
int main( void )
{
 foo( &g_msg );
 return 0;
}

// 代入用の関数
void foo( t_msg *pt_msgdt )
{
 g_sc_msg = *((t_sc_msg *)pt_msgdt->Dt);
}
以上。

投稿日時 - 2007-12-25 00:54:30

ANo.10

単純に代入したい(転送したい)というなら、memcpyで転送できます。
転送サイズは、受け側にあわせて、受け側のサイズ分転送します。
t_sc_msg = (*pt_msgdt+8); を
memcpy(&t_sc_msg,pt_msgdt->Dt,sizeof(t_sc_msg));
とします。
但し、業務的にこれで良いかどうかは、質問者さまのみしか知りえませんので、質問さまのほうで、判断してください。

投稿日時 - 2007-12-24 19:54:22

ANo.8

いっそのこと、代入用の関数作っちゃったら?
例えばこんなかんじで。

/**
 関数名 Msg2CsMsg
 概要  MSG型のデータをCS_MSG型のデータに代入する。
 引数  *src 入力元となるMSG型データへのポインタ
     *tgt 代入先となるCS_MSG型データの格納領域へのポインタ
 戻り値 0  代入成功
     -1  ポインタがNULL
*/
int Msg2CsMsg(MSG *src, CS_MSG *tgt)
{
 if( NULL == src ) return -1;
 if( NULL == tgt ) return -1;

 // あとはどうなって欲しいか、そのとーりにかく。
 // 変な小細工しないほうがデバッグしやすいでしょ。

 return 0; // 代入完了。
}

投稿日時 - 2007-12-24 18:01:24

お礼

回答ありがとうございます。
かれこれデバックもうまくいかずてんてこ舞いとなっています。

代入用の関数を作ってみたところ、やはりint型の配列にchar型の構造体は代入できないといったエラー文がきました。もう少し検討してみたいと思います。ありがとうございます。

投稿日時 - 2007-12-24 18:37:16

ANo.7

No6回答者です。
他の回答への補足を見ていると、
No6の回答は的外れっぽいですね。

int Dt[64]のデータを、t_sc_msgへ代入したいとの事ですが、
intの配列を、構造体へ代入する時のルールが不明です。

intの配列には、何が入っているのでしょうか?
出来れば、具体的なデータを示して下さい。

----------------------------------------------------------
型は無視して、単純に上から順に代入?(^^;

void foo(t_msg *pt_msgdt){
t_sc_msg sc_msg;
sc_msg.a = pt_msgdt->Dt[0];
sc_msg.b = pt_msgdt->Dt[1];
sc_msg.c = pt_msgdt->Dt[2];
sc_msg.d = pt_msgdt->Dt[3];
sc_msg.e = pt_msgdt->Dt[4];
sc_msg.f = pt_msgdt->Dt[5];
}

----------------------------------------------------------
それとも、t_sc_msgのバイナリーデータを、
intの配列として保存しているような、複雑な内容でしょうか?

投稿日時 - 2007-12-24 14:57:47

補足

回答ありがとうございます。
>intの配列には、何が入っているのでしょうか?
intの配列の中身はなんとゆったら良いかわかりませんが、0x00000001といったようなint型のリトルエンディアン型のデータです。
>intの配列として保存しているような、複雑な内容でしょうか?
何となく予想はついておられると思いますが、メッセージキューからレシーブする&T_MSGの構造体からDt[64]の中身を編集するといった内容です。
Dt[64]の中身を構造体に代入した方が編集しやすいと思ったのですが、うまく行きませんでした。宜しくお願い致します。

投稿日時 - 2007-12-24 15:44:11

ANo.6

共用体を使うべき問題ではないでしょうか?
複数の種類のデータを持つ部分を共用体として宣言します。

-------------------------------------------------
typedef struct SC_MSG{
char a;
char b;
short c;
char d;
char e;
short f;
}t_sc_msg;

//共用体を宣言
typedef union UNION_DT{
int Dt[64];
t_sc_msg sc_msg;
} t_union_dt;

typedef struct MSG{
long type;
int flg;
t_union_dt u; //共用体を入れる
}t_msg;

//int の配列として処理する関数
void hoge(t_msg *pt_msgdt){
int *pdt;
pdt = pt_msgdt->u.Dt;
}

//t_sc_msgとして処理する関数
void foo(t_msg *pt_msgdt){
t_sc_msg sc_msg;
sc_msg = pt_msgdt->u.sc_msg;
}

void main(){
t_msg msg1, msg2;
hoge(&msg1);
foo(&msg2);
}

----------------------------------------------
共用体、構造体はアライメントの問題があります。
共用体を使った型変換は期待しない結果に終わる可能があります。

共用体は、型変換の為のものではなく、
あくまで複数の型を共存させる為のものです。
その点はご注意下さい。

アライメントに関しては下記など参考に。
http://www.g-ishihara.com/c_st_01.htm

投稿日時 - 2007-12-24 14:03:48

ANo.5

>なんとかt_msg.Dt[64]だけ抜き出そうとしているのですが・・。

t_msg 型の変数 my_msg があったなら、my_msg.Dt[0], my_msg.Dt[1], ...
と順にアクセス可能です。

投稿日時 - 2007-12-24 13:50:56

ANo.4

・エラーの原因をつかめていない以上、まずは完全なソースコードを投稿することをお薦めします。
・エラーメッセージを読みましょう。エラーの原因がおおまかとはいえ表示されるわけですから、読まない理由はありません。
というのがまずは一般論です。自分もC言語は学び始めたばかりですから、頑張ってその心を読んでみます。
>foo(&t_msg);
t_msgは型なので引数に渡せません。
f_msg bar;
foo(&bar);
という感じで書きたかったのでしょうか?
>*pt_msgdt+8
pt_msgdt型の値に+演算子をつかうことはできません。
もしかして、Dt[64]にアクセスしようとして*(pt_msgdt+8) と書きたかったでしょうか?そうだとしても、*(pt_msgdt+8)はDt[64]を指しません。もしかしたら (*pt_msgdt).Dt と
書きたかったのかもしれません。
>t_sc_msg = (*pt_msgdt+8);
t_sc_msg baz = (*pt_msgdt).Dt;と書きたかったのかもしれません。これも左右の型が異なるのでそのまま代入はできません。そもそも、Dtは int Dt[64] でおそらくSC_MSGよりサイズが大きいでしょうから、代入できたとしても入りきらないと思います。

char + char + short + char + char + short → (たぶん)64ビットだ! → int Dt[64]だと64ビット(!?)だからぴったり!という発想でしょうか?そもそも、配列のデータで一発で構造体を初期化しようとするというのが無理な発想かと思います。構造体のメンバは必ずしも連続したメモリに配置されないそうですから。構造体のアライメントとかいうヤツです。いろんなところが間違っている気がします。

参考URL:http://www.hyuki.com/writing/techask.html

投稿日時 - 2007-12-24 13:29:34

お礼

早速の回答ありがとうございます。
>foo(&t_msg);
に関してはその通りです。端折りをして申し訳ございません。
>*pt_msgdt+8
ココは二番めの悩みでした。'->'を使用すると他のエラーが多発し、雰囲気的にuint32 とsint32 の次なので8byteずらせばDtの領域なのではないかと解釈しておりました。
>t_sc_msg = (*pt_msgdt+8);
ココが一番のなやみです。
単純にはt_msg.Dt[64]をt_sc_msg に先頭から代入したいということです。足りない分にはメンバを追加するという方法を取ります。
宜しくお願いします。

投稿日時 - 2007-12-24 14:14:22

ANo.3

t_msg形のtype,flg,Dtを、t_sc_msg形のa,b,c,d,e,fへ代入する方法がコンパイラも回答者もわかりません。

type,flg,Dtを、a,b,c,d,e,fへ代入する方法を細かく教えてください。

投稿日時 - 2007-12-24 13:07:22

補足

早速の回答ありがとうございます。
>t_msg形のtype,flg,Dtを、t_sc_msg形のa,b,c,d,e,fへ代入する方法がコンパイラも回答者もわかりません。
t_msgのDtのみを先頭からa,b,c,d,e,fにアドレスだけ渡して代入しようと考えてます。初歩的なことなのですがこれは不可能なのでしょうか?

投稿日時 - 2007-12-24 13:53:35

ANo.2

> Dt[64]を以下の構造体に代入します

どんな結果を得たいのでしょうか?
Dt[]と、代入先構造体のメンバーとの関係を教えてください。

投稿日時 - 2007-12-24 12:39:44

補足

早速の回答ありがとうございます。

>Dt[]と、代入先構造体のメンバーとの関係を教えてください。

他のメンバのtypeやflgを取り除いたDt[64]を先頭から順にt_sc_msgに代入したいと考えておりました。余った分はt_sc_msgのメンバを追加します。

宜しくお願いします。

投稿日時 - 2007-12-24 13:19:07

ANo.1

>foo(&t_msg);
t_msg は struct MSG の typedef なので、引数に渡せません。

> *pt_msgdt+8
*pt_msgdt の型は struct MSG です。これに 8 を加算することはできません。

>t_sc_msg = (*pt_msgdt+8);
同じく t_sc_msg も struct SC_MSG の typedef です。代入できません。

投稿日時 - 2007-12-24 12:22:18

お礼

早速の回答ありがとうございます。


>*pt_msgdt の型は struct MSG です。これに 8 を加算することはできません。
なんとかt_msg.Dt[64]だけ抜き出そうとしているのですが・・。
もう少し試行錯誤してみます。

投稿日時 - 2007-12-24 13:44:09