我可以做到这个:
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警告。
我可以做到这个:
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警告。
我认为一些来到这里的人可能只是在寻找以下内容:
$ ./prog `python -c 'print "\x41\x42\x43"'`
$ ./prog `perl -e 'print "\x41\x42\x43"'`
$ ./prog `ruby -e 'print "\x41\x42\x43"'`
正如 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
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 Singhalcout
:P 我一开始考虑使用 u,但认为这会使问题变得过于复杂,因为提问者主要是想理解一个对他们来说不太明显的概念。 - Ruben BartelinkUCHAR_MAX == 255
或 LONG_MAX > UCHAR_MAX
等等。感谢您的编辑和出色的回答! - Alok Singhalunsigned char cTest = argv[1];
这是错误的,因为argv[1]
的类型是char *
。如果argv[1]
包含类似于"0xff"
的内容,并且您想将相应的整数值赋给unsigned char
,最简单的方法可能是使用strtoul()
先将其转换为unsigned long
,然后检查转换后的值是否小于或等于UCHAR_MAX
。如果是,则可以直接赋值给cTest
。
strtoul()
的第三个参数是一个基数,可以为0表示C风格的数字解析(八进制和十六进制字面量都允许)。如果您只想允许基数16,请将其作为第三个参数传递给strtoul()
。如果您想允许任何基数(以便您可以解析0xff
、0377
、255
等),请使用0。
UCHAR_MAX
在<limits.h>
中定义。
在C语言中,做这种事情的传统方法是使用scanf()。它与printf()完全相反,从文件(或终端)中读取给定格式的内容,并将其输入到您列出的变量中,而不是将它们写入其中。
在您的情况下,您将使用sscanf
,因为您已经将其放在字符串中而不是流中。
atoi
、atol
、strtoi
、strtol
都在 stdlib.h
中。
strtol
函数将无法工作。 - dirkgentlymbstol
或其他的多字节或宽字符(或TCHAR)变体会涵盖这个问题吗?(我也怀疑这不是一个关键问题,考虑到问题的性质。) - Ruben Bartelinkmain()
函数的第二个参数不是char **
类型吗?那就不符合标准了。 - Alok Singhal