电竞比分网-中国电竞赛事及体育赛事平台

分享

Linux C 經(jīng)典題目總結(jié)

 WUCANADA 2012-12-16

【第一部分 C基本概念】

【幾個(gè)關(guān)鍵字】

1)、auto關(guān)鍵字: 聲明變量的生存期為自動(dòng),即將不在任何類、結(jié)構(gòu)、枚舉、聯(lián)合和函數(shù)中定義的變量視為全局變量,而在函數(shù)中定義的變量視為局部變量。不明白?無視他好了,編譯器默認(rèn)的缺省情況下,所有的變量都是auto的。

2)、extern關(guān)鍵字: 我們都知道,一個(gè)變量或函數(shù),可以在a.c文件中定義,而在b.c文件中使用,這個(gè)時(shí)候,b.c就需要使用extern關(guān)鍵字來聲明這個(gè)變量和函數(shù),目的是為了告訴編譯器,這個(gè)函數(shù)在b.c之外,別讓我編譯不過!

3)、register關(guān)鍵字: 這個(gè)關(guān)鍵字就很少用到了,但是卻十分有用。它的目的是告訴編譯器盡量把這個(gè)變量放到寄存器中,這樣提高存取速度,但是不是能真的放到寄存器中卻不一定,畢竟寄存器的數(shù)量是有限的。在我們的二進(jìn)制翻譯器中,這個(gè)關(guān)鍵字被巧妙的用于線程切換。

4)、static關(guān)鍵字: 好吧,我承認(rèn)我土了,我就是栽在這個(gè)關(guān)鍵字上的。static有兩種修飾,分別如下:

(1)修飾變量:變量分為全局變量和靜態(tài)變量,都存儲在內(nèi)存的靜態(tài)區(qū)中。

首先,當(dāng)static修飾全局變量的時(shí)候,該變量的作用域僅被限定在當(dāng)前文件中,別的文件即使使用extern關(guān)鍵字也無法使用這個(gè)變量。

其次,當(dāng)static修飾局部變量的時(shí)候,該變量在哪個(gè)函數(shù)體中定義,就只能在哪個(gè)函數(shù)體中使用。也許你會說,這不跟普通局部變量一樣么?不一樣!別忘了他是被存儲在內(nèi)存的靜態(tài)區(qū)中,所謂的靜態(tài)區(qū)就是全局區(qū),用來存放全局變量和靜態(tài)變量的,程序不結(jié)束,這個(gè)區(qū)是不會被釋放的,所以即使定義靜態(tài)局部變量的函數(shù)結(jié)束,改靜態(tài)局部變量仍然存在,下次訪問改函數(shù)的時(shí)候,這個(gè)變量的值仍然是上次的值!

(2)修飾函數(shù): 經(jīng)常見這種形式,但沒怎么用過,也就沒去想。其實(shí)這個(gè)作用跟靜態(tài)全局變量相似,也是限定函數(shù)的作用域?yàn)楸疚募?。這樣作的好處就是不用操心是否會跟別人編寫的文件里的函數(shù)重名。

(3)注意:函數(shù)的形參不能聲明為靜態(tài),因?yàn)閷?shí)參總是在堆棧中傳遞給函數(shù),用于支持遞歸。

在《c和指針》中這樣描述:當(dāng)static用于函數(shù)定義時(shí),或用于代碼之外的變量聲明時(shí),static關(guān)鍵字用于修改標(biāo)識符的鏈接屬性,從external改為internal,但標(biāo)識符的存儲類型和作用域不受影響。用這種方式聲明的函數(shù)或變量只能在聲明它們的源文件中訪問。

當(dāng)static用于代碼塊內(nèi)部的變量聲明時(shí),static關(guān)鍵字用于修改變量的存儲類 型,從自動(dòng)變量修改為靜態(tài)變量,但變量的鏈接屬性和作用域不受影響。用這種方式聲明的變量在程序執(zhí)行之前創(chuàng)建,并在程序的整個(gè)執(zhí)行期間一直存在,而不是每 次在代碼塊開始執(zhí)行時(shí)創(chuàng)建,在代碼塊執(zhí)行完畢后銷毀。

5)、const關(guān)鍵字: 這是一個(gè)很有意思的關(guān)鍵字,他修飾的變量是只讀的,不能被修改;很多時(shí)候,編譯器會將其優(yōu)化成一個(gè)常量。const經(jīng)常被用來修飾函數(shù)的參數(shù),表示不希望這個(gè)參數(shù)值被函數(shù)體內(nèi)的代碼意外的改變。其實(shí),最有意思的是用const修飾一個(gè)指針,讓我們看下面這個(gè)例子:

        
  1. const int *p;   //p可變,p指向的對象不可變  
  2.  int const *p;   //同上  
  3.  int *const p;   //p不可變,p指向的對象可變  
  4.  const int *const p; //p和p指向的對象都不可變  

這些各表示什么呢?注釋里面給出了答案!是不是很不好記?我們只需要記得,const修飾的是*p的時(shí)候,p指向的內(nèi)容不可變;const修飾的是p的時(shí)候,p就不可變!

6). sizeof關(guān)鍵字:很多人也許會大吃一斤,我類個(gè)去,sizeof居然是關(guān)鍵字?(高手請無視這里,我當(dāng)初就是這種表現(xiàn))。不錯(cuò),sizeof確實(shí)是關(guān)鍵字,而不是庫函數(shù)!所以,如果編譯時(shí)得不到一個(gè)數(shù)組的大小,那么就不能使用sizeof關(guān)鍵字來獲取改數(shù)組的大小!

7). typedef關(guān)鍵字: typedef說白了就是給一個(gè)已知的類型起一個(gè)外號。

8). volatile關(guān)鍵字: 也許你見過這個(gè)關(guān)鍵字,但一般你都沒有用過。哈哈,我用過!這個(gè)關(guān)鍵字表示改變量的值可能在外部被改變,編譯器在用到這個(gè)變量時(shí)不能過度的優(yōu)化,必須每次都重新從內(nèi)存中讀取這個(gè)變量的值,而不是將其優(yōu)化在寄存器中。

【鏈接】:c定義了三類鏈接:外部鏈接,內(nèi)部鏈接和無鏈接。

通常,函數(shù)和全局變量具有外部鏈接,這意味著它們對構(gòu)成程序的所有文件是可用的。

聲明為static的文件域?qū)ο缶哂袃?nèi)部鏈接,僅在聲明它們的文件中是已知的。

局部變量沒有鏈接,因此僅在他們自己的塊中是已知的。

【對齊】:

使用偽指令#pragma pack (n),C編譯器將按照n個(gè)字節(jié)對齊。
 使用偽指令#pragma pack (),取消自定義字節(jié)對齊方式

 __attribute((aligned (n))),讓所作用的結(jié)構(gòu)成員對齊在n字節(jié)自然邊界上。如果結(jié)構(gòu)中有成員的長度大于n,則按照最大成員的長度來對齊。
 __attribute__ ((packed)),取消結(jié)構(gòu)在編譯過程中的優(yōu)化對齊,按照實(shí)際占用字節(jié)數(shù)進(jìn)行對齊。

【Standard linux memory layout

【變量的值】:就是分配給該變量的內(nèi)存位置所存儲的數(shù)值,即使指針也不例外。

inline函數(shù)和用macro定義的函數(shù)區(qū)別

Macro定義
只是很初級的一種代換,實(shí)現(xiàn)的功能很單一 而且安全性很差,比如類型錯(cuò)誤、括號漏寫
都會造成很大的錯(cuò)誤, 而且錯(cuò)誤不容易被發(fā)現(xiàn),隱患很大


inline函數(shù)

