您正在编码“sinfully”(犯了许多错误,这些错误迟早会伤害到您——主要是更快)。首先,您假设整数具有正确的字节序。在某些机器上,你会错 - 无论是在 Intel 机器上,还是在 PowerPC 或 SPARC 机器上。
一般来说,你应该展示你得到的实际结果,而不是仅仅说你得到了错误的结果;您还应该显示预期的结果。这有助于人们调试您的期望。
这是我修改后的代码版本 - 它不是请求输入,而是简单地假定您指定的值。
#include <stdio.h>
int main(void)
{
unsigned int c = 2249459722;
unsigned char* cptr = (unsigned char*)&c;
printf("Integer value: %10u\n", c);
printf("Integer value: 0x%08X\n", c);
printf("Dotted decimal: %u.%u.%u.%u \n", *cptr, *(cptr+1), *(cptr+2), *(cptr+3));
return(0);
}
在我的 Mac(Intel,little-endian)上编译时,输出为:
Integer value: 2249459722
Integer value: 0x8614080A
Dotted decimal: 10.8.20.134
在我的 Sun (SPARC, big-endian) 上编译时,输出为:
Integer value: 2249459722
Integer value: 0x8614080A
Dotted decimal: 134.20.8.10
(在 SPARC 上使用 GCC 4.4.2,我收到警告:
xx.c:4: warning: this decimal constant is unsigned only in ISO C90
在 Mac 上使用 GCC 4.2.1 - 启用了许多警告 (gcc -std=c99 -pedantic -Wall -Wshadow -Wpointer-arith -Wstrict-prototypes -Wmissing-prototypes -Werror) - 我没有收到该警告,这很有趣。)我可以通过在整数常量中添加 U 后缀来删除它.
下面的代码和上面显示的极其繁琐的编译器设置说明了查看问题的另一种方法:
#include <stdio.h>
static void print_value(unsigned int c)
{
unsigned char* cptr = (unsigned char*)&c;
printf("Integer value: %10u\n", c);
printf("Integer value: 0x%08X\n", c);
printf("Dotted decimal: %u.%u.%u.%u \n", *cptr, *(cptr+1), *(cptr+2), *(cptr+3));
}
int main(void)
{
const char str[] = "2249459722";
unsigned int c = 2249459722;
printf("Direct operations:\n");
print_value(c);
printf("Indirect operations:\n");
if (sscanf("2249559722", "%d", &c) != 0)
printf("Conversion failed for %s\n", str);
else
print_value(c);
return(0);
}
编译失败(因为-Werror 设置)并显示以下消息:
cc1: warnings being treated as errors
xx.c: In function ‘main’:
xx.c:20: warning: format ‘%d’ expects type ‘int *’, but argument 3 has type ‘unsigned int *’
删除-Werror 设置并编译,但随后会显示您遇到的下一个问题 - 未检查可能失败的函数的错误指示:
Direct operations:
Integer value: 2249459722
Integer value: 0x8614080A
Dotted decimal: 10.8.20.134
Indirect operations:
Conversion failed for 2249459722
基本上,sscanf() 函数报告它未能将字符串转换为有符号整数(因为该值太大而无法容纳 - 请参阅 GCC 4.4.2 的警告),但您的代码没有检查从sscanf() 返回错误,因此您使用的是当时恰好留在c 中的任何值。
所以,您的代码存在多个问题:
- 它假定一个特定的架构(小端而不是承认大端也存在)。
- 在使用启用了很多警告的编译器时,它编译不干净 - 这是有充分理由的。
- 它不会检查可能失败的函数实际上是否成功。
阿洛克的评论
是的,sscanf() 上的测试是错误的。这就是您进行代码审查的原因,也是发布您正在测试的代码的原因。
我现在有点困惑 - 得到我无法立即解释的一致行为。通过明显的修订(在 MacOS X 10.6.2、GCC 4.2.1、32 位和 64 位编译上进行测试),我得到了一个不太理智的答案。当我更模块化地重写时,我得到了一个理智的答案。
+ cat yy.c
#include <stdio.h>
static void print_value(unsigned int c)
{
unsigned char* cptr = (unsigned char*)&c;
printf("Integer value: %10u\n", c);
printf("Integer value: 0x%08X\n", c);
printf("Dotted decimal: %u.%u.%u.%u \n", *cptr, *(cptr+1), *(cptr+2), *(cptr+3));
}
int main(void)
{
const char str[] = "2249459722";
unsigned int c = 2249459722;
printf("Direct operations:\n");
print_value(c);
printf("Indirect operations:\n");
if (sscanf("2249559722", "%d", &c) != 1)
printf("Conversion failed for %s\n", str);
else
print_value(c);
return(0);
}
+ gcc -o yy.32 -m32 -std=c99 -pedantic -Wall -Wshadow -Wpointer-arith -Wstrict-prototypes -Wmissing-prototypes yy.c
yy.c: In function ‘main’:
yy.c:20: warning: format ‘%d’ expects type ‘int *’, but argument 3 has type ‘unsigned int *’
+ ./yy.32
Direct operations:
Integer value: 2249459722
Integer value: 0x8614080A
Dotted decimal: 10.8.20.134
Indirect operations:
Integer value: 2249559722
Integer value: 0x86158EAA
Dotted decimal: 170.142.21.134
我对值 170.142.21.134 没有很好的解释;但目前它在我的机器上是一致的。
+ gcc -o yy.64 -m64 -std=c99 -pedantic -Wall -Wshadow -Wpointer-arith -Wstrict-prototypes -Wmissing-prototypes yy.c
yy.c: In function ‘main’:
yy.c:20: warning: format ‘%d’ expects type ‘int *’, but argument 3 has type ‘unsigned int *’
+ ./yy.64
Direct operations:
Integer value: 2249459722
Integer value: 0x8614080A
Dotted decimal: 10.8.20.134
Indirect operations:
Integer value: 2249559722
Integer value: 0x86158EAA
Dotted decimal: 170.142.21.134
相同的值 - 即使是 64 位而不是 32 位。也许问题在于我试图解释未定义的行为,这在定义上或多或少是无法解释的(无法解释的)。
+ cat xx.c
#include <stdio.h>
static void print_value(unsigned int c)
{
unsigned char* cptr = (unsigned char*)&c;
printf("Integer value: %10u\n", c);
printf("Integer value: 0x%08X\n", c);
printf("Dotted decimal: %u.%u.%u.%u \n", *cptr, *(cptr+1), *(cptr+2), *(cptr+3));
}
static void scan_value(const char *str, const char *fmt, const char *tag)
{
unsigned int c;
printf("Indirect operations (%s):\n", tag);
fmt = "%d";
if (sscanf(str, fmt, &c) != 1)
printf("Conversion failed for %s (format %s \"%s\")\n", str, tag, fmt);
else
print_value(c);
}
int main(void)
{
const char str[] = "2249459722";
unsigned int c = 2249459722U;
printf("Direct operations:\n");
print_value(c);
scan_value(str, "%d", "signed");
scan_value(str, "%u", "unsigned");
return(0);
}
像这样使用函数参数意味着 GCC 无法再发现伪造的格式。
+ gcc -o xx.32 -m32 -std=c99 -pedantic -Wall -Wshadow -Wpointer-arith -Wstrict-prototypes -Wmissing-prototypes xx.c
+ ./xx.32
Direct operations:
Integer value: 2249459722
Integer value: 0x8614080A
Dotted decimal: 10.8.20.134
Indirect operations (signed):
Integer value: 2249459722
Integer value: 0x8614080A
Dotted decimal: 10.8.20.134
Indirect operations (unsigned):
Integer value: 2249459722
Integer value: 0x8614080A
Dotted decimal: 10.8.20.134
这里的结果是一致的。
+ gcc -o xx.64 -m64 -std=c99 -pedantic -Wall -Wshadow -Wpointer-arith -Wstrict-prototypes -Wmissing-prototypes xx.c
+ ./xx.64
Direct operations:
Integer value: 2249459722
Integer value: 0x8614080A
Dotted decimal: 10.8.20.134
Indirect operations (signed):
Integer value: 2249459722
Integer value: 0x8614080A
Dotted decimal: 10.8.20.134
Indirect operations (unsigned):
Integer value: 2249459722
Integer value: 0x8614080A
Dotted decimal: 10.8.20.134
这些与 32 位的情况相同。我正式感到困惑。主要观察结果仍然准确 - 小心,注意编译器警告(并引发编译器警告),不要假设“全世界都在英特尔芯片上运行”(它曾经是“不要假设全世界都是VAX”,很久以前!)。