通过命令行将十六进制数传入程序

13

我可以做到这个:

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

但是,通过命令行将十六进制数传递到程序中的正确方式是什么?

unsigned char cTest = argv[1];

并不能实现这一点。它会产生一个initialization makes integer from pointer without a cast警告。


4
命令行参数始终为字符串。某些平台可能将它们作为Unicode传递,这种情况下简单的strtol函数将无法工作。 - dirkgently
@dirkgently:如果是这种情况,mbstol或其他的多字节或宽字符(或TCHAR)变体会涵盖这个问题吗?(我也怀疑这不是一个关键问题,考虑到问题的性质。) - Ruben Bartelink
1
@dirkgently:如果是这样,main()函数的第二个参数不是char **类型吗?那就不符合标准了。 - Alok Singhal
@Alok:它们仍然可能是多字节的,这种情况下mbstoul是最好的选择,但在问题的背景下,我认为这太花哨了。 - Ruben Bartelink
6个回答

18

我认为一些来到这里的人可能只是在寻找以下内容:

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

2
非常感谢 - 非常有帮助 - TomP89

12

正如 main 的类型表明的那样,命令行参数是字符串,需要将其转换为不同的表示形式。

将单个十六进制命令行参数转换为十进制的方式如下:

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

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

  return 0;
}

使用示例:

$ ./hex ff
255

使用 strtol 函数,将最后一个参数从 16 改为 0,例如:

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

使程序能够接受十进制、十六进制(以前缀0x表示)和八进制(以前缀0表示)的值:

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

使用bash命令行,利用ANSI-C引用来让shell执行转换,然后您的程序只需从命令行打印值。

#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支持形式为$'...'的许多格式,但是$'\xHH'似乎是最接近你问题的匹配。例如:

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

也许你可以将命令行中的值封装到一个字符串中并将其打印出来。

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

实际应用:

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

以上所有操作都是在重新发明轮子,但是bash和操作系统已经为您提供了这些功能。

$ echo $'\x48\x65\x6c\x6c\x6f\x21'
Hello!
< p > echo程序会将其命令行参数打印到标准输出上,你可以将其视为对参数执行for循环以及对每个参数执行printf

如果你有另一个程序来为你执行解码操作,可以使用命令替换,它会用反引号或$()括起来的命令输出替换它自己。请参考下面的示例,其中再次使用echo作为占位符。

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

7
您可以使用strtoul函数,它会遍历字符串中的字符并将它们转换为数字,在此过程中会考虑您传入的进制数(在此上下文中为16)。
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);

正如其他示例中所述,有更短的方法,但它们都无法让您清晰地检查处理错误(如果有人传递 FFF 而您只有一个 unsiged char 来存储它,会发生什么?)

例如,使用 sscanf

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

UCHAR_MAX 可能大于 LONG_MAX,所以你应该使用 strtoul() 函数,且 value > 255 应该改为 value > UCHAR_MAX。最后,static_cast 是 C++ 中的操作符;但在此处不需要进行强制类型转换,因为存在隐式转换规则。 - Alok Singhal
他,初始版本中我有一个 cout :P 我一开始考虑使用 u,但认为这会使问题变得过于复杂,因为提问者主要是想理解一个对他们来说不太明显的概念。 - Ruben Bartelink
我同意有时候为了初学者,我们需要让事情变得不那么严谨。但在这种情况下,即使对于初学者来说,也不会太难以理解。这将防止很多人假设 UCHAR_MAX == 255LONG_MAX > UCHAR_MAX 等等。感谢您的编辑和出色的回答! - Alok Singhal
@Alok:总是很乐意挑剔,谢谢!我认为这里的强制类型转换很好,因为它向读者解释了正在发生的事情,并且将避免警告,如果它们被打开,就会出现缩短转换。我会忽略mbs的问题(并且wchar_t的问题,因为问题排除了它们)。结束! - Ruben Bartelink

4
unsigned char cTest = argv[1];

这是错误的,因为argv[1]的类型是char *。如果argv[1]包含类似于"0xff"的内容,并且您想将相应的整数值赋给unsigned char,最简单的方法可能是使用strtoul()先将其转换为unsigned long,然后检查转换后的值是否小于或等于UCHAR_MAX。如果是,则可以直接赋值给cTest

strtoul()的第三个参数是一个基数,可以为0表示C风格的数字解析(八进制和十六进制字面量都允许)。如果您只想允许基数16,请将其作为第三个参数传递给strtoul()。如果您想允许任何基数(以便您可以解析0xff0377255等),请使用0。

UCHAR_MAX<limits.h>中定义。


+1 很好的解释,涵盖了我在代码中试图展示的大部分观点背后的推理。 - Ruben Bartelink

1

在C语言中,做这种事情的传统方法是使用scanf()。它与printf()完全相反,从文件(或终端)中读取给定格式的内容,并将其输入到您列出的变量中,而不是将它们写入其中。

在您的情况下,您将使用sscanf,因为您已经将其放在字符串中而不是流中。


-3

atoiatolstrtoistrtol

都在 stdlib.h 中。


1
所有指出的3分钟前(链接手册的参见部分),但没有帮助提问者理解为什么这是必要的或给出示例。当一个帖子没有添加内容时,删除它可以保持代码的DRY。 - Ruben Bartelink

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接