內(nèi)聯(lián)函數(shù)要比前者好很多 功能也要全面很多! 最主要的是內(nèi)聯(lián)函數(shù)能夠進(jìn)行安全檢查(比如參數(shù)類型等) 如果在能夠使用兩著的情況之下 推薦使用內(nèi)聯(lián)

不過有兩點(diǎn)要注意:
1     內(nèi)聯(lián)   是以代碼膨脹為代價(jià)的,
   
      不是所有的函數(shù)都適合用   內(nèi)聯(lián)   方式
 
      要考慮函數(shù)的實(shí)際情況
  
2     macro定義   也不是說一無是處了
 
      在合適的時(shí)候使用   也許會有意想不到的效果

【匯編基礎(chǔ)】

ebp和esp是32位的SP,BP 
esp是堆棧指針 bp是基址指針

EIP寄存器里存儲的是CPU下次要執(zhí)行的指令的地址。 
ESP與SP的關(guān)系就象AX與AL,AH的關(guān)系
.

32位CPU所含有的寄存器有:


4個(gè)數(shù)據(jù)寄存器(EAX、EBX、ECX和EDX)
2個(gè)變址和指針寄存器(ESI和EDI) 2個(gè)指針寄存器(ESP和
EBP)
6個(gè)段寄存器(ES、CS、SS、DS、FS和
GS)
1個(gè)指令指針寄存器(EIP) 1個(gè)標(biāo)志寄存器(EFlags)

附:Linux下使用objdump –S obj可以反匯編

【第二部分 Linux基礎(chǔ)】

【編譯過程】通常在使用gcc編譯程序時(shí),編譯過程通常會分為4個(gè)階段,即預(yù)處理編譯,匯編鏈接。

【僵尸進(jìn)程】如果某個(gè)進(jìn)程自身終止了,在調(diào)用exit清理完相關(guān)的內(nèi)容文件等資源后,它就會進(jìn)入ZOMBIE狀態(tài)

【孤兒進(jìn)程】當(dāng)某個(gè)進(jìn)程還在運(yùn)行時(shí),它的父進(jìn)程卻退出了,這個(gè)進(jìn)程卻沒有成為孤兒進(jìn)程

fork兩次產(chǎn)生守護(hù)進(jìn)程】父進(jìn)程先fork出一個(gè)兒子進(jìn)程,兒子進(jìn)程再fork出孫子進(jìn)程做為守護(hù)進(jìn)程,然后兒子進(jìn)程立刻退出,守護(hù)進(jìn)程被init進(jìn)程接管,這樣無論父進(jìn)程做什么事,無論怎么被阻塞,都與守護(hù)進(jìn)程無關(guān)了。所以,fork兩次的守護(hù)進(jìn)程很安全,避免了僵尸進(jìn)程出現(xiàn)的可能性。

【內(nèi)核:各種地址】CPU將一個(gè)虛擬內(nèi)存空間中的地址轉(zhuǎn)換為物理地址,需要進(jìn)行兩步:首先將給定一個(gè)邏輯地址(其實(shí)是段內(nèi)偏移量,這個(gè)一定要理解?。。。?,CPU要利用其段式內(nèi)存管理單元,先將為個(gè)邏輯地址轉(zhuǎn)換成一個(gè)線程地址,再利用其頁式內(nèi)存管理單元,轉(zhuǎn)換為最終物理地址

inline

通過在函數(shù)聲明的前面加上inline,即可告訴編譯程序優(yōu)化對函數(shù)的調(diào)用。從技術(shù)上講,這意味著函數(shù)的代碼將被有序擴(kuò)展而不是調(diào)用。當(dāng)然,inline只是對編譯程序的一個(gè)請求,可以忽略。注意,c++也支持inline說明符。

Linux內(nèi)存分配類型】

系統(tǒng)為進(jìn)程分配數(shù)據(jù)空間有三種形式。

靜態(tài)分配

整塊靜態(tài)分配空間,包括其中的所有數(shù)據(jù)實(shí)體,都是在進(jìn)程創(chuàng)建時(shí)由系統(tǒng)一次性分配的(同時(shí)為UNIX稱為Text的代碼分配空間)。這塊空間在進(jìn)程運(yùn)行期間保持不變。

初始化的和未初始化的實(shí)體分別放在初始化數(shù)據(jù)段和未初始化數(shù)據(jù)段(BSS)。后者和前者不同,在.o文件a.out文件里都不存在(只有構(gòu)架信息),在進(jìn)程的虛擬空間里才展開。

extern變量和static變量采用靜態(tài)分配。

在進(jìn)程創(chuàng)建時(shí)做靜態(tài)分配,分配正文(text)段、數(shù)據(jù)段和棧空間。

正文和初始化數(shù)據(jù)是按a.out照樣復(fù)制過來;未初始化數(shù)據(jù)按構(gòu)架信息展開,填以0或空;??臻g的大小由鏈接器開關(guān)(具體哪個(gè)開關(guān)忘了)決定。

棧分配

整個(gè)??臻g已在進(jìn)程創(chuàng)建時(shí)分配好。棧指針SP的初值的設(shè)定,確定了??臻g的大小。鏈接器的某個(gè)開關(guān)可以設(shè)定??臻g的大小。在進(jìn)程運(yùn)行期間,棧空間的大小不變。但是,在進(jìn)程剛啟動(dòng)時(shí),??臻g是空的,里面沒有實(shí)體。在進(jìn)程運(yùn)行期間,對具體實(shí)體的棧分配是進(jìn)程自行生成(壓棧)和釋放(彈出)實(shí)體,系統(tǒng)并不參與。

auto變量和函數(shù)參數(shù)采用棧分配。

只要壓入的實(shí)體的總長度不超過棧空間尺寸,棧分配就與系統(tǒng)無關(guān)。如果超過了,就會引發(fā)棧溢出錯(cuò)誤。

堆分配

當(dāng)進(jìn)程需要生成實(shí)體時(shí),向系統(tǒng)申請分配空間;不再需要該實(shí)體時(shí),可以向系統(tǒng)申請回收這塊空間。

堆分配使用特定的函數(shù)(如malloc()等)或操作符(new)。所生成的實(shí)體都是匿名的,只能通過指針去訪問。

對實(shí)體來說,棧分配和堆分配都是動(dòng)態(tài)分配:實(shí)體都是在進(jìn)程運(yùn)行中生成和消失。而靜態(tài)分配的所有實(shí)體都是在進(jìn)程創(chuàng)建時(shí)全部分配好的,在運(yùn)行中一直存在。

同為動(dòng)態(tài)分配,棧分配與堆分配是很不相同的。前者是在進(jìn)程創(chuàng)建時(shí)由系統(tǒng)分配整塊棧空 間,以后實(shí)體通過壓棧的方式產(chǎn)生,并通過彈出的方式取消。不管是否產(chǎn)生實(shí)體,產(chǎn)生多少實(shí)體,??臻g總是保持原來的大小。后者并沒有預(yù)設(shè)的空間,當(dāng)需要產(chǎn)生 實(shí)體時(shí),才向系統(tǒng)申請正好能容納這個(gè)實(shí)體的空間。當(dāng)不再需要該實(shí)體時(shí),可以向系統(tǒng)申請回收這塊空間。因此,堆分配是真正的動(dòng)態(tài)分配。

顯然,堆分配的空間利用率最高。

棧分配和靜態(tài)分配也有共性:整塊空間是在進(jìn)程創(chuàng)建時(shí)由系統(tǒng)分配的。但是,后者同時(shí)分配了所有實(shí)體的空間,而前者在進(jìn)程啟動(dòng)時(shí)是空的。另外,棧上的實(shí)體和數(shù)據(jù)段里的實(shí)體都是有名實(shí)體,可以通過標(biāo)識符來訪問。

 

靜態(tài)分配

棧分配

堆分配

整塊空間生成

進(jìn)程創(chuàng)建時(shí)

進(jìn)程創(chuàng)建時(shí)

用一點(diǎn)分配一點(diǎn)

實(shí)體生成時(shí)間

進(jìn)程創(chuàng)建時(shí)

進(jìn)程運(yùn)行時(shí)

進(jìn)程運(yùn)行時(shí)

實(shí)體生成者

操作系統(tǒng)

進(jìn)程

進(jìn)程申請/系統(tǒng)實(shí)施

生命期

永久

臨時(shí)

完全可控

有名/匿名

有名

有名

匿名

訪問方式

能以標(biāo)識訪問

能以標(biāo)識訪問

只能通過指針訪問

空間可否回收

不可

不可

可以

 

棧溢出的后果是比較嚴(yán)重的,或者出現(xiàn)Segmentation fault錯(cuò)誤,或者出現(xiàn)莫名其妙的錯(cuò)誤。

Linux大小端問題】

  所謂的大端模式,是指數(shù)據(jù)的低位(就是權(quán)值較小的后面那幾位)保存在內(nèi)存的高地址中,而數(shù)據(jù)的高位,保存在內(nèi)存的低地址中,這樣的存儲模式有點(diǎn)兒類似于把數(shù)據(jù)當(dāng)作字符串順序處理:地址由小向大增加,而數(shù)據(jù)從高位往低位放;

所謂的小端模式,是指數(shù)據(jù)的低位保存在內(nèi)存的低地址中,而數(shù)據(jù)的高位保存在內(nèi)存的高地址中,這種存儲模式將地址的高低和數(shù)據(jù)位權(quán)有效地結(jié)合起來,高地址部分權(quán)值高,低地址部分權(quán)值低,和我們的邏輯方法一致。

【自旋鎖、互斥量】

自旋鎖是一種非阻塞鎖,也就是說,如果某線程需要獲取自旋鎖,但該鎖已經(jīng)被其他線程占用時(shí),該線程不會被掛起,而是在不斷的消耗CPU的時(shí)間,不停的試圖獲取自旋鎖。

互斥量是阻塞鎖,當(dāng)某線程無法獲取互斥量時(shí),該線程會被直接掛起,該線程不再消耗CPU時(shí)間,當(dāng)其他線程釋放互斥量后,操作系統(tǒng)會激活那個(gè)被掛起的線程,讓其投入運(yùn)行。

uboot

Boot Loader stage1 通常包括以下步驟(以執(zhí)行的先后順序)

1.       硬件設(shè)備初始化(關(guān)看門狗,關(guān)中斷,設(shè)置cpu時(shí)鐘,初始化sdram,關(guān)閉 CPU 內(nèi)部指令/數(shù)據(jù) cache)。

2.       為加載 Boot Loader stage2 準(zhǔn)備 RAM 空間。

3.       拷貝 Boot Loader stage2 RAM 空間中。

4.       設(shè)置好堆棧。

5.       跳轉(zhuǎn)到 stage2 C 入口點(diǎn)。

Boot Loader stage2 通常包括以下步驟(以執(zhí)行的先后順序)

1.       初始化本階段要使用到的硬件設(shè)備。

2.       檢測系統(tǒng)內(nèi)存映射(memory map)。

3.       kernel 映像和根文件系統(tǒng)映像從 flash 上讀到 RAM 空間中。

4.       為內(nèi)核設(shè)置啟動(dòng)參數(shù)。

5.       調(diào)用內(nèi)核。

【第三部分 Linux內(nèi)核和驅(qū)動(dòng)】

sofitirq,taskletworkqueue

中斷處理分為兩個(gè)部分:上半部和下半部。中斷處理程序?qū)儆谏习氩?,這三者屬于下半部分,下半部的任務(wù)就是執(zhí)行與中斷處理程序密切相關(guān)但中斷處理程序本身不執(zhí)行,推后執(zhí)行的工作。

 

