开发者

Getting a hexadecimal number into a program via the command line

I can do this:

int main(int argc, char** argv) {
  unsigned char cTest = 0xff;
  return 0;
}

But what's the right way to get a hexadecimal number into the program via the command line?

unsigned char cTest = argv[1];

doesn'开发者_运维技巧t do the trick. That produces a initialization makes integer from pointer without a cast warning.


I think some people arriving here might just be looking for:

$ ./prog `python -c 'print "\x41\x42\x43"'`
$ ./prog `perl -e 'print "\x41\x42\x43"'`
$ ./prog `ruby -e 'print "\x41\x42\x43"'`


As the type of main indicates, arguments from the command line are strings and will require conversion to different representations.

Converting a single hexadecimal command-line argument to decimal looks like

#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{
  printf("%ld\n", strtol(argv[1], NULL, 16));

  return 0;
}

Example usage:

$ ./hex ff
255

Using strtol and changing the final argument from 16 to 0 as in

printf("%ld\n", strtol(argv[1], NULL, 0));

makes the program accept decimal, hexadecimal (indicated by leading 0x, and octal (indicated by leading 0) values:

$ ./num 0x70
112
$ ./num 070
56
$ ./num 70
70

Using the bash command shell, take advantage of ANSI-C Quoting to have the shell perform the conversion, and then your program just prints the values from the command line.

#include <stdio.h>
#include <string.h>

int main(int argc, char *argv[])
{
  int i;
  for (i = 1; i < argc; i++) {
    unsigned char value = argv[i][0];
    if (strlen(argv[i]) > 1)
      fprintf(stderr, "%s: '%s' is longer than one byte\n", argv[0], argv[i]);

    printf(i + 1 < argc ? "%u " : "%u\n", value);
  }

  return 0;
}

Bash supports many formats of the form $'...', but $'\xHH' appears to be the closest match to your question. For example:

$ ./print-byte $'\xFF' $'\x20' $'\x32'
255 32 50

Maybe you pack the values from the command line into a string and print it.

#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(int argc, char *argv[])
{
  int i;

  if (argc > 1) {
    char *s = malloc(argc);
    if (!s) {
      fprintf(stderr, "%s: malloc: %s\n", argv[0], strerror(errno));
      return 1;
    }

    for (i = 1; i < argc; i++)
      s[i - 1] = strtol(argv[i], NULL, 16) & 0xff;

    s[argc - 1] = '\0';
    printf("%s\n", s);
    free(s);
  }

  return 0;
}

In action:

$ ./pack-string 48 65 6c 6c 6f 21
Hello!

All of the above is reinventing wheels that bash and the operating system already provide for you.

$ echo $'\x48\x65\x6c\x6c\x6f\x21'
Hello!

The echo program prints its command-line arguments on the standard output, which you can think of as a for loop over the arguments and a printf for each.

If you have another program that performs the decoding for you, use Command Substitution that replaces a command surrounded by backticks or $() with its output. See examples below, which again use echo as a placeholder.

$ echo $(perl -e 'print "\x50\x65\x72\x6c"')
Perl
$ echo `python -c 'print "\x50\x79\x74\x68\x6f\x6e"'`
Python


You could use strtoul which would walk through the characters in the string and convert them, taking into account the radix (16 in this context) that you pass in:-

char *terminatedAt;
if (argc != 2)
    return 1;

unsigned long value = strtoul( argv[1], &terminatedAt, 16);

if (*terminatedAt != '\0')
    return 2;

if (value > UCHAR_MAX)
    return 3;

unsigned char byte = (unsigned char)value;
printf( "value entered was: %d", byte);

As covered in the other examples, there are shorter ways, but none of them allow you to cleanly error check the processing (what happens if someone passes FFF and you've only got an unsiged char to put it into?

e.g. with sscanf:

int val;
sscanf(argv[1], &val)
printf("%d\n", val); 


unsigned char cTest = argv[1];

is wrong, because argv[1] is of type char *. If argv[1] contains something like "0xff" and you want to assign the integer value corresponding to that to an unsigned char, the easiest way would be probably to use strtoul() to first convert it to an unsigned long, and then check to see if the converted value is less than or equal to UCHAR_MAX. If yes, you can just assign to cTest.

strtoul()'s third parameter is a base, which can be 0 to denote C-style number parsing (octal and hexadecimal literals are allowed). If you only want to allow base 16, pass that as the third argument to strtoul(). If you want to allow any base (so you can parse 0xff, 0377, 255, etc.), use 0.

UCHAR_MAX is defined in <limits.h>.


The traditional way to do this kind of thing in C is with scanf(). It's exactly the inverse of printf(), reading the given format out of the file (or terminal) and into the variables you list, rather than writing them into it.

In your case, you'd use sscanf as you've already got it in a string rather than a stream.


atoi, atol, strtoi, strtol

all in stdlib.h

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