我有这个示例代码,用于将32位整数转换为ip地址.
#includeint main() { unsigned int c ; unsigned char* cptr = (unsigned char*)&c ; while(1) { scanf("%d",&c) ; printf("Integer value: %u\n",c); printf("%u.%u.%u.%u \n",*cptr, *(cptr+1), *(cptr+2), *(cptr+3) ); } }
此代码为输入提供了错误的输出2249459722
.但当我更换
scanf("%d",&c) ;通过
scanf("%u",&c) ;输出结果是正确的.
PS:我知道inet_ntop
和inet_pton
.
我期待的答案不仅仅是建议那些答案.
你正在编写" 有罪 "的编码(制造一些迟早会伤害你的错误 - 大多数时间更早).首先,假设整数具有正确的字节序.在某些机器上,您将出错 - 无论是在Intel机器上还是在PowerPC或SPARC机器上.
一般来说,你应该显示你得到的实际结果,而不是仅仅说你得到了错误的结果; 你还应该显示预期的结果.这有助于人们调整您的期望.
这是我修改后的代码版本 - 它不是请求输入,而是假定您指定的值.
#includeint 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
在整数常量中添加后缀来删除它.
使用以下代码和上面显示的非常繁琐的编译器设置说明了查看问题的另一种方法:
#includestatic 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
.
因此,您的代码存在多个问题:
它假定一个特定的体系结构(little-endian而不是认识到big-endian也存在).
当使用启用了大量警告的编译器时,它不能完全编译 - 这是有充分理由的.
它不会检查可能失败的功能是否实际成功.
是的,测试sscanf()
是错误的.这就是为什么你有代码审查,以及为什么它有助于发布你正在测试的代码.
我现在有点困惑 - 得到一致的行为,我无法立即解释.通过明显的修订(在MacOS X 10.6.2,GCC 4.2.1,32位和64位编译上进行测试),我得到一个不太合理的答案.当我更加模块化地重写时,我得到了一个明智的答案.
+ cat yy.c #includestatic 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 #includestatic 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",很久以前!).
%d用于有符号整数
%u用于无符号整数
请按如下方式修改您的程序,看看您的输入是如何被真正解释的:
#includeint main() { unsigned int c ; unsigned char* cptr = (unsigned char*)&c ; while(1) { scanf("%d",&c) ; printf("Signed value: %d\n",c); printf("Unsigned value: %u\n",c); printf("%u.%u.%u.%u \n",*cptr, *(cptr+1), *(cptr+2), *(cptr+3) ); } }
如果提供的数字大于INT_MAX,则最左边的位为1.这表示它是一个带负值的有符号整数.然后将该数字解释为它的两个补码