1軟中斷代碼最為簡潔,執(zhí)行效率也最快,但相同類型的軟中斷可以同時(shí)執(zhí)行(包擴(kuò)自己同時(shí)是指同一時(shí)刻在多個(gè)cpu上執(zhí)行)這樣一來它提供的執(zhí)行序列化保障也就最少,意味著在軟中斷中必須對同享數(shù)據(jù)進(jìn)行加鎖,而且軟中斷不同于進(jìn)程,不能重新調(diào)度,這樣任何潛在睡眠的代碼不能出現(xiàn)在軟中斷中。
2tasklet 在軟中斷的基礎(chǔ)上演化行成,對數(shù)據(jù)結(jié)構(gòu)加了一些限制所以執(zhí)行效率上較軟中斷慢些,但提供了執(zhí)行序列化的保障,相同類型的tasklet不能同時(shí)執(zhí)行,不同類型可以。但是若出現(xiàn)不同類型的tasklet同享數(shù)據(jù)仍需要相應(yīng)的同步處理。tasklet與軟中斷類似不能睡眠。
3工作隊(duì)列完全不同于軟中斷和tasklet,將下半部封裝為內(nèi)核線程的一個(gè)任務(wù),是唯一一種可以允許睡眠的方法,當(dāng)然性能開銷也是最大的。同步處理同線程。
總之,選擇時(shí)試問你有任何休眠的需要嗎?有,那工作隊(duì)列是你唯一的選擇。否則最好用tasklet,要是必須專注性能,那就考慮軟中斷吧。

likely and unlikely

定義形式:

  1. #define unlikely(x) __builtin_expect(!!(x), 0)  
  2.   
  3. #define unlikely(x) __builtin_expect(!!(x), 1)  

作用:

unlikely指示gcc編譯器這種情況很少發(fā)生,在編譯優(yōu)化的時(shí)候分支預(yù)測會跳到else情況中。

likely指示這種情況發(fā)生的多,分支預(yù)測按照下面的語句走。

事實(shí)上,根據(jù)這兩個(gè)函數(shù)可以得知條件語句中最可能發(fā)生的情形,從而告知編譯器以允許它正確地優(yōu)化條件分支。

 

skb_clone and skb_copy

skb_copy是對skb的數(shù)據(jù)完整復(fù)制,也可以說是深拷貝。

skb_clone并沒有真正申請數(shù)據(jù)段,只是申請了一個(gè)skb_struct,并讓其指針指向原skb的數(shù)據(jù)段。

對比skb_copyskb_clone,前者是一個(gè)深拷貝,而后者只是一個(gè)淺拷貝。

 

kmalloc and vmalloc,malloc

1.       kmallocvmalloc是分配的是內(nèi)核的內(nèi)存,malloc分配的是用戶的內(nèi)存

2.       kmallocmalloc 相似,該函數(shù)返回速度快快(除非它阻塞)并對其分配的內(nèi)存不進(jìn)行初始化(清零),分配的區(qū)仍然持有它原來的內(nèi)容,分配的區(qū)也是在物理內(nèi)存中連續(xù)。kmalloc 能夠分配的內(nèi)存塊的大小有一個(gè)上限。kmalloc分配內(nèi)存是基于slab,因此slab的一些特性包括著色,對齊等都具備,性能較好。物理地址和邏輯地址都是連續(xù)的。

原型:void *kmalloc(size_t size, int flags);

———————————————————————————————-
情形                                       相應(yīng)標(biāo)志
———————————————————————————————-
進(jìn)程上下文,可以睡眠                   GFP_KERNEL
進(jìn)程上下文,不可以睡眠                GFP_ATOMIC
中斷處理程序                             GFP_ATOMIC
軟中斷                                    GFP_ATOMIC
Tasklet                                    GFP_ATOMIC
用于DMA的內(nèi)存,可以睡眠            GFP_DMA | GFP_KERNEL
用于DMA的內(nèi)存,不可以睡眠         GFP_DMA | GFP_ATOMIC
———————————————————————————————-

