1

私は次の構造体を持っています:

typedef union
{
    struct
    {
        unsigned char       ID;
        unsigned short      Vdd;

        unsigned char       B1State;
        unsigned short      B1FloatV;
        unsigned short      B1ChargeV;
        unsigned short      B1Current;
        unsigned short      B1TempC;
        unsigned short      B1StateTimer;
        unsigned short      B1DutyMod;

        unsigned char       B2State;
        unsigned short      B2FloatV;
        unsigned short      B2ChargeV;
        unsigned short      B2Current;
        unsigned short      B2TempC;
        unsigned short      B2StateTimer;
        unsigned short      B2DutyMod;

    } bat_values;
    unsigned char buf[64];
} BATTERY_CHARGE_STATUS;

そして私は次のように配列からそれを詰め込んでいます:

for(unsigned char ii = 0; ii < 64; ii++) usb_debug_data.buf[ii]=inBuffer[ii];

配列には次の(任意の)値があることがわかります。

inBuffer[0] = 80;
inBuffer[1] = 128;
inBuffer[2] = 12;
inBuffer[3] = 0;
inBuffer[4] = 23;
...

ここで、QEditLineのテキストを変更してこれらの値を表示したいと思います。

str=QString::number((int)usb_debug_data.bat_values.ID);
ui->batID->setText(str);
str=QString::number((int)usb_debug_data.bat_values.Vdd)
ui->Vdd->setText(str);
str=QString::number((int)usb_debug_data.bat_values.B1State)
ui->B1State->setText(str);
...

ただし、QEditLineのテキスト値が期待どおりに表示されません。私は次のように見えます:

usb_debug_data.bat_values.ID = 80 (correct)
usb_debug_data.bat_values.Vdd = 12 (incorrect)
usb_debug_data.bat_values.B1State = 23 (incorrect)

'usb_debug_data.bat_values.Vdd'は短いようですが、inBuffer[1]とinBuffer[2]から値を取得していません。同様に、'usb_debug_data.bat_values.B1State'はinBuffer[3]から値を取得する必要がありますが、何らかの理由でinBuffer[4]から値を取得しています。

なぜこれが起こっているのか考えていますか?

4

2 に答える 2

3

C および C++ では、構造体の要素間および最後の要素の後に、任意の目的でパディングを自由に挿入できます (通常は効率性ですが、基盤となるアーキテクチャが非境界整列アクセスをまったく許可しないためです)。

したがって、おそらく 2 バイト長の項目が 2 バイト境界に整列されていることに気付くでしょう。そのため、最終的には次のようになります。

unsigned char       ID;          // 1 byte
//                                  1 byte filler, aligns following short
unsigned short      Vdd;         // 2 bytes
unsigned char       B1State;     // 1 byte
//                                  3 bytes filler, aligns following int
unsigned int        myVar;       // 4 bytes

多くのコンパイラでは、次のように構造体をパックする方法を具体的に指定できます。

#pragma pack(1)

またはgcc

__attribute__((packed))

属性。

構造体をパックしたくない (またはパックできない) 場合は、フィールドごとのコピーに戻すことができます (おそらく関数で最適です)。

void copyData (BATTERY_CHARGE_STATUS *bsc, unsigned char *debugData) {

    memcpy (&(bsc->ID), debugData, sizeof (bsc->ID));
    debugData += sizeof (bsc->ID);

    memcpy (&(bsc->Vdd), debugData, sizeof (bsc->Vdd));
    debugData += sizeof (bsc->Vdd);

    : : :

    memcpy (&(bsc->B2DutyMod), debugData, sizeof (bsc->B2DutyMod));
    debugData += sizeof (bsc->B2DutyMod); // Not really needed

}

構造と機能の同期を維持しなければならないのは苦痛ですが、それほど変わらないことを願っています.

于 2013-01-15T00:54:17.013 に答える
2

構造体はデフォルトではパックされないため、コンパイラはメンバー間にパディングを自由に挿入できます。最も一般的な理由は、マシンに依存するアライメントを確保することです。データ構造のアライメントに関するウィキペディアのエントリは、開始するのに非常に適した場所です。基本的に次の 2 つの選択肢があります。

  1. アラインメントを強制するコンパイラ固有のプラグマを挿入します (例:#pragma packedまたは__attribute__((packed))__.
  2. 明示的なシリアライゼーションおよびデシリアライゼーション関数を記述して、構造体をバイト配列に変換したり、バイト配列から変換したりします

私は通常、後者を好みます。なぜなら、コンパイラ固有の装飾がどこにでもあり、コードが見苦しくならないからです。

次に発見する可能性が高いのは、マルチバイト整数のバイト順もプラットフォーム固有であるということです。 詳細については、エンディアンを調べてください

于 2013-01-15T00:56:27.793 に答える