C/C++ - 常見C 語言觀念題目總整理(適合考試和面試)

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

標籤: Software Development-C/Cpp. 常見的C/C++ 問題總整理,增強C 語言觀念並練習一些題目,努力脫離考試苦海吧! (非從零開始,適合稍有C/C++ ... Pages Home About Contact skiptomain| skiptosidebar 2017年8月7日星期一 C/C++-常見C語言觀念題目總整理(適合考試和面試) 於 8/07/201704:56:00下午 標籤: SoftwareDevelopment-C/Cpp   常見的C/C++問題總整理,增強C語言觀念並練習一些題目,努力脫離考試苦海吧! (非從零開始,適合稍有C/C++基礎的人閱讀。

) 2017.08.07初版 2018.01.28新增部分內容 一、指標 指標(pointer):一個指向某個儲存位址的變數,語法為 int*ptr=&var; 其中 &:取變數位址 *:表示為指標變數 也可用於函數變為函式指標(functionpointer),語法為 void(*fptr)(type_a,type_b)=&func; 常用的地方如下 函式sort時傳入判斷準則 multithread傳函數進入建立thread的API中 callbackfunction(一種事件導向的函式) 1.基礎指標判讀 指標判讀大原則為「從右讀到左」,例如: inta;//一個整型數 int*a;//一個指向整數的指標 int**a;//一個指向指標的指標,它指向的指標是指向一個整型數 inta[10];//一個有10個整數型的陣列 int*a[10];//一個有10個指標的陣列,該指標是指向一個整數型的 int(*a)[10];//一個指向有10個整數型陣列的指標 int(*a)(int);//一個指向函數的指標,該函數有一個整數型參數並返回一個整數 int(*a[10])(int);//一個有10個指標的陣列,該指標指向一個函數,該函數有一個整數型參數並返回一個整數 注意宣告兩個指標時不能寫做int*a,b;因為前式等價於int*a;intb;。

連續宣告兩個指標用int*a,*b; 2.指標與其他關鍵字混用 一樣右讀到左,例如: constint*foo;//一個pointer,指向constint變數。

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

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

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

關鍵字volatile等等判讀方式相同。

二、callbyvalue,callbyreference 1.callbyvalue:最常見的函式寫法,呼叫者和被呼叫者的變數各自佔有記憶體,將參數複製再傳給函式。

2.callbyreference:呼叫者和被呼叫者的變數使用相同的記憶體位址,因此在被呼叫函式中改變變數時,變動結果會保留。

(C++才有,寫法為typefunc(type&var){...}) [用心去感覺]callbyaddress(?) C語言之父明確表示C語言只有callbyvalue。

坊間有callbyaddress的說法其實是方便教學,指的是對指標變數進行操作的callbyvalue,具體的執行效果和callbyreference一樣。

下面兩篇文章對這個觀念講得很清楚: MLab-什麼是傳值callbyvalue、傳址callbyaddress、傳參考callbyreference http://wp.mlab.tw/?p=176 郭晉魁教授-c語言有沒有callbyreference(orcallbyaddress)? http://eportfolio.lib.ksu.edu.tw/~T093000170/blog?node=000000119 三、變數範圍和生命周期(關鍵字static) 1.local變數:local變數僅活在該函式內,存放位置在stack或heap記憶體中。

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

3.global變數:所有區段皆可使用此變數。

[用心去感覺]staticvsglobal 除了範圍不同,static變數只有宣告的檔案可以使用;而global變數可加上extern關鍵字修飾,即可在其他檔案以.h標頭檔方式使用該變數(也就是internallinkage和externallinkage的不同)。

[用心去感覺]記憶體的配置 Stack:存放函數的參數、區域變數等,由空間配置系統自行產生與回收。

(會稱作stack是由於其配置遵守LIFO) Heap:一般由程式設計師分配釋放,執行時才會知道配置大小,如malloc/new和free/delete。

(注意其資料結構不是DS中的heap而是link-list) Global : 包含 BSS(未初始化的靜態變數)、datasection(全域變數、靜態變數)和text/code(常數字元)。

[重要!]process在記憶體中的配置圖 [例題]配置練習 inta=0;//global初始化區 char*p1;//global未初始化區 main(){ intb;//stack chars[]="abc";//stack char*p2;//stack char*p3="123456";//123456\0在常量區,p3在stack。

staticintc=0;//global(static)初始化區 p1=(char*)malloc(10); p2=(char*)malloc(20);//分配得來得10和20位元組的區域在heap strcpy(p1,"123456"); //123456\0在常量區,編譯器可能會將它與p3中的123456\0優化成一個地方。

} [例題]static練習 staticintnum_a; //專屬於整個檔案的全域變數,其他檔案不能存取 voidfunc(intnum_b){//stack區 intnum_c;//stack區 staticintnum_d; //scope不變,只能在函數func內呼叫,但lifetime是整支程式執行的時間。

} 四、關鍵字const const通常表示只可讀取不可寫入的變數,常用來宣告常數。

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

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

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

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

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

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

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

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

