【问题标题】:Convert hex to binary from command line arguments C从命令行参数 C 将十六进制转换为二进制
【发布时间】:2016-02-19 07:56:44
【问题描述】:

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

【问题讨论】:

  • “请不要只给我代码。我需要了解这一点。”好的。
  • 到目前为止你有什么?
  • 已编辑以显示我所拥有的。不知道从那里去哪里
  • 最好提供一个完整的程序(最好是可以编译的),以便我们为您提供最好的帮助。向我们展示一切,而不仅仅是 printBits() 函数,因为您有几个问题要解决,而不仅仅是二进制操作。您还必须获取命令行参数,解析它们的正确性,然后将其转换为整数。如果他/她不愿意帮助你,你的老师会问很多人。
  • 这里提示一下,看看C库中的以下函数:putchar()、strcmp()、sscanf()。这些将让您解决“老师”为您设置的所有问题。最后,要处理命令行参数,您需要在 main() 函数中使用“argc”和“argv”参数,这些参数被广泛使用并且可以很容易地通过 Google 搜索。

标签: c binary hex


【解决方案1】:

有多种方法可以打印任意数字的二进制表示。首先,您可以简单地直接输出移位和索引操作的结果(到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

【讨论】:

    【解决方案2】:

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

    准备好字符缓冲区后,可以使用例如将它们打印到控制台。 fprintf:

    fprintf(stdout, "%s\n", binaryNumber); // (maybe use stderr)
    

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

    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;
    }
    

    打印:

    数字 00000000000000000000000000000001

    数字 000000000000000000000000000000010

    数字 000000000000000000000000000001111

    数字 00000000000000000000000001111111

    数字 000000000000000000000000100000000

    数字 00000000000000000011000000111001

    【讨论】:

    • 的目的是什么? “1”:“0”;这是在做什么?
    • @DNH 否则 1 和 0 不是可打印字符被放入缓冲区,您将看不到任何输出 - 您尝试了吗?
    • "?:" 是执行 if 语句的简写方式。 条件? if_true : if_false。如果您愿意,您可以使用普通的 if/else 语句。许多人更喜欢 ?: 但这并不重要。
    【解决方案3】:

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

    例如

    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
       ...
    }
    

    【讨论】:

      【解决方案4】:

      这是您的程序的一种外观。我已经评论了重要的部分,但您必须查看正在使用的函数的文档。如果您使用的是 linux(看起来您是根据原始问题判断的),那么您可以使用 linux“手册页”,例如 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;
      }
      

      我自己写了这个,但是网络上有很多这种“教学练习”的例子,所以我不认为在这里只包含逐字代码是一个剧透。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2012-06-26
        • 1970-01-01
        • 2015-01-28
        • 2016-08-28
        • 2013-07-25
        相关资源
        最近更新 更多