【问题标题】:What will happen if '&' is not put in a 'scanf' statement?如果 '&' 没有放在 'scanf' 语句中会发生什么?
【发布时间】:2016-04-06 01:25:24
【问题描述】:

我参加了一次面试,我被问到这个问题:

您对以下内容有何看法?

int i;
scanf ("%d", i);
printf ("i: %d\n", i);

我回复了:

  • 程序将编译成功。
  • 它会打印错误的数字,但会一直运行到最后 不会崩溃

我的回答是错误的。我不知所措。

在那之后他们解雇了我:

程序在某些情况下会崩溃并导致核心转储。

我不明白为什么程序会崩溃?谁能解释一下原因?任何帮助表示赞赏。

【问题讨论】:

  • 任何体面的编译器都会抱怨scanf 期望指针的方式
  • 请不要回滚这些标签编辑。出于某种原因,该标签已被删除。
  • @BradLarson,好吧,我不会这样做。起初我认为那个标签适合这个问题。你能告诉我标签被删除的原因吗?
  • 此链接是在编辑原因之一中提供的:meta.stackoverflow.com/questions/256592/…

标签: c scanf stdio


【解决方案1】:

定义变量后,编译器会为该变量分配内存。

int i;  // The compiler will allocate sizeof(int) bytes for i

上面定义的i 没有初始化,值不确定。

要将数据写入分配给i 的内存位置,您需要指定变量的地址。声明

scanf("%d", &i);

将用户写入int数据到分配给i的内存位置。

如果& 没有放在i 之前,那么scanf 将尝试将输入数据写入内存位置i 而不是&i。由于i 包含不确定的值,因此它可能包含与内存地址值相等的值,或者它可能包含超出内存地址范围的值。

在任何一种情况下,程序都可能会出现异常行为并导致未定义的行为。在那种情况下,任何事情都可能发生。

【讨论】:

  • 几乎可以保证你会崩溃,因为你会在运行它的任何系统上遇到内存保护错误或同等情况。当然,可能会发生其他事情,具体取决于程序的大小、分配的内存等,但我的钱花在了系统/应用程序崩溃上。
  • @sfdcfox 我的钱花在了编译器上,注意到未定义的行为并将相应的代码块视为不可访问(= 从代码中删除它并重定向通向它的任何代码路径)。
  • @JanDvorak 我已经看到了编译器可以有效地“删除”之前提到的 UB 的想法,但是您知道我们可以看到它在行动中的示例吗?听起来很难相信——这就是为什么我很确定它会是真的,因为这毕竟是 C++。 ;-) 具体来说,您的意思是可能无法达到不明智的scanf,因此无法要求用户输入?我不知道“好像”会产生可观察到的副作用,但我想该规则不适用于 UB,就像其他所有规则一样。
  • @JanDvorak:如果编译器会以这种方式“禁用”所有 UB 代码,那么大量非常常见的应用程序将突然停止运行......
  • @sfdcfox:您可能假设存在内存保护。在桌面领域之外,有相当多的系统运行时没有——其中 UB 的意思是“此应用程序以及当前在内存中的所有其他应用程序的未定义行为,包括操作系统”。这些系统在从main()返回之前对内存的反应相当差,而不是free()d...
【解决方案2】:

因为它会调用未定义的行为。当找到"%d" 说明符时,scanf() 系列函数需要一个指向整数的指针。您正在传递一个整数,该整数可以解释为某个整数的地址,但事实并非如此。这在标准中没有定义的行为。它确实会编译(会发出一些警告),但它肯定会以意想不到的方式工作。

在代码中,还有另一个问题。 i 变量永远不会被初始化,所以它会有一个不确定的值,这是 Undefined Behavior 的另一个原因。

请注意,标准并没有说明当您传递给定类型而预期其他类型时会发生什么,无论您交换什么类型,这只是未定义的行为。但是这种特殊情况属于特殊考虑,因为指针可以转换为整数,尽管只有在转换回指针并且整数类型能够正确存储值时才定义行为。这就是它编译的原因,但它肯定不能正常工作。

【讨论】:

  • 但是在我上大学的时候,在学习Spirit of C的时候,有这样一句话:scanf ("%s", name);
  • ^ 因为name 很可能是一个字符数组,并且数组的名称等于该数组中第一个元素的地址。
  • 那是非常不同的。当然name 是一个数组,并且数组会自动转换为指向其第一个元素的指针。另外,char name[100]; scanf("%99s", name); 会更安全。
  • 所以,基本上你的意思是,scanf 将尝试将值存储在变量 i 的位置。
  • 虽然你的评论不是很清楚,但我想你确实明白了是的,就是这样。
【解决方案3】:

您向scanf() 传递了类型错误的数据(应为int*,但传递了int)。这将导致未定义的行为

未定义的行为可能会发生任何事情。程序可能会崩溃,也可能不会崩溃。

在典型的环境中,我猜当一些指向操作系统不允许写入的位置的“地址”传递给scanf()时,程序会崩溃,并且写入那里会有操作系统终止应用程序,它会被观察为崩溃。

【讨论】:

    【解决方案4】:

    其他答案尚未提及的一件事是,在某些平台上,sizeof (int) != sizeof (int*)。如果参数以某种方式传递*,scanf 可能会吞噬另一个变量或返回地址的一部分。更改返回地址很可能会导致安全漏洞。

    * 我不是汇编语言专家,所以对此持保留态度。

    【讨论】:

      【解决方案5】:

      我不明白为什么程序会崩溃?谁能解释一下原因。任何帮助表示赞赏。

      也许应用得更多:

      int i = 123;
      scanf ("%d", &i);
      

      使用第一个命令,您为一个整数值分配内存并在此内存块中写入 123。对于这个例子,假设这个内存块的地址是0x0000ffff。使用第二个命令,您读取输入并 scanf 将输入写入内存块 0x0000ffff - 因为您没有访问(取消引用)此变量 i 的值,但它是地址。

      如果您使用命令scanf ("%d", i); 而不是将输入写入内存地址 123(因为这是存储在此变量中的值)。显然,这可能会出现严重错误并导致崩溃。

      【讨论】:

      • 谢谢。 +1 很好的解释。 (也是取自The Martian的好名字。
      【解决方案6】:

      由于scanf中没有&(和号)(按照标准的要求),所以只要我们输入值,程序就会突然终止,不管程序中再写多少行代码。

      -->> 我执行并在代码块中找到了。

      如果我们在 turbo c 编译器中运行相同的程序,那么即使是在 scanf 之后的所有行,它也会完美运行,但唯一的是,我们知道 i 打印的值将是垃圾。

      结论:- 由于在某些编译器会运行,而在某些编译器不会运行,所以这不是一个有效的程序。

      【讨论】:

      • 正如其他一些答案所解释的那样,虽然程序确实不正确并且可能会立即终止,但我们不能肯定地说。它有可能成功地将扫描的整数写入不确定的位置,然后继续。
      猜你喜欢
      • 1970-01-01
      • 2011-03-19
      • 2022-08-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-01-17
      • 1970-01-01
      相关资源
      最近更新 更多