C面試考題

文章推薦指數: 80 %
投票人數:10人

前指標指的內容不可變動,後指標不可變動; void dp()const; \我不會更改成員; const int* op() const{return &data;} //回傳 ...        ownedthisnote   Published LinkedwithGitHub Like28 Bookmark Subscribe Edit #C面試考題 >2018.1.9AndyLee ##1.FunctionPointer void(*fptr)(type_a,type_b)=&func; ```clike= #include intadd(inta,intb){ returna+b; } intmult(inta,intb){ returna*b; } intmain(){ int(*op)(inta,intb); op=add; printf("op(3,5)=%d\n",op(3,5)); op=mult; printf("op(3,5)=%d\n",op(3,5)); } ``` --- ##2.FunctionPointerType typedefvoid(*F1)(void*); ```clike= #include typedefint(*OP)(int,int); intadd(inta,intb){ returna+b; } intmult(inta,intb){ returna*b; } intmain(){ OPop=add; printf("op(3,5)=%d\n",op(3,5)); op=mult; printf("op(3,5)=%d\n",op(3,5)); } ``` --- ##3.指標判讀 ```clike= inta;//一個整型數 int*a;//一個指向整數的指標 int**a;//一個指向指標的指標,它指向的指標是指向一個整型數 inta[10];//一個有10個整數型的陣列 int*a[10];//一個有10個指標的陣列,該指標是指向一個整數型的 int(*a)[10];//一個指向有10個整數型陣列的指標 int(*a)(int);//一個指向函數的指標,該函數有一個整數型參數並返回一個整數 int(*a[10])(int);//一個有10個指標的陣列,該指標指向一個函數,該函數有一個整數型參數並返回一個整數 ``` ```clike= constint*foo;//一個pointer,指向constint變數。

intconst*foo;//一個pointer,指向constint變數。

int*constfoo;//一個constpointer,指向int變數。

intconst*constfoo;//一個constpointer,指向constint變數。

``` --- ##4.CallbyValue,CallbyReference,CallbyAddress 1.**callbyvalue**:最常見的函式寫法,呼叫者和被呼叫者的變數各自佔有記憶體,將==參數複製==再傳給函式。

>註:C語言之父明確表示C語言只有callbyvalue。

```clike= voidswap(intc,intd){ inttemp=c; c=d; d=temp; } intmain(){   inta=5,b=10;   swap(a,b);   printf("%d%d",a,b); } ``` ` Ans:5,10 ` 2.**Callbyreference**:呼叫者和被呼叫者的變數==使用相同的記憶體位址==,因此在被呼叫函式中改變變數時,變動結果會保留。

>註:傳參考是C++才有的東西,C語言是沒有的唷! ```clike= voidswap(int&c,int&d){    inttemp=c;    c=d;    d=temp; } intmain(){    inta=5,b=10;    swap(a,b);    printf("%d%d",a,b);    return0; } ``` ` Ans:10,5 ` >註:注意的是,一變數已經是別的變數的參考,就不可以再參考別的變數 ``` inta=5,b=10; int&c=a; printf("c=%d\\n",c); &c=b;//錯誤,因為他已經參考了a ``` ``` inta=5; int&c;  //錯誤,傳參考必須在宣告的時候就一起給參考的對象,沒辦法之後再給。

