【问题标题】:Interview Question面试题
【发布时间】:2011-05-15 06:23:02
【问题描述】:

昨天面试时被问到以下代码的输出

#include <stdio.h>
int main(void){
       printf ("%x" ,-1<<4); 
}

我有 2 分钟的时间说出答案。我回复了fffffff0。面试结果尚未公布。我想知道我的答案是否正确?

【问题讨论】:

  • 这不取决于有符号整数的表示吗?
  • 嗯,有一种方法可以找出答案。编译运行代码,傻逼!
  • @cdhowie - 如果你在开玩笑,这很有趣,但对 OP 来说可能并不完全清楚。如果你不是,那就太丢脸了。
  • 除了 -1
  • @Tony: main 不需要返回语句。见stackoverflow.com/questions/2637671

标签: c++ c


【解决方案1】:

从技术上讲,将负整数左移会调用未定义行为。这意味着-1&lt;&lt;4 是UB。我不知道他们为什么问你这个问题。可能他们想测试您对 C 和 C++ 标准的了解程度。

C99 [6.5.7/4] 说

E1 E2,减少模 比结果类型中可表示的最大值大一。如果 E1 有一个签名 类型和非负值,并且 E1× 2E2 在结果类型中是可表示的,那么就是 结果值; 否则,行为未定义

C++03 使 omitting the relevant text 的行为未定义。

【讨论】:

  • 要么他们想测试知识深度,要么他们根本不知道。我参加过多次面试,他们实际上并不知道技术问题的正确答案。通常情况下,尤其是早期阶段,面试官是 HR 或管理层。据我所知,在技术方面非常精通这种有点困难的方式对成为管理人员毫无帮助……甚至会损害你的机会。而人力资源……这根本不是他们的领域。像这样的技巧问题经常被最不了解的人提出......根据我的经验。
  • @Noah:是的 - 我记得在被问及 unsigned char 和朴素的char。但那是技术主管,而不是人力资源或管理层。
  • 我需要检查一下,但我对 C 标准的理解是,将 by 移动一个负值确实是未定义的,但是移动 a 负值是“实现定义的”,即它不必在所有平台上都相同,但需要定义。无论如何,在任何最近的平台上,-1
  • @Jean-Marc:Prasoons 的回答直接来自 C99 标准。左移负值是未定义的行为。但是,如果 right 移动负值,则结果是实现定义的。
  • Prasoon: litb 对您的“省略相关文本”链接的评论让我认为有足够的空间将 sn-p 解释为意味着左移负整数是 实现-定义,而不是UB。只是说。
【解决方案2】:

没有。你不正确。这就是坏消息。好消息是,面试官可能不知道这一点,并假设你是,因为这是他们编译和运行它时得到的结果。

真正的答案是它是由实现定义的。由于过载,我不是 100% 有信心说这是未定义的行为,但我认为可能是。至少,尽管结果取决于负数的表示方式等...您声称这两种语言都没有定义输出将是什么。

【讨论】:

  • ...我说我认为是的。
  • “因为过载”?您的意思是用cc++ 标记问​​题的“重载”吗?如果是这样:你可能选择了一个更模棱两可的术语吗? :-P
【解决方案3】:

在我的机器上:

chris@zack:~$ cat > test.c
#include <stdio.h>
int main(void){
       printf ("%x" ,-1<<4);
}

chris@zack:~$ gcc -o test test.c && ./test
fffffff0

但是,结果将取决于您的架构和编译器。所以正确的答案是“它可以输出任何东西。”

【讨论】:

  • 无关紧要,但很有趣。尽管如此,我的回答没有任何不正确的地方。
  • 不抱怨积分(反正我是有上限的,我可以不在乎)但是当有人投反对票时,我通常希望他们留下反馈,说明原因,这样我就有机会改进我的答案。
  • 我看不出编译器输出是如何无关紧要的。我认为它比引用标准更相关。尤其是当您为任何人关心的每个编译器都获得相同的输出时。
  • 所以我给了你这个反馈。别抱怨了。
  • @PigBen 编译器输出无关紧要,因为它是未定义的行为,并且结果可能会在 &lt;insert name of your favorite compiler here&gt; 的下一版本中发生变化
【解决方案4】:
Binary of 1  : 0000 0000 0000 0000 0000 0000 0000 00001

