6

私たちのチームは現在、GCC 4.5.1のカスタマイズされたバージョンを使用して、古いアーキテクチャからARMCortexM3プラットフォームに基づく新しい製品に移植されたコードを使用しています。通信リンクからデータを読み取り、生のバイト配列を構造体にキャストしてデータをクリーンに解析しようとしています。ポインターを構造体にキャストして逆参照した後、「型のパンニングされたポインターを逆参照すると、厳密なエイリアシング規則に違反します」という警告が表示されます。

いくつかの調査の結果、char配列には整列規則がなく、構造体は単語整列する必要があるため、ポインターをキャストすると未定義の動作(悪いこと)が発生することに気付きました。私たちが試みていることを行うためのより良い方法があるかどうか疑問に思います。

GCCの「属性((aligned(4)))」を使用して、char配列を明示的にワードアラインできることはわかっています。これによりコードが「より安全」になると思いますが、警告によってビルドが乱雑になるため、この状況が再び発生した場合に備えて警告を無効にしたくありません。私たちが望んでいるのは、私たちが試みていることを安全に行う方法です。それでも、後で別の場所で安全でないことをしようとしても、それは私たちに知らせてくれます。これは組み込みシステムであるため、RAMの使用量とフラッシュの使用量はある程度重要です。

移植性(コンパイラーとアーキテクチャー)は大きな問題ではありません。これは1つの製品にのみ当てはまります。ただし、ポータブルソリューションが存在する場合は、それが推奨されます。

これが私たちが現在行っていることの(非常に単純化された)例です:

#define MESSAGE_TYPE_A 0
#define MESSAGE_TYPE_B 1

typedef struct MessageA __attribute__((__packed__))
{
    unsigned char  messageType;
    unsigned short data1;
    unsigned int   data2;
}

typedef struct MessageB __attribute__((__packed__))
{
    unsigned char  messageType;
    unsigned char  data3;
    unsigned char  data4;
}


// This gets filled by the comm system, assume from a UART interrupt or similar
unsigned char data[100];


// Assume this gets called once we receive a full message
void ProcessMessage()
{
    MessageA* messageA;
    unsigned char messageType = data[0];

    if (messageType == MESSAGE_TYPE_A)
    {
        // Cast data to struct and attempt to read
        messageA = (MessageA*)data; // Not safe since data may not be word aligned
                                    // This may cause undefined behavior

        if (messageA->data1 == 4) // warning would be here, when we use the data at the pointer
        {
            // Perform some action...
        }
    }
    // ...
    // process different types of messages
}
4

6 に答える 6

6

すでに指摘したように、ポインターをキャストすることは危険な行為です。

解決策: ユニオンを使用する

struct message {
  unsigned char messageType;
  union {
    struct {
      int data1;
      short data2;
    } A;
    struct {
      char data1[5];
      int data2;
    } B;
  } data;
};

void func (...) {
  struct message msg;
  getMessage (&msg);

  switch (msg.messageType) {
    case TYPEA:
      doStuff (msg.data.A.data1);
      break;
    case TYPEB:
      doOtherStuff (msg.data.B.data1);
      break;
  }
}

これにより、コンパイラは、異なる手段で同じデータにアクセスしていることを認識し、警告や悪いことはなくなります。

もちろん、構造の配置とパッキングがメッセージ形式と一致していることを確認する必要があります。リンクの反対側のマシンが一致しない場合は、エンディアンの問題などに注意してください。

于 2012-01-26T15:22:31.517 に答える
4

とは異なる型のキャストchar *や、 の符号付き/符号なしバリアントへのポインターによる型パニングcharは、C のエイリアス規則 (および注意を怠ると場合によっては整列規則) に違反するため、厳密には準拠していません。

ただし、gcc共用体型による型パニングを許可します。gcc明示的に文書化されたのマンページ:

最後に書き込まれたものとは異なるユニオン メンバーから読み取る (「タイプ パニング」と呼ばれる) 慣行は一般的です。-fstrict-aliasing を使用しても、union 型を介してメモリにアクセスする場合は、型パニングが許可されます。

エイリアシング規則に関連する最適化を無効にするgcc(したがって、プログラムが C のエイリアシング規則を破ることができるようにする) には、プログラムを次のようにコンパイルできます -fno-strict-aliasing。このオプションを有効にすると、プログラムは厳密に準拠しなくなりますが、移植性は問題ではないと言いました。参考までに、Linux カーネルはこのオプションでコンパイルされます。

于 2012-01-25T23:32:38.730 に答える
2

パックされた構造体とmemcpy個々のフィールドを正しいサイズと型の変数に使用するのをやめてください。これは、あなたが達成しようとしていることを実行するための、安全で、ポータブルで、クリーンな方法です。運が良ければ、gcc は小さな固定サイズmemcpyをいくつかの単純なロードおよびストア命令に最適化します。

于 2012-01-26T02:36:51.627 に答える
2

GCC には、-fno-strict-aliasingstrict-aliasing ベースの最適化を無効にしてコードを安全にするフラグがあります。

それを「修正」する方法を本当に探している場合は、コードの動作を再考する必要があります。試みている方法で構造をオーバーレイすることはできないため、次のようにする必要があります。

MessageA messageA;
messageA.messageType = data[0];
// Watch out - endianness and `sizeof(short)` dependent!
messageA.data1 = (data[1] << 8) + data[2];
// Watch out - endianness and `sizeof(int)` dependent!
messageA.data2 = (data[3] << 24) + (data[4] << 16)
               + (data[5] <<  8) + data[6];

この方法により、構造体のパッキングを回避できます。これにより、コード内の他の場所でパフォーマンス特性も向上する可能性があります。別の方法:

MessageA messageA;
memcpy(&messageA, data, sizeof messageA);

パックされた構造でそれを行います。必要に応じて、逆の操作を実行して構造をフラット バッファーに変換します。

于 2012-01-25T23:23:35.700 に答える
1

Cortex M3は、整列されていないアクセスを適切に処理できます。私はこれをM3と同様のパケット処理システムで行いました。何もする必要はありません。フラグ-fno-strict-aliasingを使用して、警告を取り除くことができます。

于 2012-01-26T00:34:04.803 に答える
0

アラインされていないアクセスについては、Linux マクロ get_unaligned/put_unaligned を参照してください。

于 2012-01-26T15:33:46.690 に答える