``` 3.**Callbyaddress** 傳指標,仍然也是callbyvalue,只不過複製的值Value剛好就是位址address >坊間有callbyaddress的說法其實是方便教學,指的是對指標變數進行操作的callbyvalue或是callbyvalueofpointer,具體的執行效果和callbyreference一樣。

```clike= voidswap(int*c,int*d){    inttemp=*c;    *c=*d;    *d=temp; } intmain(){    inta=5,b=10;    swap(&a,&b);//意思就是要把a跟b的位址給swap副程式c跟d使用    printf("%d%d",a,b); } ``` ` Ans:10,5 ` --- ##5.變數範圍和生命周期 1.local變數:local變數僅活在該函式內,存放位置在stack記憶體中。

2.static變數:static變數生命周期(lifetime)跟程式一樣長,而範圍(scope)則維持不變,即在宣告的函式之外仍無法存取static變數。

3.global變數:所有區段皆可使用此變數 >註:除了範圍不同,static變數只有宣告的檔案可以使用;而global變數可加上extern關鍵字修飾,即可在其他檔案以.h標頭檔方式使用該變數(也就是internallinkage和externallinkage的不同)。

--- ##6.關鍵字const const通常表示**只可讀取不可寫入的變數**,常用來宣告常數。

使用const有以下好處: *提升程式碼可讀性 *使編譯器保護那些不希望被改變的參數 *給優化器一些附加的資訊 編譯器處理方式:define在==預處理階段==展開;const在==編譯階段==使用。

類型和安全檢查:const會在編譯階段會執行類型檢查,define則不會。

存儲方式:define直接展開不會分配記憶體,const則會在記憶體中分配。

--- ##7.關鍵字volatile 由於嵌入式系統常處理I/O、中斷、即時操作系統(RTOS)相關的問題,因此在嵌入式系統開發中volatile尤為重要。

被volatile修飾的變數代表它可能會被不預期的更新,因此==告知編譯器不對它涉及的地方做最佳化==,並在每次操作它的時候都讀取該變數實體位址上最新的值,而不是讀取**暫存器**的值。

###volatile常見的應用: *修飾中斷處理程式中(ISR)中可能被修改的全域變數。

*修飾多執行緒(multi-threaded)的全域變數。

*設備的硬體暫存器(如狀態暫存器) ###const和volatile合用 ```javascript= externconstvolatileunsignedintrt_clock; ``` 這是在RTOSkernel常見的一種宣告:rt_clock通常是指系統時鐘,它經常被時鐘中斷進行更新。

所以它是volatile。

因此在用的時候,要讓編譯器每次從記憶體裡面取值。

而rt_clock通常只有一個寫者(時鐘中斷),其他地方對其的使用通常都是唯讀的。

所以將其聲明為const,表示這裏不應該修改這個變數。

所以volatile和const是兩個不矛盾的東西,並且一個物件同時具備這兩種屬性也是有實際意義的。

--- ##8.關鍵字inline inline可以將修飾的函式設為行內函式,即像巨集(#define)一樣==將該函式展開編譯,用來加速執行速度。

== ###inline和#define的差別在於: *inline函數只對參數進行一次計算,避免了部分巨集易產生的錯誤。

*inline函數的參數類型被檢查,並進行必要的型態轉換。

*巨集定義盡量不使用於複雜的函數 ***用inline後編譯器不一定會實作,僅為建議**。

--- ##9.巨集#define #define是巨集,在前置處理器(preprocessor)執行時處理,將要替換的程式碼展開做文字替換。

define語法範例如下:`` ```clojure= #definePI3.1415926//常數巨集 #defineA(x)x//函數巨集 #defineMIN(A,B)((A)<=(B)?(A):(B)) ``` 注意把參數用括號括起來,不然容易發生以下錯誤: ```javascript= #defineSUM(a,b)a+b ``` 當SUM(2,5)*10時,因為沒有括弧先乘除後加減,得輸出為52,錯誤 正確寫法: ```javascript= #defineSUM(a,b)(a+b) ``` --- ##10.未定義行為(Undefinedbehavior) 常見的語法i++和++i具有以下性質: *i++:先用i,再將i+1 *++i:先i+1,再用i 因此會出現這種問題: ``` inti=10 i=i+++++i; ``` 這個問題的標準答案是 ``` i=i+++++i; i=10+++i; i=10+12; i=22 ``` >語言規格在定義時為了編譯器實做上的彈性和效率考量,會刻意不去規定某些規格,因此如果寫出來的程式依賴或著錯用某些沒有在規格內所規定的特性時,我們就稱之為Undefinedbehavior, > reference:[萬惡的未定義行為](http://blog.ez2learn.com/2008/09/27/evil-undefined-behavior/) --- ##11.extern *可以聲明變數會在其它的位置被定義,這個位置可能是在同一份文件之中,或是在其它文件之中 *不能在使用extern時同時指定其值,必須先聲明extern找到變數,再重新指定其值 --- ##12.CallBackFunction 簡單的說,如果你使用了某個function,那麼你就是『call』了一個function。

如果系統或是函式是要求你給一個functionpointer,這個functionpointer指到一個實際的函式。

然後它會在適當的時間呼叫此function,則此function就是所謂的callbackfunction。

--- ##13.Pointer ![](http://2.bp.blogspot.com/-9uBLcFbyYEI/VJfCVjtiPaI/AAAAAAAAAe0/HBGisvl4zus/s1600/2014122201_%E6%8C%87%E6%A8%99%E7%A4%BA%E6%84%8F%E5%9C%96.png) *指標空間釋放(delete)後,指標變數仍會記錄其位址,為避免誤用,釋放後應指向NULL *==有new就要有delete==,若在副程式new又需回傳指標,則需在主程式接收指標後delete該空間,但不建議在副程式new記憶體且回傳指標,因若封裝給別人使用,別人不知道副程式裡面有new而沒delete掉,就會造成memoryleak --- ##14.constint*p和int*constq兩者之差別: *前指標指的內容不可變動,後指標不可變動 *voiddp()const;\\我不會更改成員 *constint*op()const{return&data;}//回傳const參考保證不會更改 --- ##15.寫出一個Macro求出兩數之最大值: ```javascript= #defineMax(x,y)((x)>=(y)?(x):(y)) ``` --- ##16.指標與陣列的差別? 就記憶體方向來看,**指標**所用的記憶體位置不為連續,而"矩陣"所配置的空間為連續。

--- ##17.給予10個任意整數,輸出其最小值、最大值、平均值 ```javascript= intMax=a[0],Min=a[0],Avg=0; for(inti=0;i<10;i++) { if(Min>a[i]) Min=a[i]; if(Max(Avg)/10<0&&(n&(n-1))==0; } ``` 二進位中,只要是2的次方,都剛好會是像100(4)或者1000(8)這種1開頭,尾巴都是0的狀況 ``` 8的二進位1000 7的二進位0111 1000 & 0111 ----- 0000=0(returntrue) ``` Reference:http://davidhsu666.com/410/ispowerby2 --- ##23.下列程式碼輸出什麼 ```javascript= inta[10]={1,2,3,4,5,6,7,8,9,10}; int*p=&(a+1)[3]; printf("%d\n",*p); ``` *輸出5 *因為a+1指向a的第二個元素,[3]表示再向後移動3個元素 *a+1是跳一個int的大小(a想成指標) *&a+1是跳一整個array的大小 --- ##24.連續呼叫func10次,印出的值為何? ```javascript= voidfunc(void){ staticinti=0; i++; printf("%d",i); } ``` ANS:12345678910 --- ##25.C檢查錯誤 ```clike= voidGetMemory(char*p){ p=(char*)malloc(100); } voidtest(){ char*str=NULL; GetMemory(str); strcpy(str,"helloworld"); printf(str); } ``` 传入GetMemory(char*p)函数的形参为字符串指针,在函数内部修改形参并不能真正的改变传入形参的值去执行完。

--- ```clike= voidGetMemory(void){ charp[]="helloworld"; returnp; } voidtest(){ char*str=NULL; str=GetMemory(); printf(str); } ``` 这段代码的p[]数组为函数内的局部自动变量,在函数返回后,内存已经被释放。

--- ```clike= voidGetMemory(char**p,intnum){ *p=(char*)malloc(num);//这段代码后面未进行内存申明成功的判断 } voidtest(){ char*str=NULL; GetMemory(&str,100); strcpy(str,"hello"); printf(str); } ``` 传入GetMemory的参数为字符串指针的指针,但是在GetMemory中执行申请内存及赋值语句。

\*p=(char\*)malloc(num); 后未判断内存是否申请成功,应加上: if(*p==NULL){ ....//进行申请内存失败处理 } --- ```clike= swap(int*p1,int*p2){ int*p; *p=*p1; *p1=*p2; *p2=*p; } ``` 在swap函数中,p是一个“野”指针,有可能指向系统区,导致程序运行的崩溃。

在VC++中DEBUG运行时提示错误“ACCESS Violation”。

程序应该改写为: ```clike= swap(int*p1,int*p2){ intp=*p1; *p1=*p2; *p2=p; } ``` --- ##26.下列印出什麼? > http://dummyh.pixnet.net/blog/post/9902458-%E3%80%90c%E8%AA%9E%E8%A8%80%E3%80%91%E9%9D%A2%E8%A9%A6%E8%80%83%E5%8F%A4%E9%A1%8C-%E7%B6%93%E9%A9%97%E5%88%86%E4%BA%AB---%E4%B8%AD%E7%B4%9A ![](https://pic.pimg.tw/dummyh/1478530816-2091497920.jpg?v=1478530817) A: 第一行字串為:0113234 第二行字串為:0123456 首先,*p=s所代表的含意為pointerp指向字串s的第一個位址0,而接下來一連串的運算可分成四類: |Operations|Meaning| |--------|--------| |*p++=*(p++) |先取值,後指標下移| |*++p=*(++p) |指標下移,後取值| |++*p=++(*p) |先把該值+1,後取值| |(*p)++| 先取值,後該值+1| --- 28 × Signin Email Password Forgotpassword or Byclickingbelow,youagreetoourtermsofservice. SigninviaFacebook SigninviaTwitter SigninviaGitHub SigninviaDropbox SigninviaGoogle NewtoHackMD?Signup



請為這篇文章評分?