cbase-conversionstrtol

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.


Solution

  • 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