1 分析下列程序:
#include<iostream>
using namespace std;
int i=1;
int main() {
int i=i;
cout<<i<<endl;
}
输出结果是0.
分析:对于int i=i;这样的写法在C++中是完全合法的(但是不合理)。int i=i,i变量从声明的那一刻开始就是可见的了,main()里的i不是1,因为它和main()外的i无关,而是一个未定义值。
2 分析下列程序:
#include<iostream>
using namespace std;
int func(int x) {
int count=0;
while(x) {
count++;
x=x&(x-1);
}
return count;
}
int main() {
cout<<func(9999)<<endl;
}
输出结果是8. 9999的二进制是10011100001111,计算一个数中的1的个数。
3 比较下述两个程序:
#include<iostream>
using namespace std;
int main() {
int a,x;
for(a=0,x=0;a<=1 && !x++;a++) {
a++;
}
cout<<a<<x<<endl;
}
输出结果是21.
分析:a<=1, !x为1,符合循环条件,x++后x自增为1;进入循环体,a++,a自增为1;执行for循环中的a++,a自增为2;现在a已经是2,已经不符合小于等于1的条件了,所以&&后面的!x++不执行,x还是1,不执行循环体。
#include<iostream>
using namespace std;
int main() {
int a,x;
for(a=0,x=0;a<=1 && !x++;) {
a++;
}
cout<<a<<x<<endl;
}
输出结果是12
分析:a<=1,!x为1,符合循环条件;x++后自增为1;进入循环体,a++,a自增为1;a现在是1,符合小于等于1的条件,所以&&后面的!x++被执行,x现在是1,x的非为0,不符合循环条件,不执行循环体,但x++依然执行,自增为2。
4 分析下列程序:
#include<iostream>
using namespace std;
int main() {
int b=3;
int arr[]={6,7,8,9,10};
int *ptr=arr;
*(ptr++)+=123;//这句话可以分成两句: *ptr=*ptr+123;ptr++;
printf("%d,%d\n",*ptr,*(++ptr));
return 0;
}
输出结果是8,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.
5 分析下列程序:
if(‘A’==a)
cout<<a++;
if(a=='A')
cout<<a++;
第一种写法比较好,因为如果把==写成=的话,因为编译器不允许对常量赋值,就可以检查到错误。
6 分析下列程序:
#include<iostream>
#include<stdio.h>
#include<string.h>
#include<conio.h>
using namespace std;
int main() {
float a=1.0f;
cout<<(int)a<<endl;
cout<<&a<<endl;
cout<<(int&)a<<endl;
cout<<boolalpha<<((int)a == (int&)a)<<endl;
float b=0.0f;
cout<<(int)b<<endl;
cout<<&b<<endl;
cout<<(int &)b<<endl;
cout<<boolalpha<<((int)b == (int &)b)<<endl;
return 0;
}
发现12行
cout<<boolalpha<<((int)a == (int&)a)<<endl;输出结果是false,第18行
cout<<boolalpha<<((int)b == (int &)b)<<endl;输出结果是true。
这是因为浮点数在内存里和整数的存储方式不同,(int&)a相当于将该浮点数地址开始的sizeof(int)个字节当成int型的数据输出,因此这取决于float型数据在内存中的存储方式,而不是经过(int&)a显示转换的结果(1)。
因为float a=1.0f在内存中的表示都是3f800000,而浮点数和一般整形不一样,所以当(int&)a强制转换时,会把内存3f8000000当做int型输出,所以结果是1065353216,即
0x3f8000000的十进制。
7 分析下述程序:
#include<iostream>
using namespace std;
int main() {
unsigned int a=0xFFFFFFF7;
unsigned char i=(unsigned char)a;
char *b=(char*)&a;
printf("%08x,%08x",i,*b);
}
输出结果:000000f7,fffffff7.
分析:在X86系列的机器中,数据的存储是“小端存储”,小端存储的意思是,对于一个跨多字节的数据,其低位存放在低地址单元,其高位存放在高地址单元。比如一个int型的数据0x12345678,假如存放在0x00000000,0x00000001,0x00000002,0x00000003这四个内存单元中,那么0x00000000中存放的是低位的0x78,而0x00000003中存放的是高位的0x12,依此类推。
分析第二个为什么输出fffffff7. &a可以认为是个指向unsigned int类型数据的指针,(char *)&a把&a强制转换成char *类型的指针,并且这个时候发生了截断!截断后,指针b只指向0xf7这个数据。(为什么 b指向最低位的0xf7而不是最高位的oxff?想想上面刚刚讲过的小端存储,低地址单元存放低位数据),又由于指针b是char*型,属于有符号数,所以有符号数0xf7在printf()的作用下输出fffffff7.
8 分析下列程序:
#include<iostream>
using namespace std;
int main() {
unsigned char a=0xA5;
unsigned char b=~a>>4+1;
//cout<<b<<endl;;
printf("b=%d\n",b);
}
输出结果是:250.
对于第一个问题:unsigned char b=~a>>4,在计算这个表达式的时候,编译器会先把a和4的值转换为int类型(即所谓整数提升)后再进行计算,当计算结果出来后,再把结果转换成unsigned char赋值给b。
对于第二个问题:因为"~"优先级高于“>>”和“+”,本题过程是这样的:先对1010 0101取反0101 1010,再右移;因为+的优先级高于>>,所以直接右移5位,结果是0000 0010.
~a操作时,会对a进行整形提升,a是无符号的,提升时左边补0,(一般机器32位,char是8位,左边24个1;16位int则左边补8个0),取反后左边为1,右移就把左边的1都移动到右边(注意是算术移位),再按照无符号读取,才有250这个结果。
https://zhidao.baidu.com/question/1948282626282180428.html
https://zhidao.baidu.com/question/1692154523174105708.html
(~a) 这是个比较困扰人的地方,如果你的编译环境中将char升为int处理的话,答案就是
(~ 0000 0000 1010 0101) = (1111 1111 0101 1010)
(~ a) >> 5 就是 0000 0111 1111 1010
但是的b也是unsigned char类型,所以赋值后b = 0000 0000 1111 1010 = 250(10进制)
9 用一个表达式,判断一个数X是否是2^N次方(2,4,8,16……),不可用循环语句。
分析:2,4,8,16转化成二进制是10,100,1000,10000。如果X减一后与X做运算,答案若是0,则X是2^N次方。 !(X&(X-1))
10 分析下列程序:
#include<iostream>
using namespace std;
int f(int x,int y) {
return (x&y)+((x^y)>>1);
}
int main() {
cout<<f(729,271)<<endl;
return 0;
}
输出结果500. 分析:x&y是取相同的位与,这个结果是x和y相同位的和的一半,x^y是取x和y的不同位,右移相当于除以2,所以这个函数功能是取两个数的平均值、
11 利用位运算实现两个整数的加法运算。
int Add(int a,int b) {
if(b==0) return a;//没有进位的时候完成运算
int sum,carry;
sum=a^b;//完成第一步没有进位的加法运算
carry=(a&b)<<1;//完成第二步进位并且左移运算
return Add(sum,carry); //进行递归,相加
}
12 有两个变量a和b,不准用while,if,for,switch或其他语句,找出最大值?
int max=((a+b)+abs(a-b))/2;
13 C++和C的关系
(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)头文件中的ifdef/define/endif是干什么用?
答案:头文件中的ifdef/define/endif是条件编译的一种,除了头文件被防止重复引用(整体),还可以防止重复定义(变量、宏或者结构)。
(3)如何评价C、C++各自特点?
答案:C是一种结构化语言,重点在于算法和数据结构。C程序的设计首先考虑的是如何通过一个过程,对输入进行运算处理得到输出。而对于C++首先考虑的是如何构造一个对象模型,让这个模型能够契合与之对应的问题域,这样就可以通过获取对象的状态信息得到输出或实现过程控制。
对于大规模数值运算,C/C++和Java/.NET没有明显性能差异,不过,如果运算设计向量计算,矩阵计算,可以使用FORTRAN或者MATLAB编写计算组件。
大规模用户界面相关的软件可以考虑使用.NET进行开发(windows环境下),而且.NET同COM之间的互操作十分容易,同时.NET对数据库访问的支持也好。
《程序员面试宝典》 欧立奇 P30~P45