3.       vmalloc分配內(nèi)存的時(shí)候邏輯地址是連續(xù)的,但物理地址一般是不連續(xù)的,適用于那種一下需要分配大量內(nèi)存的情況,如insert模塊的時(shí)候。這種分配方式性能不如kmalloc

4.       kmalloc能分配的大小有限,vmallocmalloc能分配的大小相對較大

5.       內(nèi)存只有在要被DMA訪問的時(shí)候才需要物理上連續(xù)

6.       vmallockmalloc要慢。最主要的區(qū)別是分配大小的問題。
比如你需要28個(gè)字節(jié),那一定用KMALLOC,如果用VMALLOC,分配不多次機(jī)器就罷工了。

OFFSET_OF(type,member)  SIZE_OF

  1. 1)、#define offsetof(TYPE, MEMBER) ((size_t) & ((TYPE *)0)->MEMBER )  
  2.   
  3. 1. ( (TYPE *)0 ) 將零轉(zhuǎn)型為TYPE類型指針;  
  4. 2. ((TYPE *)0)->MEMBER 訪問結(jié)構(gòu)中的數(shù)據(jù)成員;  
  5. 3. &( ( (TYPE *)0 )->MEMBER )取出數(shù)據(jù)成員的地址;  
  6. 4.(size_t)(&(((TYPE*)0)->MEMBER))結(jié)果轉(zhuǎn)換類型;  
  7. 巧妙之處在于將0轉(zhuǎn)換成(TYPE*),結(jié)構(gòu)以內(nèi)存空間首地址0作為起始地址,則成員地址自然為偏移地址。2) SIZE_OF宏  
  8.   
  9. 2). #define SIZEOF(s,m) sizeof(((s *)0)->m)(引申使用)  
  10.   
  11. 3). #define container_of(ptr, type, member) ({ \  
  12. const typeof( ((type *)0)->member ) *__mptr = (ptr); \  
  13. (type *)( (char *)__mptr - offsetof(type,member) );})  


 

宏功能:從結(jié)構(gòu)體(type)某成員變量(member)指針(ptr)來求出該結(jié)構(gòu)體(type)的首指針。

1、typeof( ( (type *)0)->member )為取出member成員的變量類型。

2、定義__mptr指針ptr為指向該成員變量的指針

3、mptr為member數(shù)據(jù)類型的常量指針,其指向ptr所指向的變量處

4、.(char *)__mptr轉(zhuǎn)換為字節(jié)型指針。(char *)__mptr - offsetof(type,member))用來求出結(jié)構(gòu)體起始地址(為char *型指針),然后(type *)( (char *)__mptr -offsetof(type,member) )在(type *)作用下進(jìn)行將字節(jié)型的結(jié)構(gòu)體起始指針轉(zhuǎn)換為type *型的結(jié)構(gòu)體起始指針。
5、.({ })這個(gè)擴(kuò)展返回程序塊中最后一個(gè)表達(dá)式的值。

【自旋鎖如何保護(hù)critical region

自旋鎖可以確保在同時(shí)只有一個(gè)線程進(jìn)入臨界區(qū)。其他想進(jìn)入臨界區(qū)的線程必須不停地原地打轉(zhuǎn),直到第1個(gè)線程釋放自旋鎖。注意:這里所說的線程不是內(nèi)核線程,而是執(zhí)行的線程。

【互斥體】

與自旋鎖不同的是,互斥體在進(jìn)入一個(gè)被占用的臨界區(qū)之前不會原地打轉(zhuǎn),而是使當(dāng)前線程進(jìn)入睡眠狀態(tài)。如果要等待的時(shí)間較長,互斥體比自旋鎖更合適,因?yàn)樽孕i會消耗CPU資源。在使用互斥體的場合,多于2次進(jìn)程切換時(shí)間都可被認(rèn)為是長時(shí)間,因此一個(gè)互斥體會引起本線程睡眠,而當(dāng)其被喚醒時(shí),它需要被切換回來。

因此,在很多情況下,決定使用自旋鎖還是互斥體相對來說很容易:

(1) 如果臨界區(qū)需要睡眠,只能使用互斥體,因?yàn)樵讷@得自旋鎖后進(jìn)行調(diào)度、搶占以及在等待隊(duì)列上睡眠都是非法的;

(2) 由于互斥體會在面臨競爭的情況下將當(dāng)前線程置于睡眠狀態(tài),因此,在中斷處理函數(shù)中,只能使用自旋鎖。

[ISR即中斷服務(wù)程序中可以調(diào)用printk函數(shù)嗎?]

盡量不要使用,printk可能會引起睡眠。而中斷處理程序是不允許睡眠的!

【下面的代碼就使用了__interrupt關(guān)鍵字去定義了一個(gè)中斷服務(wù)子程序(ISR),請?jiān)u論一下這段代碼的。】

  1. __interrupt double compute_area (double radius)   
  2. {  
  3.     double area = PI * radius * radius;  
  4.     printf("\nArea = %f", area);  
  5.     return area;  
  6. }  


 

這個(gè)函數(shù)有太多的錯(cuò)誤了,以至讓人不知從何說起了:
1)ISR 不能返回一個(gè)值。如果你不懂這個(gè),那么你不會被雇用的。
2) ISR 不能傳遞參數(shù)。如果你沒有看到這一點(diǎn),你被雇用的機(jī)會等同第一項(xiàng)。
3) 在許多的處理器/編譯器中,浮點(diǎn)一般都是不可重入的。有些處理器/編譯器需要讓額處的寄存器入棧,有些處理器/編譯器就是不允許在ISR中做浮點(diǎn)運(yùn)算。此外,ISR應(yīng)該是短而有效率的,在ISR中做浮點(diǎn)運(yùn)算是不明智的。
4) 與第三點(diǎn)一脈相承,printf()經(jīng)常有重入和性能上的問題。如果你丟掉了第三和第四點(diǎn),我不會太為難你的。不用說,如果你能得到后兩點(diǎn),那么你的被雇用前景越來越光明了。

 

【第四部分編程】

【指針】

  1. int *p;   
  2.   
  3. p++; (p=p+sizeof(int))  
  4.   
  5. p=p+12;p指向當(dāng)前位置后的第12個(gè)元素。  


 

【變元函數(shù)】func/*illegal*/

cc++函數(shù)原型聲明】

cc++處理函數(shù)原型的方式上,存在著細(xì)小但重要的差別,C++的函數(shù)原型沒有參數(shù)。在C++中,原型中不帶任何參數(shù)表示空參數(shù)表。

C中,函數(shù)沒有參數(shù)時(shí),其原型使用參數(shù)表中的void。如,float f(void) ; 它告訴編譯程序該函數(shù)沒有參數(shù),且對沒有變元的函數(shù)的任何調(diào)用都將出錯(cuò)。

【向函數(shù)傳遞全結(jié)構(gòu)】

結(jié)構(gòu)用做函數(shù)的變元時(shí),用標(biāo)準(zhǔn)值調(diào)用規(guī)則把全結(jié)構(gòu)傳給函數(shù)。因此,函數(shù)對結(jié)構(gòu)內(nèi)容的修改不影響原結(jié)構(gòu),即退出函數(shù)后,原結(jié)構(gòu)內(nèi)容不變。同時(shí),當(dāng)執(zhí)行函數(shù)調(diào)用時(shí),向棧中壓入數(shù)據(jù)需要花費(fèi)開銷。

向函數(shù)傳結(jié)構(gòu)指針時(shí),壓棧的只是指針本身,使函數(shù)調(diào)用特別快;在有些情況下,傳遞指針的第二個(gè)優(yōu)點(diǎn)是,在傳遞指針時(shí),該函數(shù)可以修改作為變元的結(jié)構(gòu)的內(nèi)容。

cc++結(jié)構(gòu)聲明方法不同】

