scanf(“d”,i)的工作情况如何?
考虑一下这个代码片段(不是使用scanf(%d,&i),而是故意使用scanf(“%d”,i)) 这里我没有初始化,代码运行时带有警告,扫描输入并将垃圾值作为输出 但是如果我用某个值初始化,它就会扫描输入,程序就会崩溃scanf(“d”,i)的工作情况如何?,c,scanf,C,Scanf,考虑一下这个代码片段(不是使用scanf(%d,&i),而是故意使用scanf(“%d”,i)) 这里我没有初始化,代码运行时带有警告,扫描输入并将垃圾值作为输出 但是如果我用某个值初始化,它就会扫描输入,程序就会崩溃 scanf()在这两种情况下的工作情况如何 您使用无效参数调用scanf。而不是 scanf("%d", i); 一定有 scanf("%d", &i); 该函数将提取的数字写入&i所寻址的对象。我假设您使用了正确的#include,只是没有提及它 在这种情况下,只有
scanf()在这两种情况下的工作情况如何 您使用无效参数调用scanf。而不是
scanf("%d", i);
一定有
scanf("%d", &i);
该函数将提取的数字写入&i所寻址的对象。我假设您使用了正确的
#include
,只是没有提及它
在这种情况下,只有一行错误:
scanf("%d", i); // This line is bad
这里有两种调用方法:
i
,即使从未分配它,也会调用UB,因为它属于自动存储类(堆栈已分配),并且您从未获取它的地址(它符合寄存器的条件)
int
,其中scanf
期望int*
会自动导致UB。参数应该是&i
如果它“似乎有效”,你知道你在那里潜在地破坏什么吗
i
将删除UB的第一个原因,但不是第二个原因
旁白:您应该始终假设输入可能失败,因此请检查返回值
顺便说一句:如果您检查一下,编译器可以而且确实利用UB来修剪整个执行分支,这将导致“不可能,无需担心”。这是他们用来缩小代码大小并获得最佳性能的常用技巧之一
如果你想知道你的编译器是什么,你今天使用的版本,以及你给它的代码和选项,看看反汇编或者要求一个汇编清单。尽管不要得出任何关于它“无害”的结论。因此,您以两种方式调用了undefined behavior1:
int i;
scanf("%d", i);
首先,%d
转换说明符希望其相应的参数具有类型int*
。表达式i
的类型为int
;由于这与预期类型不匹配,因此行为未定义。如果sizeof(int*)!=sizeof(int)
,以及其他问题
其次,由于i
未初始化,它将包含一些intederminate值,scanf
将其解释为指针值,并尝试将输入写入该无效地址(该地址不是i
的地址)。在这种特殊情况下,写入无效地址不会立即导致segfault(尽管它会删除某些内容;这些内容是否重要是一个悬而未决的问题)。注意,i
未被修改;它仍然包含与开始时相同的不确定值
在第二种情况下,无论您将i
初始化为什么,都可能解析为受限地址,这就是崩溃的原因
不管怎么说,坏朱珠
1.其中,未定义行为的定义是“在使用不可移植或错误的程序结构或错误数据时,[C语言标准]未对其提出要求的行为”(,3.4.3)。编译器不需要发出诊断或暂停转换,也不需要生成执行任何特定操作的代码。代码可能会崩溃,可能会损坏数据,可能会使程序处于错误状态,或者看起来工作正常。这并不是因为它看起来工作正常。这是未定义的行为。随意的。调用UB会破坏最好的一天。
scanf
可能会将i
的垃圾值作为指针,并尝试在指定位置存储int
值。如果幸运的话,程序会崩溃(崩溃是告诉你有严重错误的一种方式)。如果你运气不好,那就不是了。如果它没有崩溃,i
将保留调用scanf
之前的垃圾值。当您初始化i
时,您可能给它一个更可能被解释为无效地址的值。顺便说一句,#include
不是可选的。@user3121023我知道scanf的格式,而且我必须给出变量的地址。我只是想试试这个。有三个人想要接近,因为没有复制或打字错误。这两种情况都不确定。所以第二种情况下的崩溃和第一种情况下的崩溃只是因为我的编译器,我会在不同的编译器上得到不同的结果?@ABJK:可能会在不同的实现、不同的日期或不同的选项上得到不同的输出,或者仅仅因为。不过也不能保证。谢谢你回答了准确的问题。@ABJK另外,不要在现代代码中使用scanf()
。使用getc()
或fgets()
绑定到诸如strtol()
之类的解析器来获取用户输入。OP说他使用scanf
时没有&
故意。@Cool Guy一如既往地编辑他的帖子。
int i;
scanf("%d", i);