
昨天有个读者私信我说看完上篇关于数据类型的文章后去面试被问了个运算符的问题当场懵了。他问我老师面试官问我unsigned int减1等于多少我说是最大值减1他说不对。这到底怎么回事我一看就知道问题出在哪了——他对运算符的理解还停留在表面没搞清楚类型转换的坑。说实话这种问题在机器人面试里太常见了。你以为自己懂了但真到了面试官追问细节的时候才发现自己根本没吃透。算术运算符不只是加减乘除那么简单加减乘除谁都会对吧但在机器人开发里事情没那么简单。举个例子你要计算电机的转速公式是 rpm (pulse_count * 60) / (encoder_resolution * sample_time)。看起来很简单但你要是直接用int类型计算很可能就掉坑里了。我见过一个实习生写的代码pulse_count是1000encoder_resolution是4096sample_time是0.1秒。他这么写int rpm (1000 * 60) / (4096 * 0.1);结果你猜怎么着rpm算出来是0。为什么因为4096乘以0.1的时候0.1是double类型整个表达式被提升为double计算结果是2441.40625。但赋值给int的时候直接截断变成了2441。等等这也不对啊应该是2441才对。不对我再算一遍。1000乘以60是600004096乘以0.1是409.660000除以409.6应该是146.48。所以最后结果是146。你看就这么一个简单的公式里面藏着类型转换、整数除法截断、浮点精度好几个坑。面试官就喜欢问这种看似简单实则暗藏玄机的问题。我的建议是在机器人代码里涉及物理量计算的一律用double。别省那点内存现在又不是上世纪八十年代。关系运算符浮点数比较的坑a b这看起来再简单不过了。但在机器人领域这么写很可能出bug。为什么因为浮点数有精度问题。你算出来的两个坐标理论上应该相等但实际计算结果可能是a 1.000000001b 0.999999999这时候 a b 返回false但你觉得它们应该相等。怎么办正确的做法是判断它们的差值是否小于一个很小的数叫epsilonconst double EPSILON 1e-6; if (std::abs(a - b) EPSILON) { // 认为a和b相等 }这个EPSILON取多少合适取决于你的应用场景。如果是处理激光雷达数据1e-6可能就够了如果是做高精度定位可能需要1e-9。面试的时候如果你能主动提到浮点数比较的这个问题面试官会觉得你有实际工程经验。真的我面试别人的时候听到候选人提到这个心里就会加分。逻辑运算符短路求值的妙用和||这两个运算符大家都知道是与和或。但你知道它们有个特性叫短路求值吗什么意思呢看这个例子if (ptr ! nullptr ptr-isValid()) { // 安全地使用ptr }如果ptr是空指针ptr ! nullptr返回false后面的ptr-isValid()根本不会执行。这就避免了解引用空指针导致的崩溃。这个特性在机器人代码里特别有用。比如你要检查传感器数据是否有效if (sensor_data ! nullptr sensor_data-timestamp last_timestamp) { process(sensor_data); }先判断指针是否为空再访问它的成员。如果反过来写程序就可能崩溃。我见过一个初级工程师写的代码他把条件写反了if (sensor_data-timestamp last_timestamp sensor_data ! nullptr) {结果程序时不时就崩溃查了半天才发现是这个问题。面试的时候面试官可能会问你短路求值有什么好处你就拿这个例子回答绝对比他听一百遍可以提高效率要强。位运算符嵌入式开发的必备技能如果你要做嵌入式机器人开发位运算符是必须要掌握的。、|、^、~、、这些符号看起来简单但用起来有很多讲究。举个例子你要从传感器读取一个16位的寄存器值高8位是温度低8位是湿度。怎么提取uint16_t reg_value read_register(); uint8_t temperature (reg_value 8) 0xFF; uint8_t humidity reg_value 0xFF;这种操作在驱动开发里太常见了。你要是不会位运算连传感器数据都读不出来。还有一个常见的用法是设置标志位。比如机器人的状态可以用一个uint8_t表示每一位代表一个状态const uint8_t STATUS_CONNECTED 0x01; // 0000 0001 const uint8_t STATUS_MOVING 0x02; // 0000 0010 const uint8_t STATUS_ERROR 0x04; // 0000 0100 // 设置移动状态 status | STATUS_MOVING; // 清除移动状态 status ~STATUS_MOVING; // 检查是否在移动 if (status STATUS_MOVING) { // 机器人正在移动 }这种写法在嵌入式代码里到处都是。面试的时候如果问你怎么设置/清除/检查某个位你要能脱口而出。说到位运算还有一个经典的面试题怎么判断一个数是2的幂答案是n 0 (n (n - 1)) 0为什么因为2的幂在二进制里只有一个1比如8是10007是0111它们相与结果是0。这种题目考的不是你会不会而是你对位运算的理解深不深。运算符优先级那些容易被忽略的细节a b * c先算哪个小学生都知道先算乘法。但在C里运算符优先级有十几个等级谁能全记住老实说我也记不住。所以我的习惯是只要涉及到不同运算符就加括号。比如 if (a b c)你觉得是先算还是先算答案是先算。因为的优先级比高。所以这个表达式实际上是a (b c)很可能不是你想要的结果。正确的写法应该是 (a b) c。我见过太多因为优先级问题导致的bug了。有个同事写的代码判断某个标志位是否设置if (status 0x01 0x01) {结果这个条件永远不成立。因为先算0x01 0x01是true也就是1然后status 1只有status的最低位是1的时候才成立。他本来想写的是 (status 0x01) 0x01。这种bug特别隐蔽因为代码能编译通过逻辑上也说得通但结果就是不对。我的建议是不要依赖优先级多加括号。代码是给人看的不是给编译器看的。你写得清楚一点后面维护的人包括三个月后的你自己会感谢你的。给正在准备面试的你一点建议运算符和表达式看起来是最基础的东西但面试里考的就是这些基础。我面试的时候最喜欢问候选人运算符相关的问题。不是因为这些问题难而是能看出一个人的基本功扎不扎实。你能把浮点数比较的坑讲清楚说明你写过真正的代码你能把位运算的用法说得头头是道说明你懂嵌入式你能主动提到优先级的问题说明你踩过坑、长过记性。怎么复习这部分我的建议是把C的运算符表过一遍重点看类型转换、浮点比较、位运算、优先级这几个地方。然后去找一些开源的机器人代码看看别人是怎么用这些运算符的。最的是自己动手写一写。纸上得来终觉浅绝知此事要躬行。如果这篇文章对你有帮助欢迎点赞、在看、转发三连。 你的支持是我持续更新的最大动力。「机器人软件开发面试·从入门到精通」连载系列上一篇第1篇 变量与数据类型——机器人程序中每个数据的身份证 下一篇预告第3篇 条件判断if/else——面试中最容易写错的分支逻辑有任何问题欢迎评论区留言我会尽量回复。