C++中,一旦結(jié)構(gòu)已經(jīng)聲明,僅使用此結(jié)構(gòu)的標(biāo)記即可聲明此類變量,而不必在其前面加上關(guān)鍵字struct。造成這種差別的原因是,在C中,結(jié)構(gòu)名并未定義完整的類型名。這就是C語言將這個(gè)名字稱為標(biāo)記的原因。但在c++中,結(jié)構(gòu)名是完整的類型名,可以被自身用以定義變量。但請記住,在C++程序中使用c風(fēng)格的聲明始終是完全合法的。上述討論可以推   廣到聯(lián)合和枚舉。

【位域】C語言具有訪問字節(jié)中位的內(nèi)設(shè)機(jī)制。type namelength

  1. struct bs  
  2. {  
  3.  int a:8;  
  4.  int b:2;  
  5.  int c:6;  
  6. }data;  


 

說明databs變量,共占兩個(gè)字節(jié)。其中位域a8位,位域b2位,位域c6位。對于位域的定義尚有以下幾點(diǎn)說明:

1.       一個(gè)位域必須存儲在同一個(gè)字節(jié)中,不能跨兩個(gè)字節(jié)。如一個(gè)字節(jié)所??臻g不夠存放另一位域時(shí),應(yīng)從下一單元起存放該位域。也可以有意使某位域從下一單元開始。

2. 由于位域不允許跨兩個(gè)字節(jié),因此位域的長度不能大于一個(gè)字節(jié)的長度,也就是說不能超過8位二進(jìn)位。

3. 位域可以無位域名,這時(shí)它只用來作填充或調(diào)整位置。無名的位域是不能使用的。

 

sizeof聯(lián)合的尺寸總是等于聯(lián)合中最大的成員的尺寸。

charunsigned char的區(qū)別】

char的值在-128127之間,

unsigned char的值在0255之間

for example:

  1. char c = 0x8f;  
  2.   
  3. printf("c is %d. \n",c);//113,取反加一  


 

【右移一位等于除以2右移一位就等于除以2,但是這里需要加一個(gè)條件,這里指的是正數(shù)。而對于有符號整數(shù),且其值為負(fù)數(shù)時(shí),在C99標(biāo)準(zhǔn)中對于其右移操作的結(jié)果的規(guī)定是implementation-defined.

【大小端程序?qū)崿F(xiàn)】

  1. unsigned char chk_cpu(){  
  2.   
  3. int data=0x0011;  
  4.   
  5. char temp=*(char *)&data;  
  6.   
  7. if(temp==0) return 0; //大端返回0;  
  8.   
  9. else return 1;   //小段返回1;  
  10.   
  11. }  


 

【一個(gè)二進(jìn)制數(shù)中含有的“1”的個(gè)數(shù)】

  1. int count(int val)  
  2.   
  3. {  
  4.   
  5.          int num = 0;  
  6.   
  7.          while(val)  
  8.   
  9.          {          
  10.   
  11.                    num += val & 0x01;  
  12.   
  13.                    val>>=1;  
  14.   
  15.          }  
  16.   
  17.          return num;  
  18.   
  19. }  


 

【實(shí)現(xiàn)memmove

  1. void memmove(void* pDest, const void* pSrc, unsigned int count)  
  2. {  
  3. char* dest ;  
  4.   
  5. const char *src;  
  6. int i;  
  7. dest = pDest;  
  8. src = pSrc;  
  9. if((pDest < pSrc)|| (pSrc + count > pDest))  
  10. {   
  11. for(i = 0; i < count; i++)  
  12.    *dest++ = *src++;  
  13. }  
  14. else  
  15. //有重疊的情形  
  16.    dest += count - 1;  
  17.    src += count - 1;  
  18.    for(i = 0; i < count; i++)  
  19.       *dest-- = *src--;  
  20. }  
  21. }  

find the nth to the last of single linked node 查找鏈表中倒數(shù)第k個(gè)結(jié)點(diǎn)】

方法一:求出正數(shù)第n-k+1個(gè)結(jié)點(diǎn)即可

代碼如下:

  1. ListNode* FindKthToTail_Solution1(ListNode* pListHead, unsigned int k)  
  2. {  
  3.          if(pListHead == NULL)  
  4.                    return NULL;  
  5.   
  6.          // count the nodes number in the list  
  7.          ListNode *pCur = pListHead;  
  8.          unsigned int nNum = 0;  
  9.          while(pCur->m_pNext != NULL)  
  10.          {  
  11.                    pCur = pCur->m_pNext;  
  12.                    nNum ++;  
  13.          }  
  14.   
  15.          // if the number of nodes in the list is less than k  
  16.          // do nothing  
  17.          if(nNum < k)  
  18.                    return NULL;  
  19.   
  20.          // the kth node from the tail of a list   
  21.          // is the (n - k)th node from the head  
  22.          pCur = pListHead;  
  23.          for(unsigned int i = 0; i < nNum - k; ++ i)  
  24.                    pCur = pCur->m_pNext;  
  25.   
  26.          return pCur;  
  27. }  


 

方法二:如果我們在遍歷時(shí)維持兩個(gè)指針,第一個(gè)指針從鏈表的頭指針開始遍歷,在第k-1步之前,第二個(gè)指針保持不動(dòng);在第k-1步開始,第二個(gè)指針也開始從鏈表的頭指針開始遍歷。由于兩個(gè)指針的距離保持在k-1,當(dāng)?shù)谝粋€(gè)(走在前面的)指針到達(dá)鏈表的尾結(jié)點(diǎn)時(shí),第二個(gè)指針(走在后面的)指針正好是倒數(shù)第k個(gè)結(jié)點(diǎn)。

這種思路只需要遍歷鏈表一次。對于很長的鏈表,只需要把每個(gè)結(jié)點(diǎn)從硬盤導(dǎo)入到內(nèi)存一次。因此這一方法的時(shí)間效率前面的方法要高。

代碼如下:

  1. ListNode* FindKthToTail_Solution2(ListNode* pListHead, unsigned int k)  
  2. {  
  3.          if(pListHead == NULL)  
  4.                    return NULL;  
  5.   
  6.          ListNode *pAhead = pListHead;  
  7.          ListNode *pBehind = NULL;  
  8. //先讓頭指針走k-1步  
  9.          for(unsigned int i = 0; i < k; ++ i)  
  10.          {  
  11.                    if(pAhead->m_pNext != NULL)  
  12.                             pAhead = pAhead->m_pNext;  
  13.                    else  
  14.                    {  
  15.                             // if the number of nodes in the list is less than k,   
  16.                             // do nothing  
  17.                             return NULL;  
  18.                    }  
  19.          }  
  20.   
  21. //然后兩個(gè)指針同時(shí)行動(dòng)  
  22.          pBehind = pListHead;  
  23.          // the distance between pAhead and pBehind is k  
  24.          // when pAhead arrives at the tail, p  
  25.          // Behind is at the kth node from the tail  
  26.          while(pAhead->m_pNext != NULL)  
  27.          {  
  28.                    pAhead = pAhead->m_pNext;  
  29.                    pBehind = pBehind->m_pNext;  
  30.          }  
  31.   
  32.          return pBehind;  
  33. }  


 

