Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/c/66.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
使用返回值是否会改变函数c的行为?_C_Return Value - Fatal编程技术网

使用返回值是否会改变函数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
开关一样,可能会发生变化