笔者经历了2014年阿里、腾讯的实习生招聘,有些题目,做完了,还以为自己对了,回来编程序跑一下,才知道是陷阱。觉得有必要总结一下哈,在此感谢一下参与讨论的筒子们,小芳,静儿。
陷阱分类:
(1)指针与字符串
7. 阅读下面代码,程序会打印出来的值是(D)------------------------------(腾讯2014实习生笔试)
void f(char **p) { *p += 2; } void main() { char *a[] = { “123”,”abc”,”456”}; f(a); printf(“%s ”,*a); }
A.123 B. abc C. 456 D. 3
void fun1(char **p) { *p+=2; cout<<(int)(*p)<<endl;//输出*p保存的地址 } void fun2(char *p) { p+=2; } int main() { char *r[]={"123","234","456"}; fun1(r); cout<<int(*r)<<endl; //输出*r保存的地址 char *r1="1234"; fun2(r1); cout<<*r<<endl<<r1; return 0; }
图1
从上图1的红框中可看出,*p和*r保存的地址相同,也就*p和*r指向同一地址,如图2所示,*p和*r也就是p1
图2
void fun1(char **p) { p++; *p+=2; } int main() { char *r[]={"123","234","456"}; fun11(r); cout<<*r<<endl<<*(r+1); system("pause"); return 0; }
运行结果为图3, 结合上面的程序和图2所示,P++后,p指向p2,*p+=2后,p2指向4,在主函数中,r就是p1,p1未变所以第一行输出123,(r+1)指向p2,p2已被更新,指向4,所以第二行输出4。
图3
8 下面C++程序的输出是(B)//---------------------------------------阿里2014实习生笔试
void f1(char *p) { p++; *p='a'; } int main() { char a[sizeof("hello")]; strcpy(a,"hello"); char *a1=a; f1(a); cout<<a<<endl; return 0; }
A hello B hallo C allo D以上都不是
如图4所示程序中的a用图4中的r表示,p其实只是a的一个副本,p++后,p指向e,将e用a覆盖。但r任然指向H。所以cout<<a,结果是hallo。
图4
若将p改成指针的引用,程序如下所示,则p和r是同一变量,p只不过是r的别名,对p操作即是对r操作。所以有图5的结果。
void f1(char *p) { p++; *p='a'; } void f2(char *&p) { p++; *p='a'; } int main() { char a[sizeof("hello")]; strcpy(a,"hello"); char *a1=a; f1(a); cout<<a<<endl; strcpy(a,"hello"); a1=a; f2(a1); cout<<a1<<endl; system("pause"); return 0; }
图5
另外,若主函数改成如下形式,则,运行出错,无输出,因为指针指向的字符串是常量,不可更改。
int main() { char *a="hello"; f1(a); cout<<a<<endl; return 0; }
无输出错误为图6:
图6
9. 现在有以下两个函数,调用test的结果是(B) //-----------------------腾讯2014实习生笔试
char* getMem(void) { Char * p = “hello world ”; P[5] = 0x0; Return p; } void test(void) { char *s = 0x0; s = getMem(); Printf(s); }
A. hello B. 无输出 C. Hello0world D. 不确定
此题也将出现图6的结果,因为指针指向的字符串,是常量。只读不可写。
(2)虚函数与多态性、构造函数与析构函数
9 在32位环境下,以上程序的输出结果是(2014)//-----------------------腾讯2014实习生笔试
class Base { public: virtual int foo(int x){return x*10;} int foo(char x[14]){return sizeof(x)+10;} }; class Derived:public Base { int foo(int x){return x*20;} virtual int foo(char x[10]){return sizeof (x)+20;} }; int main(void) { Derived stDerived; Base * pstBase=& stDerived; char x[10]; printf(“%d ”,pstBase->foo(100)+pstBase->foo(x)); return 0; }
--------------------------------------------2014腾讯实习生笔试题
class Base { public: Base(){cout << "Base Construction"<<endl;} ~Base(){cout << "Base Destruction"<<endl;}//(2) }; class Derived:public Base { public: Derived(){ cout << "Derived Construction"<<endl;} ~Derived(){cout << "Derived Destruction"<<endl;}(1) }; int main() { Base *base = new Derived(); delete base; return 0; }
A先调用(1)再调用(2) B先调用(2)再调用(1 ) C只调用(2) D只调用(1)
程序运行结果为图7,因此只调用了基类析构函数。如果析构函数不被声明成虚函数,则编译器实施静态绑定,在删除基类指针时,只会调用基类的析构函数而不调用派生类析构函数,这样就会造成派生类对象析构不完全。
图7
若主函数改成如下程序,则运行结果为图8,派生类的指针,delete时,先调用派生类析构函数,再调用基类析构函数。
int main() { Base *base = new Derived(); delete base; Derived *base1 = new Derived(); delete base1; return 0; }
图8
若将派生类析构函数改为虚函数,则结果为图9,此时,delete基类指针时,先调用派生类析构函数,再调用基类析构函数。
class Base { public: Base(){cout << "Base Construction"<<endl;} virtual ~Base(){cout << "Base Destruction"<<endl;}//(2) };
图9
若将派生类的析构函数改为虚函数,基类的函数为一般析构函数,运行时卡在一个地方。说是内存泄露,不是很明白原因。结果为图10.
class Derived:public Base { public: Derived(){ cout << "Derived Construction"<<endl;} virtual ~Derived() {cout << "Derived Destruction"<<endl;} };
图10
但若再基类加一个虚函数。运行又不卡住,不知是何原因,望求高人指点。结果为图11
class Base { public: Base(){cout << "Base Construction"<<endl;} ~Base(){cout << "Base Destruction"<<endl;} virtual void foo(){cout<<"Base foo"<<endl;} }; class Derived:public Base { public: Derived(){ cout << "Derived Construction"<<endl;} virtual ~Derived(){cout << "Derived Destruction"<<endl;} };
图11
-------------------------------------阿里笔试题
class Base { public: int bar(char x) { return (int)(x); } virtual int bar(int x) { return 2*x; } }; class Derive:public Base { public: virtual int bar(char x) { return (int)(-x); } int bar(int x) { return x/2; } }; class Derive2:public Derive { public: int bar(char x) { return (int)(-2*x); } int bar(int x) { return x/4; } };
运行结果为图12,是不是有些出乎意料啊,哈哈
图12
(3)变量所占字节数及存储位置
13.请看一下这一段C++代码,如果编译后程序在windows下运行,则一下说话正确的是(AC)-------------腾讯2014实习生笔试
Char*p1 = “123456”;
Char*p2 = (char*)malloc(10);
A. P1 和 p2都存在栈中
B. P2指向的10个字节内存在栈中
C. 堆和栈在内存中的生长方向是相反的
D. “123456”这6个字符存储在栈中
看了图12,你就懂了。。。。
图13
8. Char p1[] = “Tencent”, void *p2 = malloc((10)在32位机器上sizeof(p1)和sizeof(p2)对应的值是(C) //--------腾讯2014实习生笔试
A. 8:0 B. 4:10 C. 8:4 D. 4:4
2 . 64位系统上,定义的变量int *a[2][3]占据(D)字节 //-------------------阿里2014实习生笔试
A 4 B 12 C 24 D 48
(4)时间复杂度
2. 假设函数f1的时间复杂度O(n),那么f1*f1的时间复杂度为(A)
//----------------------------腾讯2014实习生笔试
A. O(n) B. O(n*n) C. O(n*log(n)) D. 以上都不对
这个题真的比较坑爹啊,这里的*是f1的结果相称,不是复杂度相乘,f1就调用了2次,哎。。。。>....<
11. 在一台主流配置的PC机上,调用f(35)所需要的时间大概是(C)
//---------------------------------------阿里2014实习生笔试
unsigned long long cnt=0; int f(int x) { int s=0; cnt++; while(x--) s+=f(x); return max(s,1); }
A 几毫秒 B几秒 C几分钟
D几小时
分析如下:
f(0)=1,f(1)=1+f(0)=2f(0),f(2)=1+f(1)+f(0)=4f(0),f(3)=1+f(2)+f(1)+f(0)=8f(0),f(n)=2^nf(0),f函数执行2^n次,分析结果正如图14所示。图14所示为:cout<<n<<": "<<cnt<<" "<<sum<<" time: "<<GetTickCount()-start<<endl;
输出依次为:n的取值、f()的执行次数cnt、sum的值、程序运行时间(单位ms)
图14
图15
实际n=35是,执行次数2^35=34359738368,运行时间为1368031ms约22.8分钟,PC配置为CPU 2G 22核
目前主流PC配置为主频3.5G 4核。设指令周期2-5,f()执行一次,要执行10条指令左右。因为是4核的,1s钟估测运算3.5G条指令。3.5*10^9/10=100s。这样算,大概只需要2分钟,当然,计算机不可能不做其他事。CPU也不可能100%使用,时间肯定大于2分钟。
(5)输出流执行顺序
图16的结果是不是让你震惊啊,输出流是从右向左运算的。和函数调用参数的运算顺序相同也是从右向左运算的。
图16
下面代码的输出,也让人奇怪,后加很好理解,输出流从右向左运算。前加就有点莫名其妙了。结果为图17,反汇编,有点长,不分析了哈。
int n=10; cout<<n<<" "<<n++<<" "<<n++<<endl; cout<<n<<" "<<n++<<" "<<n++<<" "<<++n<<" "<<++n<<endl;
图17
(6)函数参数执行顺序
void canshutest(int a,int b) { cout<<a<<endl<<b<<endl; } int f1(int a) { cout<<"a="<<a<<endl; return a; } int f2(int b) { cout<<"b="<<b<<endl; return b; } int main() { int n=10; canshutest(f1(++n),f2(++n)); canshutest(n++,n++); canshutest(++n,++n); return 0; }
图18
图19说明如下:
将变量n放入寄存器eax 然后eax+1 |
再将eax放入变量n |
变量n再赋给ecx |
ecx+1 |
ecx再赋给变量n |
然后变量n赋给寄存器edx |
edx压栈 |
调用f2 |
图19
图20
图21
看完以上3张图片,自然就明白了
(7)函数返回值
9 有以下程序,其执行结果是(B)
int test(char a,char b) { if (a) return b; } int main() { int a='0',b='1',c='2'; printf("%c ",test(test(a,b),test(b,c))); system("pause"); }
A 函数调用出错 B 2 C 0 D 1
int test(char a,char b) { if (a) return b; }
虽然编译是给出警告,若a为0时,函数返回0;并非出错。