【單鏈表全】

  1. typedef struct node  
  2.   
  3. {  
  4.   
  5.     int nDate;  
  6.   
  7.     struct node *pstnext;  
  8.   
  9. }Node;  
  10.   
  11. //鏈表輸出  
  12.   
  13. void output(Node *head)  
  14.   
  15. {  
  16.   
  17.     Node *p = head->pstnext;  
  18.   
  19.     while(NULL != p)  
  20.   
  21.     {  
  22.   
  23.           printf("%d  ", p->nDate);  
  24.   
  25.           p = p->pstnext;  
  26.   
  27.     }  
  28.   
  29.     printf("\r\n");  
  30.   
  31. }  
  32.   
  33. //鏈表建立  
  34.   
  35. Node* creat()  
  36.   
  37. {  
  38.   
  39.     Node *head = NULL, *p = NULL, *s = NULL;  
  40.   
  41.     int Date = 0, cycle = 1;  
  42.   
  43.     head = (Node*)malloc(sizeof(Node));  
  44.   
  45.     if(NULL == head)  
  46.   
  47.     {  
  48.   
  49.           printf("分配內(nèi)存失敗\r\n");  
  50.   
  51.           return NULL;  
  52.   
  53.     }  
  54.   
  55.     head->pstnext = NULL;  
  56.   
  57.    
  58.   
  59.     p = head;  
  60.   
  61.     while(cycle)  
  62.   
  63.     {  
  64.   
  65.         printf("請輸入數(shù)據(jù)且當(dāng)輸入數(shù)據(jù)為0時(shí)結(jié)束輸入\r\n");  
  66.   
  67.         scanf("%d", &Date);  
  68.   
  69.         if(0 != Date)  
  70.   
  71.         {  
  72.   
  73.            s = (Node*)malloc(sizeof(Node));  
  74.   
  75.            if(NULL == s)  
  76.   
  77.            {  
  78.   
  79.                     printf("分配內(nèi)存失敗\r\n");  
  80.   
  81.                     return NULL;  
  82.   
  83.            }  
  84.   
  85.            s->nDate = Date;  
  86.   
  87.            p->pstnext = s;  
  88.   
  89.            p = s;  
  90.   
  91.       }  
  92.   
  93.       else  
  94.   
  95.       {  
  96.   
  97.             cycle = 0;  
  98.   
  99.       }  
  100.   
  101.     }  
  102.   
  103.     p->pstnext = NULL;  
  104.   
  105.     return(head);  
  106.   
  107. }  
  108.   
  109. //單鏈表測長  
  110.   
  111. void length(Node *head)  
  112.   
  113. {  
  114.   
  115.     Node *p = head->pstnext;  
  116.   
  117.     int j=0;  
  118.   
  119.     while(NULL != p)  
  120.   
  121.     {  
  122.   
  123.           p = p->pstnext;  
  124.   
  125.           j++;  
  126.   
  127.     }  
  128.   
  129.     printf("%d\r\n", j);  
  130.   
  131. }  
  132.   
  133. //鏈表按值查找  
  134.   
  135. void research_Date(Node *head, int date)  
  136.   
  137. {  
  138.   
  139.     Node *p;  
  140.   
  141.     int n=1;  
  142.   
  143.     p = head->pstnext;  
  144.   
  145.     while(NULL != p && date != p->nDate)  
  146.   
  147.     {  
  148.   
  149.           p = p->pstnext;  
  150.   
  151.           ++n;  
  152.   
  153.     }  
  154.   
  155.     if(NULL == p)  
  156.   
  157.     {  
  158.   
  159.         printf("鏈表中沒有找到該值");  
  160.   
  161.     }  
  162.   
  163.     else if(date == p->nDate)  
  164.   
  165.     {  
  166.   
  167.         printf("要查找的值%d在鏈表中第%d個(gè)位置\r\n", date, n);  
  168.   
  169.     }  
  170.   
  171.     return;  
  172.   
  173. }  
  174.   
  175. //按序號查找  
  176.   
  177. void research_Number(Node *head, int Num)  
  178.   
  179. {  
  180.   
  181.     Node *p=head;  
  182.   
  183.     int i=0;  
  184.   
  185.     while(p!=NULL )  
  186.   
  187.     {  
  188.   
  189.         p=p->pstnext;  
  190.   
  191.         i++;  
  192.   
  193.         if( i == Num)  
  194.   
  195.             break;  
  196.   
  197.     }  
  198.   
  199.     if(p == NULL)  
  200.   
  201.     {  
  202.   
  203.         printf("Not found\n");  
  204.   
  205.     }  
  206.   
  207.      else if(i == 0)  
  208.   
  209.     {  
  210.   
  211.         printf("查找位置為頭結(jié)點(diǎn)\r\n");  
  212.   
  213.     }  
  214.   
  215.     else  
  216.   
  217.     {  
  218.   
  219.         printf("the %d date is %d.\n",Num,p->nDate);  
  220.   
  221.     }  
  222.   
  223. }  
  224.   
  225.    
  226.   
  227. //在指定元素之前插入新結(jié)點(diǎn)  
  228.   
  229. void insert_1(Node *head, int i, int Newdate)  
  230.   
  231. {  
  232.   
  233.     Node *pre = head, *New = NULL;  
  234.   
  235.     int j = 0;  
  236.   
  237.     while(NULL != pre && j < i-1)  
  238.   
  239.     {  
  240.   
  241.           pre = pre->pstnext;  
  242.   
  243.           j++;  
  244.   
  245.     }  
  246.   
  247.     if(NULL == pre || j > i-1)  
  248.   
  249.     {  
  250.   
  251.         printf("插入位置不存在\r\n");  
  252.   
  253.     }  
  254.   
  255.     else  
  256.   
  257.     {  
  258.   
  259.         New = (Node*)malloc(sizeof(Node));  
  260.   
  261.         if(NULL == New)  
  262.   
  263.         {  
  264.   
  265.             printf("分配內(nèi)存失敗\r\n");  
  266.   
  267.             return;  
  268.   
  269.         }  
  270.   
  271.         New->nDate = Newdate;  
  272.   
  273.         New->pstnext = pre->pstnext;  
  274.   
  275.         pre->pstnext = New;  
  276.   
  277.     }  
  278.   
  279.    
  280.   
  281. }  
  282.   
  283. //在指定元素之后插入新結(jié)點(diǎn)  
  284.   
  285. void insert_2(Node *head, int i, int Newdate)  
  286.   
  287. {  
  288.   
  289.     Node *pre = head, *New = NULL;  
  290.   
  291.     int j = 0;  
  292.   
  293.     while(NULL != pre->pstnext && j < i)  
  294.   
  295.     {  
  296.   
  297.         pre = pre->pstnext;  
  298.   
  299.         j++;  
  300.   
  301.     }  
  302.   
  303.     if(j == i)  
  304.   
  305.     {  
  306.   
  307.         New = (Node*)malloc(sizeof(Node));  
  308.   
  309.         if(NULL == New)  
  310.   
  311.         {  
  312.   
  313.             printf("分配內(nèi)存失敗\r\n");  
  314.   
  315.             return;  
  316.   
  317.         }  
  318.   
  319.         New->nDate = Newdate;  
  320.   
  321.         New->pstnext = pre->pstnext;  
  322.   
  323.         pre->pstnext = New;  
  324.   
  325.     }  
  326.   
  327.     else  
  328.   
  329.     {  
  330.   
  331.         printf("插入位置不存在\r\n");  
  332.   
  333.     }  
  334.   
  335. }  
  336.   
  337. //刪除指定結(jié)點(diǎn)  
  338.   
  339. void Delete_1(Node *head, int i3)  
  340.   
  341. {  
  342.   
  343.     Node *p = head, *pre = NULL;  
  344.   
  345.     int j = 0;  
  346.   
  347.     while(NULL != p && j < i3)  
  348.   
  349.     {  
  350.   
  351.           pre = p;  
  352.   
  353.           p = p->pstnext;  
  354.   
  355.           j++;  
  356.   
  357.     }  
  358.   
  359.     if(NULL == p)  
  360.   
  361.     {  
  362.   
  363.         printf("刪除位置不存在\r\n");  
  364.   
  365.     }  
  366.   
  367.     else  
  368.   
  369.     {  
  370.   
  371.         pre->pstnext = p->pstnext;  
  372.   
  373.         free(p);  
  374.   
  375.     }  
  376.   
  377. }  
  378.   
  379. //指定刪除單鏈表中某個(gè)數(shù)據(jù),并統(tǒng)計(jì)刪除此數(shù)據(jù)的個(gè)數(shù)  
  380.   
  381. int Delete_2(Node *head, int Delete_date)  
  382.   
  383. {  
  384.   
  385.     Node *pTmp = NULL, *nextNode=NULL;  
  386.   
  387.     pTmp = head;  
  388.   
  389.     int count = 0;  
  390.   
  391.     while(pTmp->pstnext != NULL)  
  392.   
  393.     {  
  394.   
  395.         nextNode = pTmp->pstnext;  
  396.   
  397.         if( Delete_date == nextNode->nDate )  
  398.   
  399.         {  
  400.   
  401.             count++;  
  402.   
  403.             pTmp->pstnext = nextNode->pstnext;  
  404.   
  405.             free(nextNode);  
  406.   
  407.         }  
  408.   
  409.          else  
  410.   
  411.          {  
  412.   
  413.              pTmp = nextNode;  
  414.   
  415.          }  
  416.   
  417.     }  
  418.   
  419.     return count;  
  420.   
  421. }  
  422.   
  423.    
  424.   
  425. //鏈表逆置  
  426.   
  427. void Reverse_list(Node *head)  
  428.   
  429. {  
  430.   
  431.     Node *q, *s;  
  432.   
  433.     if(NULL == head->pstnext || NULL == head->pstnext->pstnext)  
  434.   
  435.     {  
  436.   
  437.         return;  
  438.   
  439.     }  
  440.   
  441.     q = head->pstnext->pstnext;  
  442.   
  443.     head->pstnext->pstnext = NULL;  
  444.   
  445.     while(NULL != q)  
  446.   
  447.     {  
  448.   
  449.           s = q->pstnext;  
  450.   
  451.           q->pstnext = head->pstnext;  
  452.   
  453.           head->pstnext = q;  
  454.   
  455.           q = s;  
  456.   
  457.     }  
  458.   
  459. }  
  460.   
  461. //單鏈表的連接  
  462.   
  463. void connect_list(Node *head, Node *head_New)  
  464.   
  465. {  
  466.   
  467. Node *p = head;  
  468.   
  469. while(NULL != p->pstnext)  
  470.   
  471. {  
  472.   
  473.   p = p->pstnext;  
  474.   
  475. }  
  476.   
  477. p->pstnext = head_New->pstnext;  
  478.   
  479. }  
  480.   
  481. //單鏈表銷毀  
  482.   
  483. void destroy_list(Node* head)  
  484.   
  485. {  
  486.   
  487.     while (NULL != head)  
  488.   
  489.     {  
  490.   
  491.         Node* temp = head;  
  492.   
  493.         head = head->pstnext;  
  494.   
  495.         free(temp);  
  496.   
  497.     }  
  498.   
  499. }  
  500.   
  501. 【查找一個(gè)字符串中最后出現(xiàn)子串的位置】  
  502.   
  503. char *my_strrstr(char const *src,char const *substr)  
  504.   
  505. {  
  506.   
  507.     register char * last;  
  508.   
  509.     register char * current;  
  510.   
  511.    
  512.   
  513.     last = NULL;  
  514.   
  515.    
  516.   
  517.     if(*substr != '\0')  
  518.   
  519.     {  
  520.   
  521.         current = strstr(src,substr);  
  522.   
  523.         while(current!=NULL)  
  524.   
  525.         {  
  526.   
  527.             last = current;  
  528.   
  529.             current=strstr(last+1,substr);  
  530.   
  531.         }  
  532.   
  533.     }  
  534.   
  535.     return last;  
  536.   
  537. }  


 

