有多种方法可以打印任意数字的二进制表示。首先,您可以简单地直接输出移位和索引操作的结果(到stdout、文件等...)这似乎是您开始使用的方法,但随后您声明了一个 32 位缓冲区。虽然您当然可以这样做,但如果您不打算返回指向已完成缓冲区的指针,则无需缓冲结果。 (这将我带到下面的第三点)
简单地输出位而不存储/返回指向 nul-terminated 字符串中位的指针,有它的位置,但通常用途有限。然而,这是一个包含所有方法基础的常见问题。创建未填充的二进制表示的方法如下:
/** unpadded binary representation of 'v'. */
void binprn (const unsigned long v)
{
if (!v) { putchar ('0'); return; }; /* if v = 0 output '0' */
size_t sz = sizeof v * CHAR_BIT; /* get the number of bits in v */
unsigned long rem = 0; /* variable to hold shifted result */
while (sz--) /* for each bit (in decreasing order) */
if ((rem = v >> sz)) /* if bits exist in the shifted value */
putchar ((rem & 1) ? '1' : '0'); /* output '1' or '0' */
}
cmets 的解释性很强。该方案是从最高有效位开始移动每个位(例如 32 位数字的位 31 (31-0)。您检查移位后是否有任何 1 位(如果没有,则移位超过最数字中的重要位位置,不需要打印任何内容)。一旦在rem 中找到一个位,在循环迭代的其余部分中总会有位位要打印,因为您的移位量正在减少。通过开始使用最高有效位(首先打印),您最终会以正确的顺序打印出您的位,并且只打印构成该数字的位数。
通常,当您只是将二进制表示直接输出到屏幕时,您只想输出最高有效位的位。 (这可以防止输出一个 1 前面有 63 个 0 把事情弄得一团糟。)
接下来,将填充的二进制表示输出到一定数量的位。如果您只想查看任意数量的较低 8, 16, 32, ... 位,但希望每次都使用固定数量的位表示,这很有用。在这里,您只需传递您希望查看的位数。然后,您的函数将遍历您数字中的该位数并输出结果:
/** binary representation of 'v' padded to 'sz' bits.
* the padding amount is limited to the number of
* bits in 'v'. valid range: 0 - sizeof v * CHAR_BIT.
*/
void binprnpad (const unsigned long v, size_t sz)
{
if (!sz) putchar ((v & 1) ? '1' : '0'); /* if no sz, '0' is fine */
if (sz > sizeof v * CHAR_BIT) /* if sz exceed # of bits, limit */
sz = sizeof v * CHAR_BIT;
while (sz--) /* for sz positions in decreasing order, '1' or '0' */
putchar ((v >> sz & 1) ? '1' : '0');
}
您会注意到这里的主要区别是您不必自己检查是否保留位以防止打印不需要的前导零,因为您正在使用参数sz 控制位数。 (如果通过0 大小由你决定,我只是选择输出'0')
现在谈谈上面提到的第三点。从代码主体的格式化角度来看,简单地输出位是很麻烦的。我发现将这些位存储在字符数组中(nul-terminated 因此可以将其视为字符串)并返回指向该数组的指针以便将其传递给 printf等。现在你要么必须传递一个足够大的数组作为参数,声明一个static 数组,这样数组就不会在函数返回时被破坏,或者为函数内的数组动态分配存储空间。所有这些都有优点和缺点,您必须根据代码的需要来权衡。例如:
/** returns pointer to binary representation of 'v' zero padded to 'sz'.
* returns pointer to string contianing binary representation of
* unsigned 64-bit (or less ) value zero padded to 'sz' digits.
*/
char *binpad (const unsigned long v, const size_t sz)
{
static char s[BITS_PER_LONG + 1] = {0};
char *p = s + BITS_PER_LONG;
register size_t i;
for (i = 0; i < sz; i++)
*--p = (v>>i & 1) ? '1' : '0';
return p;
}
代码的功能与上面的非缓冲填充代码相同。请注意p 如何返回起始位置在缓冲区中sz 的位数开始。另请注意,BITS_PER_LONG 需要一个常量,表示硬件上long 中的位数。 (通常处理方式类似于BUILD_64)
注意: 请注意,static 声明的一个限制是转换函数只能在任何一个 printf 调用(或任何单行代码)中使用一次,因为二进制转换只有一个存储阵列。 (在拨打printf 之前,您可以随时拨打任意数量的电话并将结果存储在不同的位置)
二进制打印的最后一个变化是打印包含分隔符的表示形式,以便更容易识别和比较二进制字符串(尤其是在处理0's 和1's 的较长序列时。例如:
hexval : 0xdeadbeef => 11011110-10101101-10111110-11101111
该函数的工作原理与上面的binpad 基本相同,但增加了静态缓冲区更大以容纳分隔符并额外检查位位置以确定何时应将分隔符添加到缓冲区:
/** returns pointer to formatted binary representation of 'v' zero padded to 'sz'.
* returns pointer to string contianing formatted binary representation of
* unsigned 64-bit (or less ) value zero padded to 'sz' digits with char
* 'sep' placed every 'szs' digits. (e.g. 10001010 -> 1000-1010).
*/
char *binfmt (const unsigned long v, const unsigned char sz,
const unsigned char szs, const char sep)
{
static char s[BITS_PER_LONG * 2 + 1] = {0};
char *p = s + 2 * BITS_PER_LONG;
register size_t i;
*p = 0;
for (i = 0; i < sz; i++) {
p--;
if (i > 0 && szs > 0 && i % szs == 0)
*p-- = sep;
*p = (v >> i & 1) ? '1' : '0';
}
return p;
}
您的问题的其余部分只是处理命令行参数,并执行从输入字符串到无符号值的转换以及数字不超过 32 位等的验证检查。您可以处理带有getops 的参数或少量简单选项,您可以简单地使用循环。在 Linux 上,您的代码应该响应的唯一必需参数是-h 用于帮助和-v 用于版本。虽然没有人为简短的示例等这样做,但至少有这些信息是件好事。查看以下将所有部分组合在一起的示例,如果您有任何问题,请告诉我:
#include <stdio.h>
#include <stdlib.h> /* for strtoul */
#include <errno.h> /* for errno */
#include <limits.h> /* for UINT_MAX, ULONG_MAX, CHAR_BIT */
#define PACKAGE "hex2bin"
#define VERSION "0.01"
/* BUILD_64 - Check x86/x86_64 */
#if defined(__LP64__) || defined(_LP64)
# define BUILD_64 1
#endif
/* BITS_PER_LONG */
#ifdef BUILD_64
# define BITS_PER_LONG 64
#else
# define BITS_PER_LONG 32
#endif
unsigned long processopts (int argc, char **argv);
unsigned long xstrtoul (char *s);
void binprn (const unsigned long v);
void binprnpad (const unsigned long v, size_t sz);
char *binpad (const unsigned long v, const size_t sz);
char *binfmt (const unsigned long v, const unsigned char sz,
const unsigned char szs, const char sep);
void help (int xcode);
int main (int argc, char **argv) {
unsigned long hexval = processopts (argc, argv);
/* print unpadded binary */
printf ("\n hexval : 0x%lx (%lu) => ", hexval, hexval);
binprn (hexval);
printf ("\n");
/* print padded to 32-bits */
printf ("\n hexval : 0x%lx (%lu) => ", hexval, hexval);
binprnpad (hexval, sizeof (int) * CHAR_BIT);
printf ("\n");
/* padded binary returned as formatted string
* with '-' separators every 8 bits
*/
printf ("\n hexval : 0x%lx (%lu) => %s\n\n", hexval, hexval,
binfmt (hexval, sizeof (int) * CHAR_BIT, CHAR_BIT, '-'));
return 0;
}
/* quick custom argument handler */
unsigned long processopts (int argc, char **argv)
{
size_t i = 1;
unsigned long val = 0;
if (argc < 2) help (0); /* insufficient arguments */
for (; argv[i]; i++) { /* for each argument */
if (*argv[i] == '-') { /* for each beginning with '-' */
switch (argv[i][1]) {
case 'h': /* respond to '-h' help */
help (0);
case 'p': /* handle '-p' convert value */
if (!argv[i+1]) { /* if '-p' w/o next arg */
fprintf (stderr, "error: insufficient input.\n");
help (1);
}
if (*argv[i+1] != '0' || /* validate hex input */
(argv[i+1][1] != 'x' && argv[i+1][1] != 'X')) {
fprintf (stderr, "error: invalid 'hex_value' input.\n");
help (1);
}
val = xstrtoul (argv[i+1]); /* convert to ulong */
if (val > UINT_MAX) { /* validate 32-bits */
fprintf (stderr, "error: input value exceeds 32-bits.\n");
help (1);
}
break;
case 'v': /* respond to '-v' version */
printf ("%s, version %s\n", PACKAGE, VERSION);
exit (0);
default :
fprintf (stderr, "error: invalid/unrecognized option '%s'.\n",
argv[i]);
help (1);
}
}
}
return val; /* return val */
}
unsigned long xstrtoul (char *s)
{
unsigned long v = 0;
errno = 0;
/* test for hex or decimal conversion */
if (*s == '0' && (s[1] == 'x' || s[1] == 'X'))
v = strtoul (s, NULL, 16);
else
v = strtoul (s, NULL, 10);
/* check for various possible errors */
if ((errno == ERANGE && v == ULONG_MAX) || (errno != 0 && v == 0)) {
perror ("strtoul");
exit (EXIT_FAILURE);
}
return v;
}
/** unpadded binary representation of 'v'. */
void binprn (const unsigned long v)
{
if (!v) { putchar ('0'); return; };
size_t sz = sizeof v * CHAR_BIT;
unsigned long rem = 0;
while (sz--)
if ((rem = v >> sz))
putchar ((rem & 1) ? '1' : '0');
}
/** binary representation of 'v' padded to 'sz' bits.
* the padding amount is limited to the number of
* bits in 'v'. valid range: 0 - sizeof v * CHAR_BIT.
*/
void binprnpad (const unsigned long v, size_t sz)
{
if (!sz) putchar ((v & 1) ? '1' : '0');
if (sz > sizeof v * CHAR_BIT)
sz = sizeof v * CHAR_BIT;
while (sz--)
putchar ((v >> sz & 1) ? '1' : '0');
}
/** returns pointer to binary representation of 'v' zero padded to 'sz'.
* returns pointer to string contianing binary representation of
* unsigned 64-bit (or less ) value zero padded to 'sz' digits.
*/
char *binpad (const unsigned long v, const size_t sz)
{
static char s[BITS_PER_LONG + 1] = {0};
char *p = s + BITS_PER_LONG;
register size_t i;
for (i = 0; i < sz; i++)
*--p = (v>>i & 1) ? '1' : '0';
return p;
}
/** returns pointer to formatted binary representation of 'v' zero padded to 'sz'.
* returns pointer to string contianing formatted binary representation of
* unsigned 64-bit (or less ) value zero padded to 'sz' digits with char
* 'sep' placed every 'szs' digits. (e.g. 10001010 -> 1000-1010).
*/
char *binfmt (const unsigned long v, const unsigned char sz,
const unsigned char szs, const char sep)
{
static char s[BITS_PER_LONG * 2 + 1] = {0};
char *p = s + 2 * BITS_PER_LONG;
register size_t i;
*p = 0;
for (i = 0; i < sz; i++) {
p--;
if (i > 0 && szs > 0 && i % szs == 0)
*p-- = sep;
*p = (v >> i & 1) ? '1' : '0';
}
return p;
}
void help (int xcode)
{
xcode = xcode ? xcode : 0; /* set default exit code */
printf ("\n %s, version %s\n\n"
" usage: %s -p hex_value (32-bit)\n\n"
" converts 'hex_value' to its binary representation.\n\n"
" Options:\n\n"
" -h this help.\n"
" -p hex_value display binary representation of 'hex_value'.\n"
" -v display version information.\n\n",
PACKAGE, VERSION, PACKAGE);
exit (xcode);
}
使用/输出
$ ./bin/hex2bin -p 0xe7
hexval : 0xe7 (231) => 11100111
hexval : 0xe7 (231) => 00000000000000000000000011100111
hexval : 0xe7 (231) => 00000000-00000000-00000000-11100111
$ ./bin/hex2bin -p 0xdeadbeef
hexval : 0xdeadbeef (3735928559) => 11011110101011011011111011101111
hexval : 0xdeadbeef (3735928559) => 11011110101011011011111011101111
hexval : 0xdeadbeef (3735928559) => 11011110-10101101-10111110-11101111
$ ./bin/hex2bin -h
hex2bin, version 0.01
usage: hex2bin -p hex_value (32-bit)
converts 'hex_value' to its binary representation.
Options:
-h this help.
-p hex_value display binary representation of 'hex_value'.
-v display version information.
$ ./bin/hex2bin -v
hex2bin, version 0.01