+ -
当前位置:首页 → 问答吧 → 探讨一个scanf问题

探讨一个scanf问题

时间:2009-01-31

来源:互联网

gcc得不到想要的结果。
引用:
#include <stdio.h>
int main(void)
{
int c, rc;
rc = scanf("%c", &c);
int i=-1;
printf("%d, %c", c , c);
return 0;
}
gentoo的gcc 4.1和4。3版本都输出了不应该的结果。在一个debian的机器上,4。2。3版本,结果同样。但是在一个ubuntu的机器上,用的是4。3。2版本,输出了预期的结果,百思不得其解。如果把int i=-1(最可靠的办法就是初始化c);这行给去掉,就可以得到预期的结果(如果在x86_64编译器上用-m32编译32bit版本,结果也有问题)。
看起来是,是scanf函数不允许(把char型,输入到int型)?但是为什么有的gcc版本可以呢?为什么 int i=-1;会干扰输出?

作者: blackwhite   发布时间: 2009-01-31

不应该的结果啥样子?

那行打印语句应为
代码:
printf("%d, %c", c&0xff , c);
因为只读了一个字符,所以前三个字节都是垃圾。跟变量 i 没关系。

作者: biinn   发布时间: 2009-01-31

标准没有规定的行為是不确定的
就像 int a, b = 0;
a = b++ + ++b + b++;
一样
没有标准答案或者预期结果

作者: 8pm   发布时间: 2009-01-31

这个程序是从一个书上的几十行的例子来的。发现有问题,调试到最后调试的结果就是上面的例子了。我可以理解认为c有部分字节没有初始化,如果初始化了,就没有这个问题了。但是问题是为什么后的int i=-1;会对结果产生干扰?,有和没有结果不一样?

作者: blackwhite   发布时间: 2009-01-31

引用:
作者: 8pm
标准没有规定的行為是不确定的
就像 int a, b = 0;
a = b++ + ++b + b++;
一样
没有标准答案或者预期结果
这个仅仅是编译器依赖,对同一个编译器结果是肯定的。

作者: blackwhite   发布时间: 2009-01-31

引用:
作者: blackwhite
这个仅仅是编译器依赖,对同一个编译器结果是肯定的。
引用:
作者: blackwhite
这个程序是从一个书上的几十行的例子来的。发现有问题,调试到最后调试的结果就是上面的例子了。我可以理解认为c有部分字节没有初始化,如果初始化了,就没有这个问题了。但是问题是为什么后的int i=-1;会对结果产生干扰?,有和没有结果不一样?
虽然 C99 可以让你用之前才定义变量,但是标准没规定具体编译器怎么实现
例如是否需要一早就预留位置
在这个例子,用 -S 编译成汇编就可以看出那里不一样,leaq -8(%rbp), %rsi,和 leaq -12(%rbp), %rsi 的区别。
但是标准没定义,GCC 也没有承诺一定会这样实现。

所以我还是那句话,标准没规定的东西,没有肯定的结果。即便是你说的所謂的“编译器依赖”,不能保证一样结果就没有深究的必要

作者: 8pm   发布时间: 2009-01-31

谢谢指教。汇编那里没有看明白(因为不懂)。

作者: blackwhite   发布时间: 2009-01-31

c没有初始化,你就不能预期当你只写入了它的一个字节之后剩下那三个字节是什么,如果编译器觉得舒服它便可以:如果你初始化i那么那三个字节就是0xffffff如果你没有初始化i那么那三个字节是0x000000(当然编译器也可以有任意的其他选择就是举个例子)

作者: Etrnls   发布时间: 2009-01-31

引用:
作者: blackwhite
谢谢指教。汇编那里没有看明白(因为不懂)。
不敢不敢,讨论而已,如果有不恰当的言语,还望多包涵。

leaq 指令就是 lea 指令的 64-bit 版(我用的是 x86_64)

作者: 8pm   发布时间: 2009-01-31

引用:
作者: blackwhite
这个仅仅是编译器依赖,对同一个编译器结果是肯定的。
不一定是编译器依赖,scanf的%参数和后面给的指针必须严格一致,否则结果是不可预知的。具体的处理方式也可能是看库函数,当然编译器有可能把类似的函数直接转换成指令,这时就变成了编译器依赖了,否则就是标准库依赖而不是编译器依赖。

违反函数对参数的要求去调用函数,并依赖其“正确”结果本来就是撞大运,测试其在不同编译器下的结果对于学习语言确实有研究价值,但在实用中根本不应当这样写程序。

作者: poet   发布时间: 2009-01-31

引用:
作者: blackwhite
这个程序是从一个书上的几十行的例子来的。发现有问题,调试到最后调试的结果就是上面的例子了。我可以理解认为c有部分字节没有初始化,如果初始化了,就没有这个问题了。但是问题是为什么后的int i=-1;会对结果产生干扰?,有和没有结果不一样?
初始化了照样有问题,scanf的类型必须严格一致。你把程序放到大端的架构上运行就挂了

作者: richardpku   发布时间: 2009-01-31

引用:
作者: blackwhite
这个仅仅是编译器依赖,对同一个编译器结果是肯定的。
那可未必。这个是 undefined behavior,你说的情况那叫 implementation-defined behavior。

代码:
xxx@desktop /tmp $ cat a.c
#include <stdio.h>

