从命令行参数C将十六进制转换为二进制

时间:2016-02-19 07:56:45

标签: c binary hex

这是一个家庭作业,我必须在终端中调用命令时将十六进制转换为二进制。我的“老师”并不真正“教”C,所以我迷路了。我必须包含一个过程,void printBits(unsigned long i),它打印i中的位。它将使用'-p'开关从命令行调用,后跟一个十六进制形式的32位无符号长整数。 EX:$ lab3 -p 0x5

输出:0000 0000 0000 0000 0000 0000 0000 0101

请不要只给我代码。我需要理解这一点。

void printBits(unsigned long number) {
    // Declare Array to hold our binary number
    char binaryNumber[32];
    // For Loop Index
    int i;
    for (i = 31; i >= 0; --i)
    {
    binaryNumber[i] = (number & 1);
    number >>= 1;
    }

4 个答案:

答案 0 :(得分:1)

有多种方法可以为任意数字打印二进制表示。首先,您可以直接输出移位和索引操作的结果(到stdout,文件等...)这似乎是您开始使用的方法,但随后您声明了32位缓冲区。虽然您当然可以这样做,但如果您不打算返回指向已完成缓冲区的指针,则无需缓冲结果。 (这将我带到我的第3点,下面)

简单地输出位而不存储/返回指向 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' */
}

