使用返回值是否会改变函数c的行为?
我有这段代码使用返回值是否会改变函数c的行为?,c,return-value,C,Return Value,我有这段代码 #include <stdio.h> int main() { int i; scanf("%d", &i); printf("%x", i); } #包括 int main(){ int i; scanf(“%d”、&i); printf(“%x”,i); } 当我输入字符“a”时,它会在输出中吐出一些随机数,如“73152c”或“66152c”等。 但是当我把代码改成这个的时候 #inclu
#include <stdio.h>
int main() {
int i;
scanf("%d", &i);
printf("%x", i);
}
#包括
int main(){
int i;
scanf(“%d”、&i);
printf(“%x”,i);
}
当我输入字符“a”时,它会在输出中吐出一些随机数,如“73152c”或“66152c”等。但是当我把代码改成这个的时候
#include <stdio.h>
int main() {
int i;
int j = scanf("%d", &i);
printf("%x %d", i, j);
}
#包括
int main(){
int i;
int j=scanf(“%d”和&i);
printf(“%x%d”,i,j);
}
对于相同的输入,输出将始终为“2 0”。那么,使用函数的返回值是否会改变其行为?
我将windows 10 64位与gcc 8.1.0一起使用,编译时不使用任何开关。
scanf
返回成功转换和分配的项目数,或在文件结束或出现错误时返回EOF
%d
转换说明符告诉scanf
跳过任何前导空格,然后读取十进制数字直到第一个非数字字符;该非数字字符保留在输入流中,已读取的任何数字都将转换为整数值并分配给i
如果第一个非空白字符不是数字,则scanf
立即停止读取,返回0
以指示未转换和分配任何内容,i
保持不变
在第一个代码段中,您在阅读它之前没有初始化i
,并且它没有被更新,这就是为什么输出看起来是随机的。从技术上讲,您已经通过使用该不确定值调用了未定义的行为。未定义行为的一个可能结果是每次都得到不同的结果
在第二个代码段中,
j
获取scanf
的返回值,即0
。至于为什么i
总是设置为2
,同样,由于您试图使用该不确定值,因此行为是未定义的。未定义行为的另一个可能结果是每次都会得到相同的结果,没有明显的原因。实际上,scanf的行为没有改变。。您可以通过包括这一行,printf(“%x”,i)来符合这一点代码>扫描前。在这里,当我使用j时,我的编译器将i初始化为2。否则我将不会被初始化,并将有一些随机垃圾值。
我不认为输入错误类型的输入是未定义的行为-如果我错了,请纠正我。相反,它保持对应的和后续指针不变,并立即从scanf返回。我运行了多次,每次I的值都保持不变。
现在令人惊讶的是,当j被声明时,为什么我总是被初始化为2 使用godbolt.org检查GCC 8.1.0生成的汇编代码(无开关),以下是第一个程序中main
例程的汇编代码:
push-rbp
mov rbp,rsp
副区长,16
lea rax,[rbp-4]
移动rsi,rax
移动edi,偏移平面:.LC0
mov-eax,0
呼叫uuu isoc99\u scanf
mov eax,德沃德PTR[rbp-4]
电影esi,eax
移动edi,偏移平面:.LC1
mov-eax,0
调用printf
mov-eax,0
离开
ret
下面是第二个程序的代码:
push-rbp
mov rbp,rsp
副区长,16
lea rax,[rbp-8]
移动rsi,rax
移动edi,偏移平面:.LC0
mov-eax,0
呼叫uuu isoc99\u scanf
mov DWORD PTR[rbp-4],eax
mov eax,德沃德PTR[rbp-8]
mov edx,德沃德PTR[rbp-4]
电影esi,eax
移动edi,偏移平面:.LC1
mov-eax,0
调用printf
mov-eax,0
离开
ret
它们在两个地方有所不同。在第一个指令中,此指令将i
的地址传递给scanf
:
lea-rax[rbp-4]
在第二种情况下,这是一种指示:
lea-rax[rbp-8]
这些是不同的,因为在第二个程序中,编译器在堆栈上包含了j
的空间。无论出于何种原因,它决定将j
放在rbp-4
上,这是第一个程序中用于i
的空间。这将i
跳到rbp-8
然后,第一个程序将i
传递到printf
的位置的代码不同:
lea-rax[rbp-8]
第二次通过i
和j
:
mov eax,DWORD PTR[rbp-8]
mov edx,德沃德PTR[rbp-4]
现在我们了解了为什么您的程序为i
打印不同的内容。在第一个程序中,由于从未将值放入i
(因为scanf
在输入包含字母“a”时不对%d
赋值),因此当main
启动时,您的程序将打印[rbp-4]
中发生的任何数据。在第二个程序中,您的程序将打印[rbp-8]
中发生的任何内容
这些堆栈位置中的内容是调用为main
运行的启动代码中剩余的内容。这是设置C环境的特殊启动代码。它可能与程序中的地址有关,程序加载器在每次执行时都会故意将程序中的某些地址随机化,以抵御攻击者。(有关更多信息,请查看。)当启动代码完成时,它会在[rbp-4]
中留下一些地址,在[rbp-8]
中留下零。因此,第一个程序为i
打印一些地址,第二个程序为零
因此,这种情况下的差异不是由于使用或不使用scanf
的返回值造成的。它们是由变量的多寡引起的,从而改变了堆栈中的内容放置位置
如果您升级了C实现,并且使用了不同版本的启动代码,或者编译器生成了不同的代码,那么这种情况当然会发生变化。在编译器中启用优化,就像使用-O3
开关一样,可能会发生变化