19

私は従来の C++ アプリケーションに取り組んできましたが、間違いなく自分のコンフォート ゾーンの外にいます (良いことです)。誰かが私にいくつかの指針を与えるほど親切であるかどうか疑問に思っていました(しゃれが意図されています)。

unsigned char 配列の 2 バイトを unsigned short にキャストする必要があります。バイトは連続しています。

私がやろうとしていることの例:

ソケットから文字列を受け取り、unsigned char 配列に配置します。最初のバイトを無視して、次の 2 バイトを unsigned char に変換する必要があります。これは Windows でのみ実行されるため、ビッグ/リトル エンディアンの問題はありません (私が認識しています)。

これが私が今持っているものです(明らかに機能していません):

//packetBuffer is an unsigned char array containing the string "123456789" for testing
//I need to convert bytes 2 and 3 into the short, 2 being the most significant byte
//so I would expect to get 515 (2*256 + 3) instead all the code I have tried gives me
//either errors or 2 (only converting one byte
unsigned short myShort;
myShort = static_cast<unsigned_short>(packetBuffer[1])
4

11 に答える 11

24

さて、あなたはcharを短い値に広げています。必要なのは、2 バイトを short として解釈することです。からにstatic_castキャストできません。にキャストしてから、次のようにキャストする必要があります。unsigned char*unsigned short*void*unsigned short*

unsigned short *p = static_cast<unsigned short*>(static_cast<void*>(&packetBuffer[1]));

これで、p を逆参照して short 値を取得できます。しかし、このアプローチの問題は、unsigned char* から void* にキャストしてから、別の型にキャストすることです。標準は、アドレスが同じままであることを保証しません (さらに、そのポインターの逆参照は未定義の動作になります)。より良いアプローチは、ビットシフトを使用することです。これは常に機能します。

unsigned short p = (packetBuffer[1] << 8) | packetBuffer[2];
于 2008-11-19T02:24:51.853 に答える
4

これはおそらくあなたが気にかけていることよりもはるかに低いですが、これを行うとアラインされていないアクセスを簡単に取得できることに注意してください. x86 は寛容であり、アラインされていないアクセスによって引き起こされるアボートは内部的にキャッチされ、値のコピーと戻りが発生するため、アプリは違いを認識しません (ただし、アラインされたアクセスよりも大幅に遅くなります)。ただし、このコードが非 x86 で実行される場合 (ターゲット プラットフォームについて言及していないため、x86 デスクトップ Windows を想定しています)、これを実行するとプロセッサ データが異常終了し、手動でコピーする必要があります。データをキャストする前に、アラインされたアドレスにデータを配置します。

要するに、このアクセスを頻繁に行う場合は、コードを調整して、アライメントされていない読み取りが発生しないようにすることを検討すると、パフォーマンスが向上します。

于 2008-11-19T02:15:11.403 に答える
3
unsigned short myShort = *(unsigned short *)&packetBuffer[1];
于 2008-11-19T02:28:05.810 に答える
3

上記のビット シフトにはバグがあります。

unsigned short p = (packetBuffer[1] << 8) | packetBuffer[2];

packetBufferバイト単位 (8 ビット幅) の場合、上記のシフトは 0 になる可能性がありpacketBuffer、0 になります。packetBuffer[2];

それにもかかわらず、これはポインタよりも優先されます。上記の問題を回避するために、数行のコードを無駄にします (まったくリテラル ゼロの最適化以外)。結果は同じマシン コードになります。

unsigned short p;
p = packetBuffer[1]; p <<= 8; p |= packetBuffer[2];

または、いくつかのクロックサイクルを節約し、ビットを最後からシフトしないようにするには:

unsigned short p;
p = (((unsigned short)packetBuffer[1])<<8) | packetBuffer[2];

ポインターには注意する必要があります。オプティマイザーは、メモリの配置やその他の問題の長いリストと同様に、あなたを悩ませます。はい、正しく行うと速くなりますが、間違って行うと、バグが長時間残り、最も望ましくないときに発生する可能性があります.

あなたが怠け者で、8 ビットの配列で 16 ビットの計算をしたいとします。(リトルエンディアン)

unsigned short *s;
unsigned char b[10];

s=(unsigned short *)&b[0];

if(b[0]&7)
{
   *s = *s+8;
   *s &= ~7;
}

do_something_With(b);

*s=*s+8;

do_something_With(b);

*s=*s+8;

do_something_With(b);

完全にバグのないコンパイラが期待どおりのコードを作成するという保証はありません。関数bに送信されたバイト配列は、操作によって変更されることはありません。上記のコードには、そうすべきだとは書かれていません。コードを最適化しないと、この問題が発生することはありません (誰かがコンパイラまたはコンパイラのバージョンを最適化または変更するまで)。デバッガを使用している場合、この問題が発生することはありません (手遅れになるまで)。do_something_with()*s

