0

ポインターのアドレスを取得し、そのバイトを個別に格納する (たとえば、それらをシリアルに送信する) ための、最も標準に準拠した方法を見つける必要があります。

以下に 2 つのバージョンがあります。最初のバージョンには未定義の動作が含まれていると思われます。2 番目のバージョンには、C99 に従って定義された動作のみが含まれている必要があります。しかし、私のツールは、2番目のものにも未定義の動作があることを示しています。可能であれば、誰かがそれを確認し、未定義の動作も実装定義の動作もない解決策を示してもらえますか?

編集:実装に依存しない解決策を見つけるのを助けるために、タイプをからに変更しましたintunsigned long「16ビット幅のポインター」も削除しました。

unsigned long a[2];
unsigned char b0, b1, b2, b3;

int main1() {
  unsigned long l = (unsigned long) &(a[0]);
  b0 = (l >> 24) & 0xFF;
  b1 = (l >> 16) & 0xFF;
  b2 = (l >> 8) & 0xFF;
  b3 = l & 0xFF;
  return 0;
}


typedef union { unsigned long* p; char c[sizeof(unsigned long *)]; } u;

int main2() {
  u x;
  x.p = a;
  b0 = x.c[3];
  b1 = x.c[2];
  b2 = x.c[1];
  b3 = x.c[0];
  return 0;
}

編集 2 : これらのプログラムに関する C99 標準の一部への参照を追加しました。

任意のポインター型を整数型に変換できます。前に指定された場合を除き、結果は実装定義です。結果が整数型で表現できない場合、動作は未定義です。結果は、任意の整数型の値の範囲内にある必要はありません。

a実装定義の動作に依存せずに配列のアドレスを読み取ることができないということですか? または回避する方法はありますか?

4

1 に答える 1

2

ポインターの場合は、type unsigned long(またはunsigned long long) を使用することをお勧めします。uintptr_tデータ型がない限り。なぜunsignedですか?シフト操作は符号なし整数に対してのみ一般的であるためです。署名されたものについては、プラットフォームに依存します。

したがって、アドレスを転送したい場合 (アドレスは通常プロセスローカルであるため、何らかの理由で)、次のようにすることができます。

/**
 * @param ptr Pointer to serialize
 * @param buf Destination buffer
 * @param be  If 0 - little endian, 1 - big endian encoding
 */
void ptr2buf(const void *ptr, void *buf, int be)
{
    uintptr_t u = (uintptr_t)ptr;
    unsigned char *d = buf;

    if (be)
    {
        /* big endian */
        d += sizeof(t) - 1;

        for (i = 0; i < sizeof(t); ++i)
        {
            *d-- = u & 0xFF;
            u >>= 8;
        }
    }
    else
    {
        /* little endian */

        for (i = 0; i < sizeof(t); ++i)
        {
            *d++ = u & 0xFF;
            u >>= 8;
        }
    }
}
于 2013-05-22T11:50:20.383 に答える