评论相当明确。该方案是将每个位从最高有效位开始移位(例如,位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'

现在提到上面提到的第三点。从代码主体中的格式化角度来看,简单地输出位是很麻烦的。我发现将这些位存储在一个字符数组( nul-terminated ,因此它可以被视为一个字符串)并返回一个指向该数组的指针以便它可以传递给{{1现在你要么必须传递一个足够大小的数组作为参数,声明一个printf数组,这样数组就不会在函数返回时被销毁,或者在函数内为数组动态分配存储空间。 。根据您的代码需求,您必须权衡所有优缺点。 e.g:

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; } 如何在<{1}}位开始的缓冲区中返回中的起始位置。另请注意,您需要一个p的常量,表示硬件上sz的位数。 (通常以类似于BITS_PER_LONG的方式处理)

注意:请注意long声明的一个限制是转换功能只能在任何一次BUILD_64调用中使用一次(或在任何一行内使用)因为二进制转换只有一个存储阵列。 (您可以随时拨打任意数量的电话并将结果存储在static电话前的不同位置)

二进制打印的最后一个变体是打印包含分隔符的表示,以便更容易识别和比较二进制字符串(特别是在处理printf和{{{{}}的更长序列时1}}&#39; SEG:

printf

该函数基本上与上面0的工作方式相同,但增加了静态缓冲区以适应分隔符,并额外检查位位置以确定何时应将分隔符添加到缓冲区:

1

问题的其余部分只是处理命令行参数,并执行从输入字符串到无符号值的转换以及数字不超过32位的验证检查等。您可以处理使用hexval : 0xdeadbeef => 11011110-10101101-10111110-11101111 或少数简单选项的参数,您只需使用循环。在Linux上,代码应该响应的唯一必需参数是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; } 版本。虽然没有人为简短的例子等做这件事,但获得这些信息至少是好的。查看以下示例,将所有部分放在一起,如果您有任何问题,请告诉我:

getops

使用/输出

-h

答案 1 :(得分:0)

每个棕色都作为一系列位存储在机器中。要将它们打印到控制台,您需要传递一个字符串,其中每个字符为1(0x31)或0(0x30),具体取决于是否设置了相应的数字位。最后一个字符必须是'\ 0'才能表示字符串的结尾。

准备好字符缓冲区后,可以使用例如字符将其打印到控制台。 fprintf(stdout, "%s\n", binaryNumber); // (maybe use stderr)

void
hex_to_bin_print(unsigned long number)
{
    char binaryNumber[33];
    int i;
    for (i = 31; i >= 0; --i)
    {
        binaryNumber[i] = (number & 1) ? '1' : '0';
        number >>= 1;
    }
    binaryNumber[32] = '\0';
    fprintf(stdout, "Number %s\n", binaryNumber);
}

int main(void) {
    hex_to_bin_print(1);
    hex_to_bin_print(2);
    hex_to_bin_print(15);
    hex_to_bin_print(127);
    hex_to_bin_print(256);
    hex_to_bin_print(12345);
    return 0;
}

稍微纠正你的代码以确保字符串是NULL终止的(它有'\ 0'作为缓冲区中的最后一个字符)并且你将字符放入缓冲区(否则1和0这些不可打印的字符被放入缓冲区并且你根本看不到任何输出:

{{1}}

打印:

编号00000000000000000000000000000001

编号00000000000000000000000000000010

编号00000000000000000000000000001111

编号00000000000000000000000001111111

编号00000000000000000000000100000000

编号00000000000000000011000000111001

答案 2 :(得分:0)

首先,将从命令行获取的字符串转换为整数, 最简单的是使用sscanf

e.g。

if ( sscanf( argv[1], "%X", &n ) == 1)
{
  ...

现在您有小数值n

为了转换为二进制,你需要遍历无符号整数中的每一位。

通过按位和小数值,您可以检查每个位是否已设置并根据位打印“1”或“0”

for (int i = 0; i < 32; ++i)
{
   unsigned int mask = 0x8000 >> i; // this bit we check
   char ch = (n & mask) ? '1' : '0'; // see if it is set or not
   ...
}

答案 3 :(得分:0)

这是您的程序可以看到的一种方式。我已经对重要部分进行了评论,但您必须查看正在使用的函数的文档。如果你正在使用linux(看起来你正在判断原始问题)那么你可以使用linux&#34;手册页&#34;例如man sscanf提供有关sscanf或C库中任何其他功能的完整信息。

编译: gcc main.c -o lab3

/* main.c */
#include <stdio.h> //provides putchar()
#include <strings.h> //provides sscanf() and strcmp()
#include <stdlib.h> //provides EXIT_x values

void printBits(unsigned long i)
{
  int j; //define a loop counter

  for(j = 0 ; j < 32 ; j++)
  {
    //test the highest bit and write a 1 or a 0
    //(we always test the highest bit but we shift the number along each time)
    putchar(i & 0x80000000 ? '1' : '0');

    //shift the bits along by one ready for the next loop iteration
    i <<= 1;

    //print a space after every 4th bit to break it up into nybbles
    if((j % 4) == 3)
      putchar(' ');
  }

  //finish the output in a tidy manner by writin a newline
  putchar('\n');
}


//a helpful function to assist the user
void usage(const char* progname)
{
  //show the user the proper way to run the program
  printf("%s -p 0x1234abcd\n", progname);
}

//this version of the main() signature gives access to commandline arguments
int main(int argc, char** argv)
{
  //flag to show the commandline arguments are in the wrong format
  int badargs = 0;

  //variable to store the 32 bit number given by the user
  unsigned long value;

  if(argc == 3) //do we have the right number of commandline arguments?
    if(strcmp(argv[1], "-p") == 0) //is argv[1] equal to "-p" ?
      if(sscanf(argv[2], "0x%x", &value) == 1) //is the number formatted like hex?
        printBits(value); //success, user input was good, print the bits!
      else
        badargs = 1; //the integer was not properly formatted as hexadecimal like 0x1234
    else
      badargs = 1; //argv[1] was not "-p"
  else
    badargs = 1; //wrong number of args given by user

  if(badargs) //we detected bad argument syntax earlier so we'd better remind the user what to do
  {
    printf("Incorrect argument syntax\n\n\t");
    usage(argv[0]); //argv[0] is the name of your executable program file ("lab3" in your case)
    putchar('\n');
    return EXIT_FAILURE;
  }

  return EXIT_SUCCESS;
}

我自己写了这个,但是有很多这种教学练习的例子&#34;在网上,所以我不认为在这里只包括逐字代码是一个扰流板。