給定一個(gè)整型變量a,寫兩段代碼,第一個(gè)設(shè)置abit 3,第二個(gè)清除a bit 3。在以上兩個(gè)操作中,要保持其它位不變。

【林銳getMemory

  1. #define BIT3 (0x1 << 3)//8  
  2.   
  3. static int a=0x07;  
  4.   
  5. void set_bit3(void)  
  6.   
  7. {  
  8.   
  9.     a |= BIT3;  
  10.   
  11.     printf("after set bit : %d.\n",a);//15  
  12.   
  13. }  
  14.   
  15. void clear_bit3(void)  
  16.   
  17. {  
  18.   
  19.     a &= ~BIT3;//~BIT3 = 7  
  20.   
  21.     printf("after clear bit : %d.\n",a);//7  
  22.   
  23. }  


 

【訪問固定的內(nèi)存位置】

在某工程中,要求設(shè)置一絕對地址為0x67a9的整型變量的值為0xaa66。編譯器是一個(gè)純粹的ANSI編譯器。寫代碼去完成這一任務(wù)。

  1. int *ptr;  
  2.     ptr = (int *)0x67a9;  
  3.     *ptr = 0xaa66;  
  4.   
  5. 或者晦澀一點(diǎn)的方法:*(int * const)(0x67a9) = 0xaa55;  


 

【林銳高質(zhì)量c/c++編程習(xí)題(選)】

  1. void GetMemory(char *p)   
  2.   
  3. {   
  4.   
  5.   p = (char *)malloc(100);   
  6.   
  7. }     
  8.   
  9. void Test(void){   
  10.   
  11.   char *str = NULL;   
  12.   
  13.   GetMemory(str);   
  14.   
  15.   strcpy(str, "hello world");   
  16.   
  17.   printf(str);   
  18.   
  19. }   


 

請問運(yùn)行Test函數(shù)會有什么樣的結(jié)果?

答:程序崩潰。因?yàn)?span style="font-family:Calibri">GetMemory并不能傳遞動(dòng)態(tài)內(nèi)存, Test函數(shù)中的 str一直都是 NULL。strcpy(str, "hello world");將使程序崩潰。

  1. char *GetMemory(void)   
  2.   
  3.   {   
  4.   
  5.   char p[] = "hello world";   
  6.   
  7.   return p;   
  8.   
  9.  }   
  10.   
  11. void Test(void)   
  12.   
  13.   {   
  14.   
  15.   char *str = NULL;   
  16.   
  17.   str = GetMemory();   
  18.   
  19.   printf(str);   
  20.   
  21.  }   


 

請問運(yùn)行Test函數(shù)會有什么樣的結(jié)果?

答:可能是亂碼。 因?yàn)镚etMemory返回的是指向“棧內(nèi)存”的指針,該指針的地址不是 NULL,但其原現(xiàn)的內(nèi)容已經(jīng)被清除,新內(nèi)容不可知。

  1. void GetMemory2(char **p, int num)   
  2.   
  3. {   
  4.   
  5.   *p = (char *)malloc(num);   
  6.   
  7. }   
  8.   
  9. void Test(void)   
  10.   
  11. {   
  12.   
  13.   char *str = NULL;   
  14.   
  15.   GetMemory(&str, 100);   
  16.   
  17.   strcpy(str, "hello");   
  18.   
  19.   printf(str);   
  20.   
  21. }   


 

請問運(yùn)行Test函數(shù)會有什么樣的結(jié)果?

答: (1)能夠輸出hello (2)內(nèi)存泄漏

  1. void Test(void)   
  2.   
  3. {   
  4.   
  5.    char *str = (char *) malloc(100);   
  6.   
  7.    strcpy(str, “hello”);    
  8.   
  9.     free(str);   
  10.   
  11.   if(str != NULL)   
  12.   
  13.   {   
  14.   
  15.     strcpy(str, “world”);   
  16.   
  17.     printf(str);   
  18.   
  19.  }   
  20.   
  21. }   


 

請問運(yùn)行Test函數(shù)會有什么樣的結(jié)果?

