まず、 を使用strtoul
して 32 ビット値を取得します。次に、バイトオーダーを でビッグエンディアンに変換しますhtonl
。最後に、結果を配列に保存します。
#include <arpa/inet.h>
#include <stdlib.h>
/* ... */
unsigned char q[32] = "1100111...";
unsigned char result[4] = {0};
*(unsigned long*)result = htonl(strtoul(q, NULL, 2));
他の方法もあります。
でも足りない<arpa/inet.h>
!
次に、プラットフォームのバイト順を知る必要があります。ビッグ エンディアンの場合htonl
は何もせず、省略できます。リトルエンディアンの場合htonl
は、次のとおりです。
unsigned long htonl(unsigned long x)
{
x = (x & 0xFF00FF00) >> 8) | (x & 0x00FF00FF) << 8);
x = (x & 0xFFFF0000) >> 16) | (x & 0x0000FFFF) << 16);
return x;
}
運が良ければ、オプティマイザーがユーザーの作業を認識して、効率的なコードに変換する可能性があります。そうでない場合は、少なくともレジスタと O(log N) ですべて実装可能です。
プラットフォームのバイト順がわからない場合は、それを検出する必要があります。
typedef union {
char c[sizeof(int) / sizeof(char)];
int i;
} OrderTest;
unsigned long htonl(unsigned long x)
{
OrderTest test;
test.i = 1;
if(!test.c[0])
return x;
x = (x & 0xFF00FF00) >> 8) | (x & 0x00FF00FF) << 8);
x = (x & 0xFFFF0000) >> 16) | (x & 0x0000FFFF) << 16);
return x;
}
たぶんlong
8バイトです!
さて、OPは配列サイズで4バイトの入力を暗示していましたが、8バイトlong
は実行可能です:
#define kCharsPerLong (sizeof(long) / sizeof(char))
unsigned char q[8 * kCharsPerLong] = "1100111...";
unsigned char result[kCharsPerLong] = {0};
*(unsigned long*)result = htonl(strtoul(q, NULL, 2));
unsigned long htonl(unsigned long x)
{
#if kCharsPerLong == 4
x = (x & 0xFF00FF00UL) >> 8) | (x & 0x00FF00FFUL) << 8);
x = (x & 0xFFFF0000UL) >> 16) | (x & 0x0000FFFFUL) << 16);
#elif kCharsPerLong == 8
x = (x & 0xFF00FF00FF00FF00UL) >> 8) | (x & 0x00FF00FF00FF00FFUL) << 8);
x = (x & 0xFFFF0000FFFF0000UL) >> 16) | (x & 0x0000FFFF0000FFFFUL) << 16);
x = (x & 0xFFFFFFFF00000000UL) >> 32) | (x & 0x00000000FFFFFFFFUL) << 32);
#else
#error Unsupported word size.
#endif
return x;
}
それは 8 ビットではないためchar
(DSP はこれを行うのが好きです)、自分で作業する必要があります。(これが、SHARC シリーズの DSP が 8 ビットのバイトを持っていたときに大したことだった理由です。これにより、既存のコードの移植が非常に簡単になりました。これは、C が移植性サポートのひどい仕事をしているためです。)
任意の長さのバッファはどうですか? おかしなポインタ型キャストはやめてください。
OP のバージョンで改善できる主な点は、ループの内部を再考することです。出力バイトを固定データ レジスタと考える代わりに、連続する各ビットが右端 (LSB) にシフトされるシフト レジスタと考えてください。これにより、これらすべての部門と改造からあなたを救うことができます (うまくいけば、ビットシフトに最適化されます)。
unsigned char
正気を保つために、私はuint8_t
.
#include <stdint.h>
unsigned StringToBits(const char* inChars, uint8_t* outBytes, size_t numBytes,
size_t* bytesRead)
/* Converts the string of '1' and '0' characters in `inChars` to a buffer of
* bytes in `outBytes`. `numBytes` is the number of available bytes in the
* `outBytes` buffer. On exit, if `bytesRead` is not NULL, the value it points
* to is set to the number of bytes read (rounding up to the nearest full
* byte). If a multiple of 8 bits is not read, the last byte written will be
* padded with 0 bits to reach a multiple of 8 bits. This function returns the
* number of padding bits that were added. For example, an input of 11 bits
* will result `bytesRead` being set to 2 and the function will return 5. This
* means that if a nonzero value is returned, then a partial byte was read,
* which may be an error.
*/
{ size_t bytes = 0;
unsigned bits = 0;
uint8_t x = 0;
while(bytes < numBytes)
{ /* Parse a character. */
switch(*inChars++)
{ '0': x <<= 1; ++bits; break;
'1': x = (x << 1) | 1; ++bits; break;
default: numBytes = 0;
}
/* See if we filled a byte. */
if(bits == 8)
{ outBytes[bytes++] = x;
x = 0;
bits = 0;
}
}
/* Padding, if needed. */
if(bits)
{ bits = 8 - bits;
outBytes[bytes++] = x << bits;
}
/* Finish up. */
if(bytesRead)
*bytesRead = bytes;
return bits;
}
inChars
null で終了していることを確認するのはあなたの責任です。この関数は、'0'
or以外の最初の'1'
文字が検出された場合、または出力バッファが不足した場合に戻ります。使用例:
unsigned char q[32] = "1100111...";
uint8_t buf[4];
size_t bytesRead = 5;
if(StringToBits(q, buf, 4, &bytesRead) || bytesRead != 4)
{
/* Partial read; handle error here. */
}
これは 4 バイトを読み取るだけで、読み取れない場合はエラーをトラップします。
unsigned char q[4096] = "1100111...";
uint8_t buf[512];
StringToBits(q, buf, 512, NULL);
これは、変換できるものを変換し、残りを 0 ビットに設定するだけです。
break
この関数は、Cに複数レベルのループまたはswitch
;から抜け出す機能があれば、より適切に実行できます。現状では、同じ効果を得るにはフラグ値を追加する必要がありますが、これは混乱です。または、 を追加する必要がgoto
あります。