關於2014年阿里、騰訊的實習生招聘,有些題目是陷阱。覺得有必要總結一下哈,在此感謝一下參與討論的筒子們,小芳,靜兒。
陷阱分類:
(1)指針與字符串
7. 閱讀下面代碼,程序會打印出來的值是(D)------------------------------(騰訊2014實習生筆試)
void f(char **p)
{
*p += 2;
}
void main()
{
char *a[] = { “123”,”abc”,”456”};
f(a);
printf(“%s\r\n”,*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;
}<span style="font-size:18px;"></span>
無輸出錯誤爲圖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\n”,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\n",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;並非出錯。