所以它是volatile。

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

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

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

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

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

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

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

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

七、前處理器相關 前處理器主要處理加入檔案#include、巨集定義#define和#undef條件編譯。

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

define語法範例如下: #definePI3.1415926//常數巨集 #defineA(x)x//函數巨集 #defineMIN(A,B)((A)<=(B)?(A):(B)) 注意把參數用括號括起來,不然容易發生以下錯誤: #defineSUM(a,b)a+b 當SUM(2,5)*10時,因為沒有括弧先乘除後加減,得輸出為52,錯誤。

2.引入防護和條件編譯 引入防護(Includeguard)是一種條件編譯,用於防範#include指令重複引入的問題。

/*避免重複引入*/ #ifndefMYHEADER #defineMYHEADER ... #endif 第一次被引入時會定義巨集MYHEADER,再次引入時判斷#ifndef測試失敗,因此編譯器會直接跳到#endif,由此避免了重複引用。

另有非標準的指令#pragmaonce提供相同效果,但由於可攜性不如上例,因此大多時候還是上面提到的方法為主。

條件編譯還有一些其它應用: /*若前處理器已經defineMYHEADER,就編譯partA,否則編譯partB。

*/ #ifdefMYHEADER #defineMYHEADER //partA #else //partB #endif /*DEBUGflag*/ #ifdefDEBUG print("device_open(%p)",file); #endif 八、bitwiseoperator 邏輯上的運算子在C中的語法分別如下: AND(&) OR(|) NOT(!) XOR(^)//bit值不一樣為1 complement(~) shift(<>) bitwise的操作常與"0x"這種16進位表示法,方便轉換操作。

[例題]基本運算 unsignedlongnum_a=0x00001111; unsignedlongnum_b=0x00000202; unsignedlongnum_c; num_c=num_a&(~num_b); num_c=num_c|num_b; printf("%lx",num_c);//00001313 [例題]mask方法做bitwise操作 a=a|7//最右側3位設為1,其餘不變。

a=a&(~7)//最右側3位設為0,其餘不變。

a=a^7//最右側3位執行NOToperator,其餘不變。

九、複製:memcpy和strcpy 1.記憶體複製 void*memcpy(void*dest,constvoid*src,size_tcount); memcpy()可以複製任何類型資料,不處理字串結束'\0'的情況,當*src長度大於*dest時會bufferoverflow(編譯時不會錯誤)。

2.字串複製 void*strcpy(void*dest,constvoid*src); strcpy()只能用於字串複製,不需要指定長度,因為會自動偵測以'\0'為結尾,當*src長度大於*dest時會bufferoverflow(*dest將沒有\0)。

舉例 #include #include intmain(){ constchar*str1="abc\0def"; charstr2[16]={0}; charstr3[16]={0}; strcpy(str2,str1); memcpy(str3,str1,sizeof(str3));//8 printf("str2=%s\n",str2);//str2=abc printf("str3=%c\n",str3[5]);//str3=e return0; } 十、延伸性資料型態:struct、typedef、union和enum 1.結構struct struct是使用者自定的型態,包含數個不同資料型態的變數,將不同的資料型態關聯在一起,使他們的關聯更直覺。

struct[structName]{ charname[16]; intage; struct[structName]*ptr; //不能含有自己,但可以有自己型別的指標。

}; intmain(){ struct[structName]person1={"Amy",20};//初始化 person.age=21;//操作 } 2.重新定義型態名稱typedef typedef保留字可以為資料型態建立別名,使程式更易閱讀理解。

例如: typeofstruct[structName]{ charname[16]; intage; struct[structName]*ptr; }PERSON; intmain(){ PERSONperson1={"Amy",20}; person.age=21; } 3.列舉enum enum是一種常數定義方式,可以提升可讀性,enum裡的識別字會以int的型態,從指定的值開始遞增排列(預設為0)。

typedefenum{SUN=0,MON,TUE,WED,THU,FRI,SAT}week_type; week_typeweek=WED; if(week==WED) cout<



請為這篇文章評分?