寥寥數筆,記錄我的C語言盲點筆記,只為曾經經歷過,亦有誤,可交流。
1.typedef來定義一個函數指針類型的方法,定義一個新的函數指針類型。:建立一個類型別名的方法很簡單,在傳統的變量聲明表達式裡用類型名替代變量名,然後把關鍵字typedef加在該語句的開頭”。typedef int (*PFUN)(): 定義PFUN這個函數指針類型,由編譯器自動來完成,比如PFUN pfun;定義了一個函數指針,類似於抽象出一種新的變量類型。
typedef unsigned short (*pTaskEventHandlerFn)( unsigned char task_id, unsigned short event );
pTaskEventHandlerFn tasksArr[];//申明函數指針的數組
使用typedef將一個類型名字替代原先申明時的變量名,然後類型名可以用來申明定義變量。
2.有初始化的數組定義可以省略方括號中的數組大小。
例如,下面的代碼中數組定義為5個元素:
int a[]={2,4,6,8,10};
編譯時必須知道數組的大小。通常,聲明數組時方括號內的數字決定了數組的大小。有初始化的數組定義又省略方括號中的數組大小時,編譯器統計花括號之間的元素個數,以求出數組的大小。
例如,下面的代碼產生相同的結果:
static int a1[5]={1,2,3,4,5};
static int a2[]={1,2,3,4,5};
2,棧頂一般為低地址,函數入棧一般參數從右往左進行。
3.for(a;b;c){e}執行順序:先進行a, 然後是b, 執行e, 在執行c.進一步執行b,執行e,c.直到b不再滿足退出循環。
4.union數據類型,內存是重疊的,完全一樣typedef union {
uint32 time32;
uint16 time16[2];
uint8 time8[4];
} osalTime_t;
對time32的賦值後,訪問其他類型時依舊是原來的內存。三個成員占據一樣的數據空間,起始地址都是一樣的,一旦一個成員數值變化就會出現其他成員取值也發生變化
5.inline內聯函數總結內聯函數具有代碼量小且直接編譯進可執行文件中,不進行call調用,也就是在不同的文件的函數如果調用內聯函數,則不同的函數都會出現該內聯函數的可執行代碼。但是,如果不重復調用的話,內聯函數帶來的好處就是執行更加快速,無需再call,以及函數參數的出入棧SP,保護棧幀,跳出函數,清空棧幀等各種耗時。起到快速執行的效果。故內聯函數的代碼量都較小。總之是以增加執行文件大小來獲取程序執行速度的提高。
6.(int *)0,表示指向一個整形的地址,地址值為0,即空指針所在的地方,該處一般不允許寫操作,以免破壞系統,但可以讀取。
7.如果一個函數要被其他文件中函數使用,定義時加extern關鍵字,在沒有加extern和static關鍵字時,一般編譯器會默認是extern類型的,因此你在其他函數裡調用也可以使用。
8.結構體之offsetof宏詳細解析 ,#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE*)0)->MEMBER) (include/linux/stddef.h)
9.32位機讀取一個字節,實際底層的匯編做了進一步的處理。比如讀取一個字節,實際返回的是一個32位,經過屏蔽移位取得你要的地址所在的數。寫入時也一樣,會先保持4字節其他字節不變,然後單獨把要寫入的字節做改變。由編譯器根據你的數據類型來完成實際的實現。32字節的讀取最為高效。
10.中斷發生時,未完成服務程序的處理時,將會忽略掛起Pending,優先級高的中斷可以獲取中斷使用權。另一個同樣的中斷發生,直到當前中斷處理完成,才允許再次中斷進入。
11.switch case誤區。switch case往往對不同的命令進行分類操作,但是如果出現遇到對應case做完處理後,如果沒有遇到break就會接著默認執行下面的case內容(不再去case,因為只有剛進入switch才會去匹配case,已經進入後,case匹配忽略)。直到遇到帶break後為止跳出case,否則一直執行完。
因為只在第一次才做switch判斷跳到對應的內容去執行。所以,好的程序基本是需要break的,但是有些情況(多個情況對應處理的內容要一樣,即前面的幾個類型可以不做處理,不需要break,直到最後一個做處理,實現多個類型執行相同的功能,但不增加代碼量,更直接)。總之,只有在第一次才做判斷與選擇,進入以後就是安裝順序執行,不break會一直執行到switch結束
12 C語言中的感歎號!邏輯取反,不同於~的按位取反!=:表示不等於。!(1)表示為0,!(0)表示為1,有取反之意,但只能用在0和1上面。不等於0的會變為0,等於0的變為1
13.C語言+和<<遇到的一個BUG問題。在這裡+的優先級高於<<左右移。所以比如a<<8+b,實際是從右往左賦值,8+b相加後來移動a,所以不實現兩個字節合並為一個16位的數據。故要加(a<<8)+b才能實現本來的意圖,這個誤區需要注意。
14.:#define PINMUX0_31_28 0x00000001u,0x00000001ULu表示無符號,UL unsigned long int 32位,unsigned long long 64位
15數組指針和指針數組的區別數組指針,為一個指針,int (*a)[10];指向一個數組,類似於函數指針指針數組,為一個數組,int* a[10];類似於指針函數,返回的是一個指針。
16.window下面絕對路徑的文件需要用雙斜槓來表示\\
17 printf("c=%#x\n",c);什麼意思?
%#表示的輸出提示方式,如果是8進制,在前面加0,如果是十進制,不加任何字符,如果是十六進制,會自動加上0x
18 靜態變量在編譯的時候初始化,所以初值必須是常量(可以是常數、定義成常數的宏、C++編譯器中用const修飾的常量),靜態變量不能用變量賦初始值,但在運行時是可以用變量賦值的。
19 assert函數,用於判斷是否為真,滿足就繼續執行,不然直接終止程序的運行 20.做到4個字節對齊,使用如下方式:((CSL_CacheRegsOvly)CSL_CACHE_REGS)->L1DIWC = ((byteCnt+3)>>2);字節數不為4的倍數時,需要多保留一個字的數量,故加3,1+3,2+3,3+3.4字節對齊的方式,獲得4字節對齊後的字節數字:byte_num = (byte + 3) & 0xfc; 這樣字節數必為4的倍數。
21 for循環中如果break,則i++不再執行。
22.temp = (temp+1)&7.表示temp從0到7變化,循環。類似於temp=8時賦值回0.
23.unsigned char *p;unsigned char a[];p=a;這樣正確。
unsigned int p1= a;錯誤,因為是指針類型賦值給了整形的數據類型,類型不匹配。unsigned char * p[10]:申明一個指針數組,10個指針,每個指針指向char類型的空間。unsigned char (*p)[10]申明一個數組指針,指向含有10個char類型的空間,指針維護一個10的空間,偏移1加10個空間。
24.多個if會進行分支處理,分別判斷後進行是否執行。而if 和多個else if等配合時,一旦出現某個滿足條件時,不再執行下面的else if的判斷,確保只進入一個分支。
25. char a[3][4]. char **p =a;出現編譯錯誤,類型不匹配不能賦值因為a表示的是一個指向一維數組的指針,即char (*a)[3]這個類型.a+1是會偏移3個單位。p是一個指針的指針,稱為二級指針。所以兩者類型不一樣。
26.數組名取地址在《C和指針》p142中說到,在以下兩中場合下,數組名並不是用指針常量來表示,就是當數組名作為sizeof操作符和單目操作符&的操作數時。 sizeof返回整個數組的長度,而不是指向數組的指針的長度。 取一個數組名的地址所產生的是一個指向數組的指針,而不是一個指向某個指針常量的指針。
所以&a後返回的指針便是指向數組的指針(類似於(*p)[3]),跟a(一個指向a[0]元素的指針)在指針的類型上是有區別的。&a+1是偏移一個數組sizeof大小的尺寸,因為&a是指向數組指針的地址,是一個數值指針。但是作為形參時,一級數組名就轉為了一個普通的一級指針而已。
27. 二維數組名不能簡單的理解為2級指針,而是要從數組的系統結構上去了解。個人認為二維數組名其實就是一個指針,但是他指向的數據類型是一維數組的這麼個數據類型。即數值指針,其實可以理解為功能和二維數組名類似。至於數據訪問,完全是編譯器來自動完成棧區的訪問。
28.數值名不能修改,是因為在棧區域內已經定好了ebp,基地址。訪 問數組都是基於這個ebp來進行的。所以,給數組名賦值變得沒有意義,肯定不允許修改ebp的。所以在編譯階段就報錯。
29.fseek和發tell配合來完成確定文件大小的過程。fseek(fPtr,0,SEEK_END);file_size = ftell(fPtr);fseek(fPtr,0,SEEK_SET),位置指針復位進行讀寫操作
30.兩數交換不使用第三個緩沖區,三條代碼的方法:主要通過運算符來實現。
A = A^B;B = A^B;A = B^A;
A = A+B;B = A-B;A = A-B;