int main()
{
 int a;
 scanf ("%d", &a);
 int b = a++ + ++a + a++ + a++;
 printf ("%d\n", b);
 return 0;
}
xxx@desktop /tmp $ gcc a.c
xxx@desktop /tmp $ ./a.out <<< 2
11
xxx@desktop /tmp $ gcc -O a.c
xxx@desktop /tmp $ ./a.out <<< 2
12
xxx@desktop /tmp $ gcc -v
Using built-in specs.
Target: x86_64-pc-linux-gnu
Configured with: /var/tmp/portage/sys-devel/gcc-4.2.4/work/gcc-4.2.4/configure --prefix=/usr --bindir=/usr/x86_64-pc-linux-gnu/gcc-bin/4.2.4 --includedir=/usr/lib/gcc/x86_64-pc-linux-gnu/4.2.4/include --datadir=/usr/share/gcc-data/x86_64-pc-linux-gnu/4.2.4 --mandir=/usr/share/gcc-data/x86_64-pc-linux-gnu/4.2.4/man --infodir=/usr/share/gcc-data/x86_64-pc-linux-gnu/4.2.4/info --with-gxx-include-dir=/usr/lib/gcc/x86_64-pc-linux-gnu/4.2.4/include/g++-v4 --host=x86_64-pc-linux-gnu --build=x86_64-pc-linux-gnu --disable-altivec --disable-nls --with-system-zlib --disable-checking --disable-werror --enable-secureplt --enable-multilib --disable-libmudflap --disable-libssp --disable-libgcj --enable-languages=c,c++,treelang,fortran --enable-shared --enable-threads=posix --enable-__cxa_atexit --enable-clocale=gnu
Thread model: posix
gcc version 4.2.4 (Gentoo 4.2.4 p1.0)

作者: richardpku   发布时间: 2009-01-31

楼主说的太诡异了!
int i=-1;会有影响?这一句完全可以删掉的,难道真的会有影响?!

楼主预期的结果的什么,不妨也写出来,便于对比。
你的程序我编译了,输入同一个参数,每次结果都不同。(icc,gcc)

yf@qv ~ $ ./a.out
128
-1081859279, 1
yf@qv ~ $ ./a.out
128
-1082095055, 1
yf@qv ~ $ ./a.out
3
-1081318605, 3
yf@qv ~ $ ./a.out
3
-1075771341, 3
yf@qv ~ $ ./a.out
3
-1080130509, 3
yf@qv ~ $ ./a.out
AA
-1076881599, A
yf@qv ~ $ ./a.out
A
-1080507327, A
yf@qv ~ $ ./a.out
A
-1081625791, A


应该说是你的编程风格不好,里面包含陷阱。

int i=-1;注释掉和不注释都试了。

#include <stdio.h>
#include<stdlib.h>
int main(void)
{
int c, rc;
rc = scanf("%c", &c);
//int i=-1;
printf("%d, %c\n", c , c);
//return 0;
exit(0);
}

作者: quantumfang   发布时间: 2009-01-31

拜托,楼上的。
把大家的帖子看懂了再说话。

作者: biinn   发布时间: 2009-01-31

谢谢大家讨论。
这个程序是我从一个那个C programming Language的习题答案上来的简化过来有问题的code。
这个程序出问题的时候,我看了man scanf,man里边是说了前后类型要一致。它用的是must be。
在不同的机器上,不同版本的机器上,反正结果不可以预测。后边有没有其它的操作也会影响到结果。
12楼给出的例子,是个很好的例子,但是我想这个例子更可以说明的是,程序优化,也可能会让结果变化。上次gcc的那个abs bug,就是优化的结果完全错误。

作者: blackwhite   发布时间: 2009-02-01

引用:
作者: blackwhite
谢谢大家讨论。
这个程序是我从一个那个C programming Language的习题答案上来的简化过来有问题的code。
这个程序出问题的时候,我看了man scanf,man里边是说了前后类型要一致。它用的是must be。
在不同的机器上,不同版本的机器上,反正结果不可以预测。后边有没有其它的操作也会影响到结果。
12楼给出的例子,是个很好的例子,但是我想这个例子更可以说明的是,程序优化,也可能会让结果变化。上次gcc的那个abs bug,就是优化的结果完全错误。
abs那个是gcc bug。 这里是undefined behavior,根本不是一回事……

作者: richardpku   发布时间: 2009-02-01

引用:
作者: biinn
拜托,楼上的。
把大家的帖子看懂了再说话。
我说的是楼主说的其中一点:int i=-1;是否有影响?

当然我知道大多数砖家说的是非标准代码产生不确定结果

楼上的批评我不知所云,那我反问你别人的答复跟我的观点有关系吗?

貌似有点关系?:非标准的代码不是好的风格。

本人不是程序员,没有编过很多,很大的程序,但是有点体会:代码里面有时候陷阱重重,其实好多陷阱都是自己设的。事后恍然大悟,根本不是陷阱。那又是什么?自问吧

所以我强调的是好的风格

作者: quantumfang   发布时间: 2009-02-01

引用:
作者: quantumfang
楼上的批评我不知所云,那我反问你别人的答复跟我的观点有关系吗?
你至少连 lz 的帖子都没仔细看。lz 的代码是从书中看到的,可你却说 lz 的编程风格不好。那代码根本不是他写的。

至于你回帖中的其他内容,我不想说什么了。

作者: biinn   发布时间: 2009-02-01