金三银四季,面试题中套路深

swift 发布于 2019年08月20日

各位朋友好久不见,大家在这个春天工作的是否开心有收获呢,春天不仅是一年的开始,也是另外一件事情开始活跃的时候,那就是招聘。每年这个时候都是各个公司大量招人,以及程序员们集中跳槽的时节。

本人在近一个月内以平均每两天一个电话的频率,被各种猎头问候,也深深体会到此刻躁动的节奏。自己对猎头问候的心态也随着年深日久也在发生着微妙的变化。刚入职场一到两年的时候,被猎头打电话还有一种莫名的兴奋,其实内心就是觉得自己有价值了。但是到了多年以后的此时此刻,再接到类似的问候时,心中早已没了当时那种兴奋,更多的是马上想结束对话的冲动。以至于到了现在,对这些问候的标准回复也变成了最直接的:“不好意思,不太需要”。

当然,我自己虽然对猎头并不感冒,但我不否认他们的价值。任何事物都是存在即合理,如果你正身处于一个对自身发展并不是很好的团队,那么换个环境未尝不是一个理智的选择。

关于面试题

今天要聊的主题也自然不是上面说的这些,而是关于面试题这个话题的讨论。 既然是跳槽季,大家就免不了遇到各种面试题,对于开发职位来说,笔试题就是经常会遇到的,特别是那些一次会面试很多人的大团队。

事情还要从我的交流群中一个同学的分享开始,他遇到了这样一个面试题:

#define SQUAKE(a) ((a) * (a))

- (void)viewDidLoad {
[super viewDidLoad];

int a = 5;
int b = SQUAKE(a++);
NSLog(@"%d",b);

}

上面这段代码的输出结果是什么? 大家可以不必深究。我先把我的分析过程和大家分享:

首先, a 的初始值是 5, 然后我们调用 SQUAKE(a++) 并把结果给 b。

SQUAKE(a++) 调用展开后,就变成了这样 ((a++) * (a++)),至于为什么会变成这样,可以看一下第一行的 #define 语句。

如果你不了解宏定义,我简单讲解一下。第一行 #define SQUAKE(a) ((a) * (a)) 是一个宏定义,编译器会在你的代码实际编译成机器码之前,将宏定义先展开,我们前面代码中的 int b = SQUAKE(a++) 展开之后就变成了 int b = ((a++) * (a++))。 实际被编译的是展开后的代码。

对于自增运算,如果你之前有过了解的话就会明白, a++ 这个运算,首先会将变量 a 当前的值作为表达式的值返回, 然后再将变量 a 自身的值加 1。 对于我们程序中的表达式,a 的初始值是 5,那么实际执行的代码就是这样:int b = ((5) * (6))

也就是说,经过这样推演,程序的输出结果应该是 30。

虽然我有比较大的把握,但我依然不敢保证程序运行的结果一定是 30(稍后我会把原因告诉你)。随后我打开 XCode 在实际的环境中跑了一下,证明了我的推导没有错。最终的输出结果就是 30 。

意义何在

之所以举这个面试题的例子,目的并不是为了在这里聊如何解这个题。 而是通过这种面试题,我发现了一个有趣的现象。虽然我知道这个题背后的代码原理,但我不认为这是一个很有意义的笔试题。

这点我当时在群里也和大家讨论了不少,虽然我知道这个题目中,宏展开和自增运算的完整规则,这个题目的目的大致也是要考验答题者对这方面的理解。 但我还是不敢保证机器的输出一定是我所计算出来的 30。

而且我想即便是各路技术大牛,看到这类题目也不敢保证百分百都能答对。反而是知识越深入的人,面对这类问题就越拿不准。打个比方,如果对一个只学过大学 C 语言的学生,那么他基本上就会断定 a++ 这个运算,一定是先返回 a 当期的值,然后在对 a 自身做自增。

但对一个有很多开发经验的人来说,他反倒会对这样的运算持一个怀疑的态度。到底是先返回 a,再自增, 还是先自增再返回 a 呢?虽然说绝大多数情况下,会是我们常见的先返回后自增,但某些编译器下,确实会有不一样的结果。

比如知乎上的这个问题,就是一个真实的例子:https://www.zhihu.com/question/264902912, 题主在 win-tcdev-c++5.7.1 上面编译同样的代码,却得出两个不同的运行结果。

对于这种高度依赖于运行环境的代码,用笔试的方式来提问,并不称得上是一个好方案。也许有人会说,题目中已经明确给出了 ObjC 代码,肯定点明了是在 XCode 开发环境中编译的情况。 但即便是这样,又有多少人会在平常开发中特意观察过,自增运算的优先级,并且能牢记在心呢? 更重要的是,自增运算本身在现代的开发语言中就是不推荐的。 因为它很容易产生歧义,在很多现代开发语言中,比如 Swift,已经明确禁止使用自增运算了。

如果从工程角度上看,这类的代码,我们平时工作中更是基本不会写,也不会遇到。 如果真的想考察开发人员的技术深度,倒不如问一些原理性的东西,比如 HTTPS 协议如何通信,多线程之前如何处理资源共享问题等,可以让应试者把自己对这些原理的理解表达出来。

而且我个人更认为开放性的问题更能接近的了解一个人的真实能力,可能是我们从小被应试教育影响的太多,我们更注重答案的正确与否,而对人的思维过程可能不是特别看重。即便是刚才提到的自增运算那个面试题,如果能让应试者把思维过程写下来,也会比直接看答案的对错来的更好一些。毕竟,我们在现实中处理各种问题的时候从来也没有标准答案,只有最适合当前情况的方案。

当然,实际情况还有另外一个维度,就是面试成本的考虑,如果一个大团队在同时面试几十甚至上百人,那么面试官在评价能力上给与每个人的精力肯定会变小。也许这样机械的筛选,在这个维度上,可能反倒成为合适的方案了。所以在如何面试人这件事上,也没有完全标准的答案,同样要看团队当时所处的情境。

总结

说了这么多,只想给最近遇到很多类似问题的朋友一点建议,如果你在面试中遇到这种类型的问题,并且没有答上来,不要全盘怀疑自己的能力。如果不幸被这些机械题目筛掉,其实也并没有什么,很大概率下是运气问题(当然,有时候也存在实力问题的情况)。其实不可否认,即便在面试工作的时候,也是有运气成分的。比如恰好这个团队正大量需要人,那么标准就会适当放低等等。尤其是很多大团队在面试技术人员的时候,通常都是同时在面试很多人。 很难做到完全精准的评估每个人的能力并分出高下来,并且在很多时候,团队的文化和面试者性格之间是否合适,也会影响最终的决策。

对于职场老鸟们来说,以上建议恐怕早已心知肚明了,甚至比我了解的更多。总之,希望上面分享的这些思维方式,能够给大家提供一个新的参考,至于有没有用,就看你自己了。


如果你觉得这篇文章有帮助,还可以关注微信公众号 swift-cafe,会有更多我的原创内容分享给你~

本站文章均为原创内容,如需转载请注明出处,谢谢。
关注微信公众号
发现更多精彩
swift-cafe