Существует несколько способов печати двоичного представления любого числа. Во-первых, вы можете просто вывести результат вашей операции сдвига и индексации напрямую (в stdout
, файл и т. д.). Кажется, это подход, с которого вы начали, но затем вы объявили 32-битный буфер. Хотя вы, безусловно, можете это сделать, нет необходимости буферизовать результаты, если вы не собираетесь возвращать указатель на заполненный буфер. (это подводит меня к моему 3-му пункту ниже)
Простой вывод битов без сохранения/возврата указателя на биты в строке с нулевым завершением имеет место быть, но, как правило, имеет ограниченное применение. Тем не менее, это общая проблема, которая охватывает основы всех подходов. К созданию незаполненного двоичного представления можно подойти следующим образом:
/** 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' */
}
Комментарии достаточно поясняющие. Схема состоит в том, чтобы сдвигать каждый бит, начиная со старшего бита (например, бит 31 (31-0) для 32-битного числа. Вы проверяете, есть ли какие-либо 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'
)
Теперь о третьем пункте, упомянутом выше. Простой вывод битов является громоздким с точки зрения форматирования в основной части вашего кода. Я считаю, что гораздо полезнее хранить биты в массиве символов (с нулевым завершением, чтобы его можно было рассматривать как строку) и возвращать указатель на массив, чтобы его можно было передать в 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
и 1
, например:
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
person
David C. Rankin
schedule
19.02.2016
0-size
, вы получаете биты в порядке хост-байтов, поэтому первые биты, которые вы сдвигаете, являются наименее значимыми битами на оборудовании с прямым порядком байтов. Вы можете справиться с этим несколькими способами, проиндексировать позиции символов в вашем массиве от высокого к низкому, чтобы исправить проблему, или вы можете сначала выполнить большие сдвиги (например,n >> size - 1
) и записать биты в нормальном порядке. Как только вы разберетесь с этой причудой, кодирование станет легким. - person David C. Rankin   schedule 19.02.2016