答:篡改動(dòng)態(tài)內(nèi)存區(qū)的內(nèi)容,后果難以預(yù)料,非常危險(xiǎn)。 因?yàn)閒ree(str);之后str成為野指針, if(str != NULL)語句不起作用。

 

 

【排序算法(合集)】

插入排序

算法概要:插入排序依據(jù)遍歷到第N個(gè)元素的時(shí)候前面的N-1個(gè)元素已經(jīng)是排序好的,那么就查找前面的N-1個(gè)元素把這第N個(gè)元素放在合適的位置,如此下去直到遍歷完序列的元素為止。

  1. void shellSort(int array[], int length)    
  2.   
  3. {    
  4.   
  5.     int key;    
  6.   
  7.     int increment;    
  8.   
  9.     for(increment = length/2; increment>0; increment /= 2)    
  10.   
  11.     {    
  12.   
  13.         for(int i=increment; i<length; i++)    
  14.   
  15.         {    
  16.   
  17.             key = array[i];    
  18.   
  19.             for(int j = i-increment; j>=0 && array[j] > key; j -= increment)    
  20.   
  21.             {    
  22.   
  23.                 array[j+increment] = array[j];    
  24.   
  25.             }    
  26.   
  27.             array[j+increment]=key;    
  28.   
  29.         }    
  30.   
  31.     }    
  32.   
  33. }  


 

希爾排序 

算法概要:shell排序是對插入排序的一個(gè)改裝,它每次排序排序根據(jù)一個(gè)增量獲取一個(gè)序列,對這這個(gè)子序列進(jìn)行插入排序,然后不斷的縮小增量擴(kuò)大子序列的元素?cái)?shù)量,直到增量為1的時(shí)候子序列就和原先的待排列序列一樣了,此時(shí)只需要做少量的比較和移動(dòng)就可以完成對序列的排序了。

  1. void shellSort(int array[], int length)    
  2.   
  3. {    
  4.   
  5.         int key;    
  6.   
  7.         int increment;    
  8.   
  9.         for(increment = length/2; increment>0; increment /= 2)    
  10.   
  11.         {    
  12.   
  13.             for(int i=increment; i<length; i++)    
  14.   
  15.             {    
  16.   
  17.                 key = array[i];    
  18.   
  19.                 for(int j = i-increment; j>=0 && array[j] > key; j -= increment)    
  20.   
  21.                 {    
  22.   
  23.                     array[j+increment] = array[j];    
  24.   
  25.                 }    
  26.   
  27.                 array[j+increment]=key;    
  28.   
  29.             }    
  30.   
  31.         }        
  32.   
  33. }  


 

冒泡排序

算法概要:冒泡排序是經(jīng)過n-1趟子排序完成的,第i趟子排序從第1個(gè)數(shù)至第n-i個(gè)數(shù),若第i個(gè)數(shù)比后一個(gè)數(shù)大(則升序,小則降序)則交換兩數(shù)。

  1. void bubbleSort(int  array[], int length)    
  2.   
  3. {    
  4.   
  5.     int flag = 0;    
  6.   
  7.     for(int i=0; i<length-1; i++)    
  8.   
  9.     {    
  10.   
  11.         for(int j=0; j<length-1-i; j++)    
  12.   
  13.         {    
  14.   
  15.             if(array[j]>array[j+1])    
  16.   
  17.             {    
  18.   
  19.                 flag = 1;    
  20.   
  21.                 array[j] = array[j] + array[j+1];    
  22.   
  23.                 array[j+1] = array[j] - array[j+1];    
  24.   
  25.                 array[j] = array[j] - array[j+1];    
  26.   
  27.             }    
  28.   
  29.         }    
  30.   
  31.         if(flag == 0) break;    
  32.   
  33.     }    
  34.   
  35. }  


 

快速排序

快速排序(Quicksort)是對冒泡排序的一種改進(jìn)。通過一趟排序?qū)⒁判虻臄?shù)據(jù)分割成獨(dú)立的兩部分,其中一部分的所有數(shù)據(jù)都比另外一部分的所有數(shù)據(jù)都要小,然后再按此方法對這兩部分?jǐn)?shù)據(jù)分別進(jìn)行快速排序,整個(gè)排序過程可以遞歸進(jìn)行,以此達(dá)到整個(gè)數(shù)據(jù)變成有序序列。

  1. int Sort(int array[], int first, int last)    
  2.   
  3.  {    
  4.   
  5.         int pivot = array[first];    
  6.   
  7.         int temp;    
  8.   
  9.         if(last-first <=0) return -1;    
  10.   
  11.         
  12.   
  13.         while(first != last)    
  14.   
  15.         {    
  16.   
  17.             while(array[last] >= pivot && last != first) last--;    
  18.   
  19.             temp = array[first];    
  20.   
  21.             array[first] = array[last];    
  22.   
  23.             array[last] = temp;    
  24.   
  25.             while(array[first] <= pivot && last != first) first++;    
  26.   
  27.             temp = array[first];    
  28.   
  29.             array[first] = array[last];    
  30.   
  31.             array[last] = temp;    
  32.   
  33.         }    
  34.   
  35.         return last;    
  36.   
  37.  }    
  38.   
  39.         
  40.   
  41.     void quickSort(int array[], int length)    
  42.   
  43.     {    
  44.   
  45.         int temp = Sort(array, 0, length-1);    
  46.   
  47.         if(temp == -1 ) return;    
  48.   
  49.         quickSort(array,temp+1);    
  50.   
  51.         quickSort(&array[temp+1],length-temp-1);    
  52.   
  53.     }  


 

歸并排序

算法概要:歸并排序法是將兩個(gè)(或兩個(gè)以上)有序表合并成一個(gè)新的有序表,即把待排序序列分為若干個(gè)子序列,每個(gè)子序列是有序的。然后再把有序子序列合并為整體有序序列。

  1. void Merge(int A[],int low,int mid,int high)        
  2.   
  3.  {        
  4.   
  5.      int i,j,k;      
  6.   
  7.      int *P = new int[mid-low+1],*Q = new int[high-mid];        
  8.   
  9.        
  10.   
  11.      for (i =0;i  < mid - low +1;++i)   P[i] = A[i+low];  
  12.   
  13.             
  14.   
  15.      for (i = 0; i  < high - mid;++i)   Q[i] = A[mid+1+i];        
  16.   
  17.         
  18.   
  19.      i = j = 0,k = low;        
  20.   
  21.      while ((i  <= mid-low) && (j  <= high-mid-1))        
  22.   
  23.      {        
  24.   
  25.         
  26.   
  27.          if (P[i]  <= Q[j])   A[k++] = P[i++];        
  28.   
  29.          else  A[k++]= Q[j++];        
  30.   
  31.      }        
  32.   
  33.         
  34.   
  35.      if (i > mid - low)   { for (;j  <= high-mid-1;++j)   A[k++] = Q[j]; }        
  36.   
  37.      else       
  38.   
  39.      {   for (; i <= mid - low;++i)   A[k++] = P[i];  }        
  40.   
  41.         
  42.   
  43.      delete [] P;        
  44.   
  45.      delete [] Q;        
  46.   
  47.  }        
  48.   
  49.         
  50.   
  51.  void mergeSort(int A[],int left,int right)        
  52.   
  53.  {        
  54.   
  55.      if (left  < right)        
  56.   
  57.      {        
  58.   
  59.          int i = (left + right) / 2;        
  60.   
  61.          MergeSort(A,left,i);        
  62.   
  63.          MergeSort(A,i+1,right);        
  64.   
  65.          Merge(A,left,i,right);        
  66.   
  67.      }        
  68.   
  69.  }  


 

【第五部分 WiFi

WiFi.

12. ToDS From Ds

 

13. RTS CTS

 

    本站是提供個(gè)人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多