将出现的 0 替换为 1,因为您要计算负数的二进制数

How to calculate binary of negative numbers

Binary of -1 : 1111 1111 1111 1111 1111 1111 1111 11111

Left shift 4 : 1111 1111 1111 1111 1111 1111 1111 0000

结果左移 4 的十六进制表示 会是

1111 : F 

0000 : 0

所以计算的输出将是:

FFFFFFF0

你的答案是对的。

【讨论】:

  • 谁说机器必须使用二进制补码?
【解决方案5】:

对于一般情况来说,左移一个负数是未定义的,但我们必须理解为什么这种未定义的行为(UB)?请记住,最高有效位 (MSb) 是符号位。如果该位为 1,则该数字为负数。如果它为零,则该数字为正数。这是关键信息在第一次左移时丢失。例如

-32768<<4

是一样的
0x8000<<4

(为简单起见假设一台 16 位机器)

结果当然是 0,它没有任何意义,因此是 UB。

在 OP 的面试问题的特定情况下,我们只关心一个特定的值......不是一般情况。 -1(在 32 位机器上为 0xffffffff)左移 4 次将产生 0xfffffff0,正如 OP 最初认为的那样。

【讨论】:

  • 您所描述的不是未定义的行为,而是未指定/定义的实现(取决于文档请求)。此外,-1 并不总是“1111...111”。它可以是“1000...0001”或“111...1110”。我仍然不完全相信 -1
  • 如果未定义,我的猜测是可能涉及到图片之外的溢出异常,这意味着委员会无法保证它甚至可以让程序继续或如何操作系统会响应这样的事件。这是未定义的行为。
  • @Noah - 我没有意识到 -1 的代表不是 '1111...111'。只是出于好奇,什么样的(例子?)实现不使用'1111...111'来表示-1?
  • @semaj:C 标准允许使用 2 的补码、1 的补码和符号幅度表示来实现。
  • @semaj:进一步挑剔/即使对于 2 的补码,“如果 [MSB] 为零,则数字为正”应该说“非负”。不要忘记数字 0 的所有位都是 0 但不是正数。
【解决方案6】:

这是未定义的行为。

$ cat undef.c 
#include <stdio.h>
int main(void){
       printf ("%x" ,-1<<4); 
}
$ clang -fsanitize=undefined undef.c
$ ./a.out
undef.c:3:24: runtime error: left shift of negative value -1
fffffff0

【讨论】:

    【解决方案7】:

    我在 3 个不同的编译器和操作系统上运行此代码。所有人都给了我与问题中提到的相同的答案。直到并且除非有人提出了这实际上是未定义行为的编译器,否则我会说答案是正确的。如果这在 99.99% 的情况下是稳定的,那么标准被更改的可能性比编译器停止支持它的可能性更大。

    【讨论】:

    • 好吧,在 16 位 DOS 上,答案是 fff0
    • BigEndian : LittleEndian CPU 也会影响答案吗?
    【解决方案8】:

    我只是将代码写在一个文本文件中,编译后,YES,答案是正确的。

    【讨论】:

    • 我通常赞成反对匿名反对票,但在这种情况下,答案不仅是错误的,而且还会积极误导。检查使用特定编译器编译并在特定系统上运行的程序的行为并不能告诉您标准要求什么(更不用说编译器可能有错误)。几乎例外:Comeau 是一个非常符合标准的编译器,您通常可以信任,使用 Comeau Online(免费)测试小代码 sn-ps 是个好主意,但即使 Comeau 也有一些错误......
    • @Akf:我明白了。感谢您的意见。自从我使用编译语言并忘记了所有这些以来,已经有很长时间了。我应得的反对票。经验教训u_u
    • +1 表示态度好 :-)。还不如删除答案...? cdhowie 竭尽全力地涵盖了同一点,他无缘无故地剪切和粘贴了他的代码,获得了 3 票! @Alf:对 Comeau 分享的有用见解 - 谢谢(尽管这里有一个有趣的反例 - 如您所知,即使 Comeau 在 some 特定 CPU 上编译/测试运行,所以这里的转变行为是没有比任何其他编译器更具权威性/在这种情况下,可以预期未定义的行为会落入 CPU 级别,而不是标准合规问题)。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-11-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多