作 为一个求职者或是应届毕业生,公司除了对你的项目经验有所问询之外,最好的考量办法就是你的基本功,包括你的编程风格,你对赋值语句、递增语句、类型转换、数据交换等程序设计基本概念的理解。当然,在考试之前你最好对你所掌握的程序概念知识有所复习,尤其是各种细致的考点要加以重视。以下的考题来自真实的笔试资料,希望读者先不要看答案,自己解答后再与答案加以比对,找出自己的不足。
面试例题1 :Which of the following statements describe the results of executing the code snippet below in C++?(下列C++代码的输出结果是什么?)[台湾某著名杀毒软件公司2010年7月笔试题]
A.The i within main will have an undefined value.(main()里的i是一个未定义值)
B.The i within main will have a value of 1.(main()里的i值为1)
C.The compiler will not allow this statement.(编译器不允许这种写法)
D.The i within main will have a value of 0.(main()里的i值为0)
解析 :当面试者看到int i=i;时,也许第一反应就是怎么有这么诡异的代码?但是在C++中这样做是完全合法的(但显然不合理)。int i=i,i变量从声明的那一刻开始就是可见的了,main()里的i 不是1,因为它和main()外的i无关,而是一个未定义值。
答案 :A
面试例题2: What does the following program print?(下面程序的结果是多少?)[中国台湾某著名计算机硬件公司2005年12月面试题]
解析:
x*=(y=z=5)的意思是说5赋值给z,z再赋值给y,x=x*y,所以x为2*5=10。
x==(y=z)的意思是说z赋值给y,然后看x和y相等否?不管相等不相等,x并未发生变化,仍然是10。
x=(y==z)的意思是说首先看y和z相等否,相等则返回一个布尔值1,不等则返回一个布尔值0。现在y和z是相等的,都是3,所以返回的布尔值是1,再把1赋值给x,所以x是1。
x=(y&z)的意思是说首先使y和z按位与。y是3,z也是3。y的二进制数位是0011,z的二进制数位也是0011。按位与的结果如下表所示。
所以y&z的二进制数位仍然是0011,也就是还是3。再赋值给x,所以x为3。
x=(y&&z)的意思是说首先使y和z进行与运算。与运算是指如果y为真,z为真,则(y&&z)为真,返回一个布尔值1。这时y、z都是3,所以为真,返回1,所以x为1。
x=(y|z)的意思是说首先使y和z按位或。y是4,z是3。y的二进制数位是0100,z的二进制数位是0011。与的结果如下表所示。
所以y&z的二进制数位是0111,也就是7。再赋值给x,所以x为7。
x=(y||z)的意思是说首先使y和z进行或运算。或运算是指如果y和z中有一个为真,则(y||z)为真,返回一个布尔值1。这时y、z都是真,所以为真,返回1。所以x为1。
答案: 10,10,1,3,1,7,1。
面试例题3 :以下代码结果是多少?[中国某杀毒软件公司2010年3月笔试题]
A.8
B.9
C.10
D.11
解析 :本题func函数返回值是形参x转化成二进制后包含1的数量。理解这一点就很容易答出来了。9999转化为二进制是:
答案 :A
面试例题1: 下面两段代码的输出结果有什么不同?[中国著名网络企业XL公司2007年10月面试题]
第1段:
第2段:
解析 :这两段代码的不同点就在for循环那里,前者是for(a=0,x=0;a<=1&&!x++;a++),后者是for(a=0,x=0;a<=1&&!x++;)。
先说第1段代码。
第1步:初始化定义a=0,x=0。
第2步:a小于等于1,x的非为1,符合循环条件。
第3步:x++后x自增为1。
第4步:进入循环体,a++,a自增为1。
第5步:执行for(a=0,x=0;a<=1&&!x++;a++)中的a++,a自增为2。
第6步:a现在是2,已经不符合小于等于1的条件了,所以“&&”后面的“!x++”不执行,x还是1,不执行循环体。
第7步:打印a和b,分别是2和1。
再说第2段代码。
第1步:初始化定义a=0,x=0。
第2步:a小于等于1,x的非为1,符合循环条件。
第3步:x++后x自增为1。
第4步:进入循环体,a++,a自增为1。
第5步:a现在是1,符合小于等于1的条件,所以“&&”后面的“!x++”被执行,x现在是1,x的非为0,不符合循环条件,不执行循环体,但x++依然执行,自增为2。
第6步:打印a和b,分别是1和2。
答案 :第一段输出结果是21,第二段输出结果是12。
面试例题2: What will be the output of the following C code?(以下代码的输出结果是什么?)[中国著名通信企业H公司2007年7月面试题]
A.8 8
B.130 8
C.7 7
D.7 8
解析 :C中printf计算参数时是从右到左压栈的。
几个输出结果分别如下:
printf("%d\n ",*ptr);此时ptr应指向第一个元素6。
*(ptr++)+=123应为*ptr=*ptr+123;ptr++,此时ptr应指向第二个元素7。
printf("%d\n ",*(ptr-1));此时输出第一个元素129,注意此时是经过计算的。
printf("%d\n ",*ptr);此时输出第二个元素7,此时ptr还是指向第二个元素7。
printf("%d,%d\n ",*ptr,*(++ptr));从右到左运算,第一个是(++ptr),也就是ptr++,*ptr=8,此时ptr指向第三个元素8,所以全部为8。
答案 :A
面试例题: We have two pieces of code,which one do you prefer,and tell why.(下面两段程序有两种写法,你青睐哪种,为什么?)[美国某著名计算机嵌入式公司2005年10月面试题]
答案:
A.第一种写法'A'==a 比较好一些。这时如果把“==”误写成“=”的话,因为编译器不允许对常量赋值,就可以检查到错误。
B.第二种写法好一些,将部分加法运算放到了循环体外,提高了效率。缺点是程序不够简洁。
面试例题1: 下面程序的结果是多少?[中国著名通信企业S公司2007年8月面试题]
解析 :在机器上运行一下,可以得到结果,“cout<<(int&)a<<endl;”输出的是1065353216,而不是1。这是因为浮点数在内存里和整数的存储方式不同,(int&)a相当于将该浮点数地址开始的sizeof(int)个字节当成int型的数据输出,因此这取决于float型数据在内存中的存储方式,而不是经过(int&)a显示转换的结果(1)。
因为float a=1.0f 在内存中的表示都是3f800000,而浮点数和一般整型不一样,所以当(int&)a强制转换时,会把内存值3f8000000当作int型输出,所以结果自然变为了1065353216(0x3f800000的十进制表示)。
答案 :false true或者0 1。
面试例题2: 下面程序的结果是多少?[中国著名通信企业S公司2007年8月面试题]
解析 :在X86系列的机器中,数据的存储是“小端存储”,小端存储的意思就是,对于一个跨多个字节的数据,其低位存放在低地址单元,其高位存放在高地址单元。比如一个 int 型的数据ox12345678,假如存放在0x00000000,0x00000001,0x00000002,0x00000003这四个内存单元中,那么ox00000000中存放的是低位的ox78,而ox00000003中存放的是高位的0x12,依此类推。
有了以上的认识,继续分析上面的程序为什么输出fffffff7:char*b=(char*)&a;这句话到底干了什么事呢?其实说来也简单,&a可以认为是个指向 unsigned int类型数据的指针,(char*)&a则把&a强制转换成 char*类型的指针,并且这个时候发生了截断!截断后,指针b只指向oxf7这个数据(为什么b指向最低位的oxf7而不是最高位的oxff?想想上面刚刚讲过的“小端存储”,低地址单元存放低位数据),又由于指针b是 char*型的,属于有符号数,所以有符号数0xf7在printf()的作用下输出fffffff7。
或者我们可以通过汇编代码更直观地看内部的情况:
答案 :000000f7,fffffff7。
扩展知识
C++定义了一组内置类型对象之间的标准转换,在必要时它们被编译器隐式地应用到对象上。
隐式类型转换发生在下列这些典型情况下。
1.在混合类型的算术表达式中
在这种情况下最宽的数据类型成为目标转换类型,这也被称为算术转换(Arithmetic Conversion),例如:
2.用一种类型的表达式赋值给另一种类型的对象
在这种情况下目标转换类型是被赋值对象的类型。例如在下面第一个赋值中文字常量0的类型是int。它被转换成int*型的指针表示空地址。在第二个赋值中double型的值被截取成int型的值。
3.把一个表达式传递给一个函数,调用表达式的类型与形式参数的类型不相同在这种情况下目标转换类型是形式参数的类型。例如:
4.从一个函数返回一个表达式的类型与返回类型不相同
在这种情况下返回的表达式类型自动转换成函数类型。例如:
算术转换保证了二元操作符,如加法或乘法的两个操作数被提升为共同的类型,然后再用它表示结果的类型。两个通用的指导原则如下:
(1)为防止精度损失,如果必要的话,类型总是被提升为较宽的类型。
(2)所有含有小于整型的有序类型的算术表达式在计算之前其类型都会被转换成整型。
规则的定义如上面所述,这些规则定义了一个类型转换层次结构。我们从最宽的类型long double开始。
如果一个操作数的类型是long double,那么另一个操作数无论是什么类型都将被转换成long doubless。例如在下面的表达式中,字符常量小写字母a将被提升为long double,它的ASC码值为97,然后再被加到long double型的文字常量上:
如果两个操作数都不是long double型,那么若其中一个操作数的类型是double 型,则另一个就将被转换成double 型。例如:
类似地,如果两个操作数都不是double 型而其中一个操作数是float型,则另一个被转换成float 型。例如:
否则如果两个操作数都不是3种浮点类型之一,它们一定是某种整值类型。在确定共同的目标提升类型之前,编译器将在所有小于int的整值类型上施加一个被称为整值提升(integral promotion)的过程。
在进行整值提升时类型char、signed char、unsigned char和short int都被提升为类型int。如果机器上的类型空间足够表示所有unsigned short型的值,这通常发生在short用半个字而int用一个字表示的情况下,则unsigned short int也被转换成int,否则它会被提升为unsigned int。wchar_t和枚举类型被提升为能够表示其底层类型(underlying type)所有值的最小整数类型。例如已知如下枚举类型:
相关联的值是0和1。这两个值可以但不是必须存放在char类型的表示中。当这些值实际上被作为char类型来存储时,char代表了枚举的底层类型,然后status 的整值提升将它的底层类型转换为int。
在下列表达式中:
在确定两个操作数被提升的公共类型之前,cval found和mval 都被提升为int类型。
一旦整值提升执行完毕,类型比较就又一次开始。如果一个操作数是unsigned long型,则第二个也被转换成unsigned long型。在上面的例子中所有被加到ulong上的3个对象都被提升为unsigned long型。如果两个操作数的类型都不是unsigned long 而其中一个操作数是long型,则另一个也被转换成long型。例如:
long类型的一般转换有一个例外。如果一个操作数是long型而另一个是unsigned int型,那么只有机器上的long型的长度足以存放unsigned int的所有值时(一般来说,在32位操作系统中long型和int型都用一个字长表示,所以不满足这里的假设条件),unsigned int才会被转换为long型,否则两个操作数都被提升为unsigned long型。若两个操作数都不是long型而其中一个是unsigned int型,则另一个也被转换成unsigned int型,否则两个操作数一定都是int型。
尽管算术转换的这些规则带给你的困惑可能多于启发,但是一般的思想是尽可能地保留多类型表达式中涉及的值的精度。这正是通过把不同的类型提升到当前出现的最宽的类型来实现的。
面试例题1: 下面程序的结果是多少?[中国台湾某著名CPU生产公司2010年7月面试题]
A.245
B.246
C.250
D.2
解析 :这道题目考查两个知识点:一是类型转换问题;二是算符的优先级问题。
对于第一个问题:unsigned char b=~a>>4,在计算这个表达式的时候,编译器会先把a和4的值转换为int类型(即所谓整数提升)后再进行计算,当计算结果出来后,再把结果转换成unsigned char赋值给b。
对于第二个问题:因为“~”的优先级高于“>>”和“+”,本题的过程是这样的:先对于1010 0101取反0101 1010;再右移,这里有一个问题,是先右移4位再加1呢,还是直接右移5(4+1)位。因为“+”的优先级高于“>>”,所以直接右移5位。结果是0000 0010。
~a操作时,会对a进行整型提升,a是无符号的,提升时左边补0(一般机器32位,char是8位,左边24个1;16位int则左边补8个0),取反后左边为1,右移就把左边的1都移到右边(注意是算术移位),再按照无符号读取,才有250这个结果。
答案 :C
扩展知识
运算符优先级如下表所示。
面试例题2: 用一个表达式,判断一个数X是否是2 N 次方(2,4,8,16,…),不可用循环语句。[中
国台湾某著名CPU生产公司2007年10月面试题]
解析: 2、4、8、16这样的数转化成二进制是10、100、1000、10000。如果X减1后与X做运算,答案若是0,则X是2 N 次方。
答案 :!(X&(X-1))
面试例题3 :下面代码:
(729,271)=______
解析 :这道题如果使用笨办法来求解,就都转化成二进制然后按位与。但这样的做法显然不是面试官所期待的。仔细观察一下题目,x&y是取相同的位与,这个的结果是x和y相同位的和的一半,x^y是取x和y的不同位,右移相当于除以2,所以这个函数的功能是取两个数的平均值。(729+271)/2=500。
答案 :500
面试例题4 :利用位运算实现两个整数的加法运算,请用代码实现。
答案 :代码如下:
面试例题1: There are two int variables:a and b,don’t use“if”,“?:”,“switch”or other judgement statements,find out the biggest one of the two numbers.(有两个变量a和b,不用“if”、“?:”、“switch”或其他判断语句,找出两个数中间比较大的。)[美国某著名网络开发公司2005年面试题]
答案: 方案一:
方案二:
上面的情况没有考虑溢出的情况,如果考虑溢出的话需要加额外的判断。
面试例题2 :两个整型数,不准用while,if,for,switch,?:等判断语句求出两者最大值。
答案: 代码如下,可以采用bool值:
面试例题3 :有2数据,写一个交换数据的宏?
解析: 如果用异或语句,无须担心超界的问题
这样做的原理是按位异或运算。按位异或运算符“∧”是双目运算符,其功能是参与运算的两数各对应的二进制位相异,或当对应的二进制位相异时结果为1。参与运算数仍以补码形式出现。例如9∧5可写成如下算式:
但是如果这个数据是浮点的话,就不好处理了(浮点数不能进行位运算),正确的做法是采用内存交换。
答案:
面试例题1: 在C++程序中调用被C编译器编译后的函数,为什么要加extern "C"?
答案 :C++语言支持函数重载,C语言不支持函数重载。函数被C++编译后在库中的名字与C语言的不同。假设某个函数的原型为void foo(int x,int y)。该函数被C编译器编译后在库中的名字为_foo,而C++编译器则会产生像_foo_int_int之类的名字。
C++提供了C连接交换指定符号extern "C"解决名字匹配问题。
面试例题2: 头文件中的ifndef/define/endif是干什么用的?
答案 :头文件中的ifndef/define/endif是条件编译的一种,除了头文件被防止重复引用(整体),还可以防止重复定义(变量、宏或者结构)。
面试例题3: 评价一下C与C++的各自特点。如果一个程序既需要大量运算,又要有一个好的用户界面,还需要与其他软件大量交流,应该怎样选择合适的语言?
答案 :C是一种结构化语言,重点在于算法和数据结构。C程序的设计首先考虑的是如何通过一个过程,对输入(或环境条件)进行运算处理得到输出(或实现过程(事务)控制)。而对于C++,首先考虑的是如何构造一个对象模型,让这个模型能够契合与之对应的问题域,这样就可以通过获取对象的状态信息得到输出或实现过程(事务)控制。
对于大规模数值运算,C/C++和Java/.NET之间没有明显的性能差异。不过,如果运算设计向量计算、矩阵运算,可以使用FORTRAN或者MATLAB编写计算组件(如COM)。
大规模用户界面相关的软件可以考虑使用.NET进行开发(Windows环境下),而且.NET同COM之间的互操作十分容易,同时.NET对数据库访问的支持也相当好。
面试例题1 :下面的switch语句输出什么。[日本著名软件企业F公司2013年2月面试题]
A.cdd
B.cd
C.abcd
D.cderror
解析 :本题考的是switch中的“fall through”:如果case语句后面不加break,就依次执行下去。
所以先顺序执行,考虑n的初始值,从'c'开始查找输出(default和ab直接略过),输出c;没有break,那么继续输出后面的,输出d。
答案 :B
面试例题2 :上机题目描述:选秀节目打分,分为专家评委和大众评委,score[]数组里面存储每个评委打的分数,judge_type[]里存储与 score[]数组对应的评委类别,judge_type==1,表示专家评委,judge_type==2,表示大众评委,n表示评委总数。打分规则如下:专家评委和大众评委的分数先分别取一个平均分(平均分取整),然后,总分=专家评委平均分*0.6+大众评委*0.4,总分取整。函数最终返回选手得分。[中国著名通信企业H公司2013年2月面试题]
解析 :上机题目都是很简单的,但是考的就是考虑问题全面与否。
答案 :代码如下: