3

リンクで指定されているように、長さゼロの配列で構造体を初期化できます。

長さゼロ

私は次の構造を使用しています:

typedef unsigned char UINT8;
typedef unsigned short UINT16;

typedef struct _CommandHeader
{
    UINT16 len;
    UINT8 payload[0];
} CommandHeader;

typedef struct _CmdXHeader
{
    UINT8 len;
    UINT8 payload[0];
} CmdXhHeader;

CommandHeader.payload は CmdXHeader 構造体を指す/含む必要があります。つまり、メモリは次のようになります。

 -------------------------------------------------------------
| CommandHeader.len | CmdXHeader.len | CmdXHeader.payload ....|
 -------------------------------------------------------------

CmdXHeader / CommandHeader をカスタマイズした長さに簡単に malloc できます。しかし、CmdXHeader ペイロードに値を割り当てる方法、または CmdXHeader オブジェクトを CommandHeader.payload にリンクする方法は?


私の解決策

返信ありがとうございます。次の方法で解決しました。

//Get the buffer for CmdXHeader:
size_t cmdXHeader_len = sizeof(CmdXHeader) + custom_len;
CmdXHeader* cmdXHeader = (CmdXHeader*) malloc(cmdXHeader_len);
//Get a temporary pointer and assign the data to it
UINT8* p;
p[0] = 1;
p[2] = 2;
.......

//Now copy the memory of p to cmdXHeader
memcopy(cmdHeader->payload, p, custom_len);

// allocate the buffer for CommandHeader
CommandHeader* commandHeader = (CommandHeader*) malloc (sizeof (CommandHeader) + cmdXHeader_len);

// populate the fields in commandHeader
commandHeader->len = custom_len;
memcpy(commandHeader->payload, cmdXHeader, cmdXHeader_len);

これで、commandHeader オブジェクトに必要なメモリが確保され、任意の方法で型キャストできるようになりました...

4

4 に答える 4

9

構造体の最後、またはその他の場所にある長さゼロの配列は、標準Cでは実際には違法です(より正確には制約違反です)。これはgcc固有の拡張です。

これは、「構造体ハック」のいくつかの形式の1つです。これを行うためのもう少し移植性の高い方法は、0ではなく長さ1の配列を定義することです。

C言語の作成者であるDennisRitchieは、これを「Cの実装での不当な混乱」と呼んでいます。

ISO C標準の1999年の改訂では、「柔軟な配列メンバー」と呼ばれる機能が導入されました。これは、これを行うためのより堅牢な方法です。最近のほとんどのCコンパイラはこの機能をサポートしています(ただし、Microsoftのコンパイラはサポートしていないと思います)。

これについては、 comp.lang.cFAQの質問2.6で詳しく説明されています。

アクセス方法については、どのフォームを使用する場合でも、他の配列と同じように扱うことができます。ほとんどのコンテキストでは、メンバーの名前がポインターに減衰するため、メンバーにインデックスを付けることができます。十分なメモリを割り当てている限り、次のようなことができます。

CommandHeader *ch;
ch = malloc(computed_size);
if (ch == NULL) { /* allocation failed, bail out */ }
ch.len = 42;
ch.payload[0] = 10;
ch.payload[1] = 20;
/* ... */

明らかに、これは大まかな概要にすぎません。

sizeofタイプまたはそのタイプのオブジェクトに適用するとCommandHeader、柔軟な配列メンバーを含まない結果が得られることに注意してください。

アンダースコアで始まる識別子は実装用に予約されていることにも注意してください。このような識別子を独自のコードで定義しないでください。typedef名とstructタグに個別の識別子を使用する必要はありません。

typedef struct CommandHeader
{
    UINT16 len;
    UINT8 payload[0];
} CommandHeader;

また、で定義されている標準の型uint16_tとを使用することをお勧めします(コンパイラがそれをサポートしていると仮定します。C99でも新しいです)。uint8_t<stdint.h>

(実際には、アンダースコアで始まる識別子の規則は少し複雑です。標準の最新ドラフトであるセクション7.1.3のN1570を引用します。

  • アンダースコアと大文字または別のアンダースコアで始まるすべての識別子は、常に使用できるように予約されています。
  • アンダースコアで始まるすべての識別子は、通常の名前空間とタグの名前空間の両方でファイルスコープを持つ識別子として使用するために常に予約されています。

また、予約済み識別子にはさらにいくつかのクラスがあります。

ただし、ファイルスコープで安全に使用できる識別子と、他のスコープで安全に使用できる識別子を特定するよりも、アンダースコアで始まる識別子を定義しないほうがはるかに簡単です。)

于 2013-01-30T18:52:48.423 に答える
1

メモリにいくつかのバイトがあり、ペイロードへのポインタを見つけたいと思いますか?

typedef struct _CmdXHeader
{
    UINT8 len;
    UINT8* payload;
} CmdXhHeader;

typedef struct _CommandHeader
{
    UINT16 len;
    CmdXhHeader xhead;
} CommandHeader;

次に、CommandHeaderへのポインタにメモリをキャストできます

uint8_t* my_binary_data = { /* assume you've got some data */ };

CommandHeader* cmdheader = (CommandHeader*) my_binary_data;

// access the data
cmdheader->xhead.payload[0];

重要!構造体をパックしない限り、おそらく単語の境界に整列し、移植性がありません。構造体をパックする方法の具体的な構文については、コンパイラのドキュメントを参照してください。

また、バイトを消費している場合(つまり、ファイルまたはワイヤーから読み取った場合)にのみ、表示したことを実行します。あなたがデータの作成者であるならば、私はあなたが示したものに対して心からお勧めします。

于 2013-01-31T03:16:53.203 に答える
0
   struct _CommandHeader *commandHeader = malloc(sizeof(struct _CommandHeader)+
                                  sizeof(struct _CmdXHeader));
于 2013-01-30T19:09:51.747 に答える
0

C99 では、ペイロード[0] の代わりにペイロード[] を使用することをお勧めします。C99 コンパイラの中には、長さゼロの配列の使用を推奨しないものがあります。したがって、ここでエラーが発生した場合:

typedef struct CommandHeader
{
    UINT16 len;
    UINT8 payload[0];
} CommandHeader;

次のようにいつでも修正できます。

typedef struct CommandHeader
{
    UINT16 len;
    UINT8 payload[];
} CommandHeader;
于 2014-05-02T04:12:00.677 に答える