コンパイラは s と b の間の接続を認識しません。これらは 2 つの完全に別の項目です。オプティマイザは*s、メモリに書き戻さないことを選択する場合があります。これ*sは、多くの操作があるため、その値をレジスタに保持し、最後にのみメモリに保存できるためです (存在する場合)。

上記のポインターの問題を解決するには、次の 3 つの基本的な方法があります。

  1. svolatile として宣言します。
  2. ユニオンを使用してください。
  3. 型を変更するたびに関数を使用します。
于 2008-11-20T23:39:17.973 に答える
2

unsigned char ポインターを unsigned short ポインターにキャストしないでください (つまり、より小さなデータ型のポインターからより大きなデータ型へのキャスト)。これは、アドレスが正しく配置されると想定されているためです。より良いアプローチは、バイトを実際の unsigned short オブジェクトにシフトするか、memcpy を unsigned short 配列にシフトすることです。

コンパイラの設定を調整してこの制限を回避できることは間違いありませんが、これは非常に微妙な問題であり、コードが渡されて再利用されると、将来壊れる可能性があります。

于 2008-11-19T02:18:17.920 に答える
1

静的キャストには異なる構文があり、さらにポインターを操作する必要があります。やりたいことは次のとおりです。

unsigned short *myShort = static_cast<unsigned short*>(&packetBuffer[1]);
于 2008-11-19T02:08:41.200 に答える
0
char packetBuffer[] = {1, 2, 3};
unsigned short myShort = * reinterpret_cast<unsigned short*>(&packetBuffer[1]);

私はいつもこれをしなければなりませんでした。ビッグエンディアンは明らかな問題です。マシンが不整合な読み取りを嫌う場合、実際に得られるのは誤ったデータです!(そして書く)。

テストキャストとアサーションを記述して、正しく読み取られるかどうかを確認することをお勧めします。したがって、ビッグエンディアンのマシン、またはさらに重要なことに、読み取りのずれを嫌うマシンで実行すると、追跡が難しい奇妙な「バグ」ではなく、アサートエラーが発生します;)

于 2008-11-19T05:59:48.737 に答える
0

Windows では、以下を使用できます。

unsigned short i = MAKEWORD(lowbyte,hibyte);
于 2008-11-21T11:33:20.340 に答える
0

これは古いスレッドであり、ここで行われたすべての提案を試したとは言えません。私は自分自身を mfc に慣れさせているだけで、uint を 2 バイトに変換し、ソケットの反対側に戻す方法を探していました。

ネット上にはビットシフトの例がたくさんありますが、どれも実際には機能していないようです。多くの例は過度に複雑に見えます。uint から 2 バイトを取得し、それらをネットワーク経由で送信し、反対側の uint にプラグインすることについて話しているだけですよね?

これは私が最終的に思いついた解決策です:

クラス ByteConverter
{
公衆:
 static void uIntToBytes(unsigned int theUint, char* bytes)
  {
   unsigned int tInt = theUint;

   void *uintConverter = &tInt;
   char *theBytes = (char*)uintConverter;

   バイト[0] = theBytes[0];
   バイト[1] = theBytes[1];
  }
 static unsigned int bytesToUint(char *bytes)
  {
   符号なし theUint = 0;

   void *uintConverter = &theUint;
   char *thebytes = (char*)uintConverter;

   thebytes[0] = バイト[0];
   thebytes[1] = バイト[1];

   theUint を返します。
  }
};

次のように使用します。

unsigned int theUint;
文字バイト[2];
CString msg;
ByteConverter::uIntToBytes(65000,バイト); theUint = ByteConverter::bytesToUint(バイト);
msg.Format(_T("theUint = %d"), theUint); AfxMessageBox(msg, MB_ICONINFORMATION | MB_OK);

これが誰かを助けることを願っています。

于 2010-01-23T20:37:08.140 に答える
0

入力が文字列であることを誰も見ませんでした!

/* If it is a string as explicitly stated in the question.
 */
int byte1 = packetBuffer[1] - '0'; // convert 1st byte from char to number.
int byte2 = packetBuffer[2] - '0';

unsigned short result = (byte1 * 256) + byte2;

/* Alternatively if is an array of bytes.
 */
int byte1 = packetBuffer[1];
int byte2 = packetBuffer[2];

unsigned short result = (byte1 * 256) + byte2;

これにより、他のソリューションのほとんどが特定のプラットフォームで発生する可能性のある配置の問題も回避されます。注 short は少なくとも 2 バイトです。ほとんどのシステムでは、2 バイト アラインされていない (またはシステムの sizeof(short) が何であれ) ショート ポインターを逆参照しようとすると、メモリ エラーが発生します。

于 2008-11-19T03:41:08.160 に答える