There are a number of ways to approach printing the binary representation for any number. First, you can simply output the result of your shift and index operation directly (to stdout, a file, etc...) This seems to be the approach you began with, but then you declared a 32 bit buffer. While you can certainly do that, there is no need to buffer the results if you are not going to return a pointer to the completed buffer. (that brings me to my 3rd point, below)
Simply outputting bits without storing/returning a pointer to the bits in a nul-terminated string, has its place but is generally of limited use. Nevertheless, it is a common problem that encompasses the basics of all approaches. Creating an unpadded binary representation can be approached as follows:
/** 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' */
}
The comments are fairly explanatory. The scheme is to shift each bit beginning with the most significant bit (e.g. bit 31 (31-0) for a 32-bit number. You check whether there are any 1-bits following the shift (if not, the shift exceeds the most significant bit position in the number and nothing need be printed). Once there is a bit found in rem, there will always be bits bits to print throughout the remainder of the loop iterations because you are shifting by a decreasing amount. By starting with the most significant bit (which prints first), you end up with your bits printed out in the correct order and only printing the number of bits that make up the number.
Generally when you are simply outputting the binary representation directly to the screen, you will only want to output bits up to the most significant bit. (which prevents outputting a 1 with 63 0's in front of it making a mess out of things.)
Next, is outputting a padded binary representation to some number of bits. This is useful if you just want to look at the lower 8, 16, 32, ... bits in any number, but want a representation with a fixed number of bits each time. Here you simply pass the number of bits you wish to look at. Your function will then loop over that number of bit-positions in your number and output the results:
/** 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');
}
You will notice the primary difference here is that you do not have to concern yourself with checking whether bits remain to prevent printing unwanted leading zeros, because you are controlling the number of bits with the parameter sz. (it's up to you what you do if a 0 size is passed, I just choose to output '0')
Now for the third point mentioned above. Simply outputting bits is cumbersome from a formatting standpoint back in the main body of your code. I find it far more useful to store the bits in a character array (nul-terminated so it can be treated as a string) and return a pointer to the array so it can be passed to printf, etc. Now you either have to pass an adequately sized array as a parameter, declare a static array so the array isn't destroyed on function return, or dynamically allocate storage for the array within the function. All have pluses and minuses that you have to weigh depending upon the needs of your code. e.g.:
/** 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;
}
The code functions the same as its non-buffered padded counterpart above. Note how p returns the beginning position within the buffer where sz number of bits begins. Also note that you will need a constant for BITS_PER_LONG denoting the number of bits in a long on your hardware. (which is normally handled in a manner similar to BUILD_64)
note: just be aware that one limitation to a static declaration is the conversion function can only be used once in any one printf call (or within any single line of code) since there is only one storage array for the binary conversion. (You can always make any number of calls and store the results in different locations just before the making the printf call)
One final variation on the binary print is the print the representation with separators included to make it easier to identify and compare between binary strings (especially when dealing with longer sequences of 0's and 1's. e.g.:
hexval : 0xdeadbeef => 11011110-10101101-10111110-11101111
The function essentially works the same as binpad above, but with the addition that the static buffer is larger to accommodate the separators and an additional check on bit-position to determine when a separator should be added to the buffer:
/** 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;
}
The remainder of your problem is simply to process the command line arguments, and perform the conversion from input string to unsigned value along with the validation checks that the number does not exceed 32-bits, etc.. You can either process the arguments with getops or for a small number of simple options you can simply use a loop. On Linux, the only required arguments your code should respond to are -h for help and -v for version. While nobody does this for short examples, etc., it is at least nice to have that information. Look over the following example that puts all the pieces together and let me know if you have any questions:
#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);
}
Use/Output
$ ./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