2017-03-14 10 views
-1

ネットワーク経由でデータグラムを受信しましたが、適切なフィールド(メッセージのフォーマットに対応)を持つ構造体にデータをコピーしたいと思います。さまざまなタイプのデータグラム(フィールドとサイズが異なる)があります。ここでは単純化されたバージョンは、(実際にフィールドは常に文字の配列です)です。パディングを無視して構造体サイズを決定する

struct dg_a 
{ 
    char id[2]; 
    char time[4]; 
    char flags; 

    char end; 
}; 

struct dg_a data; 
memcpy(&data, buffer, offsetof(struct dg_a, end)); 

現在、私は私がどのように多くのバイトを決定するためにoffsetofを使用できるように、ダミーのフィールドは、構造体の最後にendと呼ばれる追加しますコピー。

これを行う方が、エラーが発生しにくい方法がありますか?私は__attribute__((packed))を入れてsizeofを使うよりもポータブルなものを探していました。

- コメントに

EDIT

何人かが私のアプローチが悪いと述べていたが、これまでのところ、誰もこれが理由を提示していません。構造体メンバはcharなので、トラップ表現はなく、メンバ間のパディングもありません(標準で保証されています)。

+0

コメントは議論の対象外です。この会話は[チャットに移動]されています(http://chat.stackoverflow.com/rooms/138031/discussion-on-question-by-martinkunev-determine-struct-size-ignoring-padding)。 – meagar

答えて

0

Cには、構造要素間の埋め込みや構造の終わりの回避のための標準的なメカニズムはありません。しかし、多くの実装ではこのような拡張機能が提供されています。構造レイアウトとネットワークメッセージペイロードを一致させたい場合は、そのような拡張機能に頼るしかありません。

__attribute__((packed))または類似のものを使用すると、あなたの目的にsizeofを使用できるようになりますが、これは単なるボーナスです。これを行う主なポイントは、提案されたメモリコピーの利点のために、構造レイアウトをネットワークメッセージ構造に一致させることです。プロトコルメッセージにnoneが含まれている内部パディングを使用して構造をレイアウトすると、提案するような直接メッセージメッセージのコピーは単純に機能しません。その場合はsizeofは、大きなサイズの問題の唯一の症状ですあなたに正しいサイズを与えていません。

また、生のバイトをコピーする際に他の問題が発生する可能性があることにも注意してください。特に、アーキテクチャーの異なるマシン間でメッセージを交換しようとする場合に、これらのメッセージに1バイトを超える整数が含まれている場合は、バイトオーダーの違いを考慮する必要があります。プロトコルがうまく設計されていれば、実際にはバイト順序が指定されます。同様に、文字データを渡している場合は、エンコードの問題に対処する必要があるかもしれません(独自のバイトオーダの考慮事項があります)。

全体として、メッセージ全体のペイロードを対応する構造に一度にコピーすることに基づいて、堅牢でポータブルなプロトコル実装を構築することはできません。少なくとも、メインコピーの後にメッセージタイプ固有のフィックスアップを実行する必要があります。代わりに、弾丸を噛んで、対応するネットワーク表現に出入りする各メッセージタイプに対して適切なマーシャル関数を書くことをお勧めします。あなたはもっと簡単にこれをポータブルにするでしょう。

1

中心的な問題は、サイズがbuffer(文字配列とみなされます)です。 2コピー、おそらく数バイトの違い。

memcpy(&data, buffer, offsetof(struct dg_a, end)); // 7 
// or 
memcpy(&data, buffer, sizeof data);     // 7, 8, 16 depends on alignment. 

これらの問題を回避することを検討し、同じ幅の任意のデータ構造としてbufferを使用して、ゼロが先行着信データが移入されることにパディング/充填。

struct dg_a { 
    char id[2]; 
    char time[4]; 
    char flags; 
}; // no end field 

union dg_all { 
struct dg_a a; 
struct dg_b b; 
... 
struct dg_z z; 
} buffer = { 0 }; 

foo(&buffer, sizeof buffer); // get data 

switch (bar(buffer)) { 
    case `a` { 
    struct dg_a data = buffer.a; // Ditch the memcpy 
    // or maybe no need for copy, just use `buffer.a` 
1

用語「言語」は、ソーステキストと行動との間のマッピングを参照している場合は、名前のCは、言語の2人の家族説明:

  1. に「Cの構文」をマッピングされた言語の家族を仕様よりも先例によって定義された方法での普遍的なマイクロコンピュータハードウェアの動作であるが、一般的なハードウェアを対象とした実装の間では、1980年代を通して、そして1990年代のほとんどにわたって本質的に100%一貫していた。

  2. 意図的に気まぐれな実装によって処理されたものを含む、C仕様を満たすすべての言語ファミリ。 C標準の著者はCプログラムによって提供される目的のすべて、考え方は、いくつかのフィールドのみのプログラムで浮上しているため、すべての実装が適していることを義務付ける実用的ではないことを認識していても

「ポータブル」とみなされるべきであるのは、スタンダードがすべての実装でサポートする必要があるものです。一般的なハードウェアのコンパイラが後半に全会一致でサポートしていたセマンティクスから大きく利益を得ても、故意に変態的な実装によって壊れる可能性のあるプログラム(その考え方を考えれば)は、 "移植不可能"または "間違った"スタンダードが素晴らしい代替品を定義していないということです。

ハイエンド番号の処理のような特定のフィールドをターゲットとするコンパイラは、コードが特定のハードウェア機能に依存しないことを前提としているため、また標準の作成者は、どのような目的に適していると見なすことができますが、コンパイラライターの中には、データを構造体にオーバーレイしようとするコードをサポートしたくないものがあります。このような構造は、すべてのデータを手動で解析しようとするコードよりも読みやすく、そのようなコードをサポートするように努力するコンパイラは、すべてのデータを手動で解析するコードよりも簡単かつ効率的に処理することができますコンパイラが構造レイアウトを割り当てることを愚かにすれば、コンパイラライターは、構造上にデータをオーバーレイしようとするコードに欠陥があると考えるように心がけています。

関連する問題