- 相關(guān)推薦
c語(yǔ)言面試題
1.gets()函數
問(wèn):請找出下面代碼里的問(wèn)題:
- #include<stdio.h>
- int main(void)
- {
- char buff[10];
- memset(buff,0,sizeof(buff));
- gets(buff);
- printf("\n The buffer entered is [%s]\n",buff);
- return 0;
- }
答:上面代碼里的問(wèn)題在于函數gets()的使用,這個(gè)函數從stdin接收一個(gè)字符串而不檢查它所復制的緩存的容積,這可能會(huì )導致緩存溢出。這里推薦使用標準函數fgets()代替。
2.strcpy()函數
問(wèn):下面是一個(gè)簡(jiǎn)單的密碼保護功能,你能在不知道密碼的情況下將其破解嗎?
- #include<stdio.h>
- int main(int argc, char *argv[])
- {
- int flag = 0;
- char passwd[10];
- memset(passwd,0,sizeof(passwd));
- strcpy(passwd, argv[1]);
- if(0 == strcmp("LinuxGeek", passwd))
- {
- flag = 1;
- }
- if(flag)
- {
- printf("\n Password cracked \n");
- }
- else
- {
- printf("\n Incorrect passwd \n");
- }
- return 0;
- }
答:破解上述加密的關(guān)鍵在于利用攻破strcpy()函數的漏洞。所以用戶(hù)在向“passwd”緩存輸入隨機密碼的時(shí)候并沒(méi)有提前檢查 “passwd”的容量是否足夠。所以,如果用戶(hù)輸入一個(gè)足夠造成緩存溢出并且重寫(xiě)“flag”變量默認值所存在位置的內存的長(cháng)“密碼”,即使這個(gè)密碼無(wú)法通過(guò)驗證,flag驗證位也變成了非零,也就可以獲得被保護的數據了。例如:
- $ ./psswd aaaaaaaaaaaaa
- Password cracked
雖然上面的密碼并不正確,但我們仍然可以通過(guò)緩存溢出繞開(kāi)密碼安全保護。
要避免這樣的問(wèn)題,建議使用 strncpy()函數。
作者注:最近的編譯器會(huì )在內部檢測棧溢出的可能,所以這樣往棧里存儲變量很難出現棧溢出。在我的gcc里默認就是這樣,所以我不得不使用編譯命令‘-fno-stack-protector’來(lái)實(shí)現上述方案。
3.main()的返回類(lèi)型
問(wèn):下面的代碼能 編譯通過(guò)嗎?如果能,它有什么潛在的問(wèn)題嗎?
- #include<stdio.h>
- void main(void)
- {
- char *ptr = (char*)malloc(10);
- if(NULL == ptr)
- {
- printf("\n Malloc failed \n");
- return;
- }
- else
- {
- // Do some processing
- free(ptr);
- }
- return;
- }
答:因為main()方法的返回類(lèi)型,這段代碼的錯誤在大多數編譯器里會(huì )被當作警告。main()的返回類(lèi)型應該是“int”而不是“void”。因為“int”返回類(lèi)型會(huì )讓程序返回狀態(tài)值。這點(diǎn)非常重要,特別當程序是作為依賴(lài)于程序成功運行的腳本的一部分運行時(shí)。
4.內存泄露
問(wèn):下面的代碼會(huì )導致內存泄漏嗎?
- #include<stdio.h>
- void main(void)
- {
- char *ptr = (char*)malloc(10);
- if(NULL == ptr)
- {
- printf("\n Malloc failed \n");
- return;
- }
- else
- {
- // Do some processing
- }
- return;
- }
答:盡管上面的代碼并沒(méi)有釋放分配給“ptr”的內存,但并不會(huì )在程序退出后導致內存泄漏。在程序結束后,所有這個(gè)程序分配的內存都會(huì )自動(dòng)被處理掉。但如果上面的代碼處于一個(gè)“while循環(huán)”中,那將會(huì )導致嚴重的內存泄漏問(wèn)題!
提示:如果你想知道更多關(guān)于內存泄漏的知識和內存泄漏檢測工具,可以來(lái)看看我們在Valgrind上的文章。
5.free()函數
問(wèn):下面的程序會(huì )在用戶(hù)輸入'freeze'的時(shí)候出問(wèn)題,而'zebra'則不會(huì ),為什么?
- #include<stdio.h>
- int main(int argc, char *argv[])
- {
- char *ptr = (char*)malloc(10);
- if(NULL == ptr)
- {
- printf("\n Malloc failed \n");
- return -1;
- }
- else if(argc == 1)
- {
- printf("\n Usage \n");
- }
- else
- {
- memset(ptr, 0, 10);
- strncpy(ptr, argv[1], 9);
- while(*ptr != 'z')
- {
- if(*ptr == '')
- break;
- else
- ptr++;
- }
- if(*ptr == 'z')
- {
- printf("\n String contains 'z'\n");
- // Do some more processing
- }
- free(ptr);
- }
- return 0;
- }
答:這里的問(wèn)題在于,代碼會(huì )(通過(guò)增加“ptr”)修改while循環(huán)里“ptr”存儲的地址。當輸入“zebra”時(shí),while循環(huán)會(huì )在執行前被終止,因此傳給free()的變量就是傳給malloc()的地址。但在“freeze”時(shí),“ptr”存儲的地址會(huì )在while循環(huán)里被修改,因此導致傳給free()的地址出錯,也就導致了seg-fault或者崩潰。
6.使用_exit退出
問(wèn):在下面的代碼中,atexit()并沒(méi)有被調用,為什么?
- #include<stdio.h>
- void func(void)
- {
- printf("\n Cleanup function called \n");
- return;
- }
- int main(void)
- {
- int i = 0;
- atexit(func);
- for(;i<0xffffff;i++);
- _exit(0);
- }
這是因為_(kāi)exit()函數的使用,該函數并沒(méi)有調用atexit()等函數清理。如果使用atexit()就應當使用exit()或者“return”與之相配合。
7. 寫(xiě)一個(gè)“標準”宏MIN,這個(gè)宏輸入兩個(gè)參數并返回較小的一個(gè)。
#define MIN(A,B) ((A) <= (B) (A) : ))
這個(gè)測試是為下面的目的而設的:
1). 標識#define在宏中應用的基本知識。這是很重要的,因為直到嵌入(inline)操作符變?yōu)闃藴蔆的一部分,宏是方便產(chǎn)生嵌入代碼的唯一方法,對于嵌入式系統來(lái)說(shuō),為了能達到要求的性能,嵌入代碼經(jīng)常是必須的方法。 2). 三重條件操作符的知識。這個(gè)操作符存在C語(yǔ)言中的原因是它使得編譯器能產(chǎn)生比if-then-else更優(yōu)化的代碼,了解這個(gè)用法是很重要的。 3). 懂得在宏中小心地把參數用括號括起來(lái) 4). 我也用這個(gè)問(wèn)題開(kāi)始討論宏的副作用,例如:當你寫(xiě)下面的代碼時(shí)會(huì )發(fā)生什么事? least = MIN(*p++, b);
8. 嵌入式系統中經(jīng)常要用到無(wú)限循環(huán),你怎么樣用C編寫(xiě)死循環(huán)呢?
這個(gè)問(wèn)題用幾個(gè)解決方案。我首選的方案是: while(1) { } 一些程序員更喜歡如下方案: for(;;) { } 這個(gè)實(shí)現方式讓我為難,因為這個(gè)語(yǔ)法沒(méi)有確切表達到底怎么回事。如果一個(gè)應試者給出這個(gè)作為方案,我將用這個(gè)作為一個(gè)機會(huì )去探究他們這樣做的 基本原理。如果他們的基本答案是:“我被教著(zhù)這樣做,但從沒(méi)有想到過(guò)為什么。”這會(huì )給我留下一個(gè)壞印象。 第三個(gè)方案是用 goto Loop: ... goto Loop; 應試者如給出上面的方案,這說(shuō)明或者他是一個(gè)匯編語(yǔ)言程序員(這也許是好事)或者他是一個(gè)想進(jìn)入新領(lǐng)域的BASIC/FORTRAN程序員。
9. 嵌入式系統總是要用戶(hù)對變量或寄存器進(jìn)行位操作。給定一個(gè)整型變量a,寫(xiě)兩段代碼,第一個(gè)設置a的bit 3,第二個(gè)清除a 的bit 3。在以上兩個(gè)操作中,要保持其它位不變。
對這個(gè)問(wèn)題有三種基本的反應 1). 不知道如何下手。該被面者從沒(méi)做過(guò)任何嵌入式系統的工作。 2). 用bit fields。Bit fields是被扔到C語(yǔ)言死角的東西,它保證你的代碼在不同編譯器之間是不可移植的,同時(shí)也保證了的你的代碼是不可重用的。我最近不幸看到 Infineon為其較復雜的通信芯片寫(xiě)的驅動(dòng)程序,它用到了bit fields因此完全對我無(wú)用,因為我的編譯器用其它的方式來(lái)實(shí)現bit fields的。從道德講:永遠不要讓一個(gè)非嵌入式的家伙粘實(shí)際硬件的邊。 3). 用 #defines 和 bit masks 操作。這是一個(gè)有極高可移植性的方法,是應該被用到的方法。最佳的解決方案如下: #define BIT3 (0x1<<3) static int a; void set_bit3(void) { a |= BIT3; } void clear_bit3(void) { a &= ~BIT3; } 一些人喜歡為設置和清除值而定義一個(gè)掩碼同時(shí)定義一些說(shuō)明常數,這也是可以接受的。我希望看到幾個(gè)要點(diǎn):說(shuō)明常數、|=和&=~操作。
10. 嵌入式系統經(jīng)常具有要求程序員去訪(fǎng)問(wèn)某特定的內存位置的特點(diǎn)。在某工程中,要求設置一絕對地址為0x67a9的整型變量的值為0xaa66。編譯器是一個(gè)純粹的ANSI編譯器。寫(xiě)代碼去完成這一任務(wù)。
這一問(wèn)題測試你是否知道為了訪(fǎng)問(wèn)一絕對地址把一個(gè)整型數強制轉換(typecast)為一指針是合法的。這一問(wèn)題的實(shí)現方式隨著(zhù)個(gè)人風(fēng)格不同而不同。典型的類(lèi)似代碼如下: int *ptr; ptr = (int *)0x67a9; *ptr = 0xaa55;
一個(gè)較晦澀的方法是: *(int * const)(0x67a9) = 0xaa55;
即使你的品味更接近第二種方案,但我建議你在面試時(shí)使用第一種方案。
【c語(yǔ)言面試題】相關(guān)文章:
c語(yǔ)言心得05-17
c語(yǔ)言的就業(yè)方向08-11
基礎C++/C語(yǔ)言筆試題分享11-21
華為C語(yǔ)言筆試題12-12
華為筆試題(C語(yǔ)言)12-10
c語(yǔ)言實(shí)習心得09-01
C語(yǔ)言實(shí)訓心得12-01
C語(yǔ)言實(shí)習心得01-01
C語(yǔ)言的論文致謝詞11-25
C++語(yǔ)言的基本符號10-11