4

C语言面试题详解(第3节),基础要是不牢,就很容易中套

 3 years ago
source link: https://blog.popkx.com/explanation-of-interview-questions-in-c-language-section-3/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

C语言面试题详解(第3节),基础要是不牢,就很容易中套

发表于 2019-02-05 20:02:23   |   已被 访问: 370 次   |   分类于:   C语言   |   暂无评论

研究看似“奇葩”的面试题目,很能帮助我们查漏补缺,找到自身知识的不足,巩固自己的技术基础。

f2b387fff01dddb1924a71a0240589a9.png

先来看看这个问题

请看下面这个C语言程序,它来自中国台湾某著名 CPU 生产公司的面试题,一起思考下,这道题会输出什么呢?

#include <stdio.h>

int main()
{
     unsigned char a = 0xa5;
     unsigned char b = ~a>>4+1;

     printf("b=%d\n", b);

     return 0;
}
2b4fc9e3185357464b0a66d718a452c8.png
A. 245  B. 246  C. 250  D. 2

估计大部分人都会选 D,[/斜眼笑]。

大致分析下,发现这个问题似乎考察两个知识点:一是类型转换问题,再就是运算符的优先级问题。先来分析

unsigned char b = ~a>>4+1;

因为程序没有指定数值 4 和 1 的类型,所以编译器会将其默认为 int 类型。~ 的优先级高于 >>,+ 的优先级高于 >>,所以上面这句 C 语言代码相当于下面这句:

unsigned char b = (~a)>>5;
781b25194d3348a0e4a0af40be4293e3.png

也就是先对 a 取反,再右移 5 位。0xa5 取反之后就是 0x5a,再右移 5 位,得到 0x2。那看起来应该选 D 了?验证答案最简单的方法就是写好代码,编译执行:

# gcc t.c
# ./a.out
b=250

怎么回事?并不是预计的 2,b 居然等于 250!这是为什么呢?

进一步分析

相信大家应该都知道,在C语言程序计算的过程中,如果运算符两边的数值类型不一致,编译器会做一些隐式的自动转换。这种数据类型转换比较复杂,但是总体遵循这个准则:尽量避免精度损失

这就明白了,若是需要隐式的转换数据类型,编译器总是尽量的往较宽数据类型(占用内存空间较多,比如 int 类型常常比 char 类型宽)转换。例如,如果某次计算中,计算数的类型是 long double,那么另一个操作数无论是什么类型,都会被转换位 long double 类型,因为 C 语言中没有比 long double 还宽的数据类型了。

7df5da4809835689c3a85db00da77342.png

如果计算过程中的计算数不是浮点数,那么它们肯定都是整型,编译器一般会将所有小于 int 类型宽度的数据类型转换位 int 类型,这其实就是所谓的“整值提升”。

知道了“整值提升”的概念,再来考虑上面的思考题就简单了。a 是 unsigned char 类型的数据,小于 int 类型的宽度,因此在 a 进行取反运算之前,会现将其提升到 int 类型宽度。

C标准并没有定义 int 类型的宽度,在我的机器上,int 类型占用 4 个字节的内存空间,因此将 a 转换位 int 类型后,a 等于 0x000000a5,取反后就等于 0xffffff5a 了,然后再右移 5 位,得到 0x07fffffa。现在将其赋值给 b,而 b 是 unsigned char 类型的,所以赋值完成后,b 等于 0xfa,也即 250

a6cd1ef4134fbe94e6d5d027c84c5a39.png

那怎样才能得到 D 答案呢?现在已经明白最终 b 等于 250 是因为 C语言编译器做了“整值提升”,我们强制其不做该转换就可以了,所以可以如下修改 C 语言代码:

#include <stdio.h>

int main()
{
     unsigned char a = 0xa5;
     unsigned char b = ((unsigned char)(~a))>>4+1;

     printf("b=%d\n", b);

     return 0;
}
30db0afff64f4180a90b0e0482f6a1af.png

编译执行,会发现最终 b 等于 2 了。
# gcc t.c 
# ./a.out 
b=2

至此,我们就解决了这道面试题。可以看出,此题有陷阱,若是对C语言的隐式数据类型转换不熟悉,就很容易上套,得出错误的答案。隐式数据类型转换虽然比较复杂,但是可以说,一切都是基于这条准则:尽量避免精度损失

阅读更多:   C语言


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK