久久99精品久久久久久琪琪,久久人人爽人人爽人人片亞洲,熟妇人妻无码中文字幕,亚洲精品无码久久久久久久

編程知識總結(jié)C難點總結(jié)

時間:2019-05-12 00:04:03下載本文作者:會員上傳
簡介:寫寫幫文庫小編為你整理了多篇相關的《編程知識總結(jié)C難點總結(jié)》,但愿對你工作學習有幫助,當然你在寫寫幫文庫還可以找到更多《編程知識總結(jié)C難點總結(jié)》。

第一篇:編程知識總結(jié)C難點總結(jié)

《編程知識總結(jié)》—C#難點總結(jié)

1.編寫菜單中對應子菜單項的消息響應函數(shù),考慮的方面很多,例 如,當前打開一個文件,此文件已經(jīng)被修改,如果用戶需要新建 或者打開另外一個文件,程序要詢問用戶是否保存當前的文件。

問題的解決:先用紙張寫一份詳細的業(yè)務流程圖,在之后的編寫 過程中,按照業(yè)務流程的規(guī)定進行功能的逐步實現(xiàn)。

2.編寫判斷是否要提醒用戶保存的程序,判斷是否提醒保存的根據(jù) 是什么。我的判斷根據(jù)驗證文件內(nèi)容是否被修改,如果被修改,提醒用戶保存。如果被保存的文件沒有文件路徑,調(diào)用另存為的 響應函數(shù)進行執(zhí)行。

3.無法關閉窗體,當我響應文件菜單下退出菜單項的時候,如果使 用 Close()函數(shù)的話,可以就直接退出,但是當我需要以同樣的 方式響應右上角關閉按鈕的話,事件信息就會進入死循環(huán),而且 永遠不會結(jié)束。所以,在關閉窗體的時候,目前我用的就是 Application.exit();這個函數(shù),強制性終結(jié)這個窗口

4.保存文件,我們打開文件和直接輸入信息的文件的情況,對于保 存來說是不一樣的,因為打開的時候,應該是直接保存到指定的 文件中,而直接輸入的應該提示保存,通過另存為的方式保存。5.文件的刪除。C#的 textBox 沒有直接為我們提供文本刪除的函數(shù),我弄了許久,后來,無意間突然發(fā)現(xiàn)通過已經(jīng)選擇的字符串的下 標來操作,就可以刪除文件了,具體操作就是,先獲取被選擇文 件的前半部分,在獲取后半部分,而略過中間被選中的部分即可。6.開始的時候沒有使用 Using System.IO,系統(tǒng)報錯;

7.由于在 MessageBox.Show 后沒有寫 return 導致錯誤;

8.初步創(chuàng)建簡易記事本后點擊打開文件選項并不顯示文件的內(nèi)容,且編譯器報錯了,是由于沒有判斷文件名為空,且文件的完整路 徑名獲取的不準確,將文件名為空的情況及其文件的完整路徑名 獲取之后,讀取文件正確;

9.首先,我在添加單擊“新建”響應的代碼時,單擊進去,結(jié)果代 碼全部不能運行,必須是雙擊。

10.獲取文件名時,文件名不需要特別處理,程序內(nèi)部直接處理好的,我在這里畫蛇添足了。

11.操作對象必須指代明確,不如要出錯。

12.在用 WinForm 做文本文檔的時候,在“新建文本文檔”項的代碼 中,需要在項目中新建一個 new 窗體作為“新建文本文檔”,在 form1 的代碼中寫如下代碼: Form frm = new NewForm();frm.Show();this.Hide();然后在 Form2 中下如同樣的代碼即可。

13.還發(fā)現(xiàn)“保存”與“另存為”項的代碼編寫沒有任何差異,考慮 并修改程序無果。代碼如下: private void 保存SToolStripMenuItem_Click(object sender, EventArgs e)// { DialogResult save = saveFileD

ialog1.ShowDialog();

if

(save

== DialogResult.OK){ string filepath = saveFileDialog1.FileName;//FileInfo fi = new FileInfo(filepath);string content = richTextBox1.Text;File.WriteAllText(filepath, content, Encoding.Default);} } 14.在保存的時候必須輸入保存路徑才能正常執(zhí)行,沒有實現(xiàn)文本文 檔的基本保存功能。并且在打開一個原本有內(nèi)容的文本文檔之后,加以修改再保存就不能保存新的內(nèi)容。

15.獲取 TextBox 中獲取一行的值,使用 TextBox 的 Lines 屬性來獲 取

16.做刪除操作時,由 MessageBoxv.show 來彈出一個確認窗體,把返 回的值賦給一個 DialogResult 類型的變量

17.在復制文件時,CopyTo 函數(shù)將第二個參數(shù)置為 TRUE 則在復制時,如果文件存在會被覆蓋掉。

18.在向文件寫入數(shù)據(jù)時,使用 Encoding 的 Default 的屬性可避免輸 入中文亂碼問題。19.關于上午的文件編程,剛開始對程序要執(zhí)行的具體操作不夠明確,在編程時,功能實現(xiàn)的矛盾性問題很多,其問題在于編程前分析 不夠到位;

20.文件編程中起先將打開文件定位為文件夾,導致彈出對話框顯示 “不存在此文件”; 21.File.WriteAllLines(),無法將臨時修改后的文件信息進行正 確保存,文本沒有實現(xiàn)正確的回車換行;File.WriteAllText()能實現(xiàn);

22.this.Close()無法實現(xiàn)程序的結(jié)束 Close()方法是關閉 Form 23.當創(chuàng)建了控件的響應事件后,修改控件的 name 屬性,響應事件 名為改變,但并不影響進程的進行,要修改方法名應刪除該響應 方法,雙擊控件,重新獲得響應事件。

24.代碼經(jīng)常會出現(xiàn)命名不規(guī)范的情況,同時沒有寫注釋的習慣

25.經(jīng)常會丟掉方法后的括號,如 savedialog1.showdialog()26.一 些 方 法 中 的 細 微 差 別 如 File.Write.AllLine 27.對于異常的處理經(jīng)常忽略,導致程序的容錯處理比較差。

28.當編輯框改變時有兩種情況,一種是打開,一種是編輯,當打開 時不應該記錄改變標志。解決的辦法:做一個打開標志,當打開操作時,賦值為 true,在 文本框的改變函數(shù)中判斷,如果是打開操作,將打開操作之位 false,同時文本改變標志值為 false

29.此處如何把長整形變量賦值給一個 string 類型? 使用 ToString()30.如何在文本框上添加滾動條。設置 TextBox.ScrollBars

31.textbox 控件大小跟窗體一起變化:剛開始想通過相應窗體大小 改變事件的辦法解決,后來通過其他同學得知可以通過更改 textbox 的 Dock 屬性(改為 Fill)來解決。

32.當 textbox 空間的文本改變時,進行新建、打開、退出等操作時 應提示用戶是否保存更改,通過設置文件更改標志(flag)并相 File.WriteAllText 和 響 textbox 的 textchanged 事件來解決。

33.由于需要提示用戶保存更改,剛

開始時代碼控制結(jié)構(gòu)寫的很復雜,連自己也有點難理解。后來用調(diào)用“保存”事件的響應函數(shù)(剛 開始一位不能這樣)后才解決這一問題。

34.由于剛開始做,命名方面不是很規(guī)范,導致了變量、方法和類的 名字不易區(qū)分,不能一目了然,間接增加了編寫程序所需的時間。解決方法是在編寫程序中時刻遵守命名規(guī)范。

35.在對文件操作時,文件的路徑經(jīng)常出錯,原因是少了轉(zhuǎn)換為絕對 路徑的@。

36.對文件注釋太少,經(jīng)常導致前面編過的代碼過一段時間就完全看 不懂了,所以,在編程中也要養(yǎng)成隨時加注釋的習慣,其實這樣 做表面上是增加了時間,統(tǒng)籌來看,這樣做既方便了自己日后修 改程序,又為閱讀我們程序的人提供了便利。

37.文件操作經(jīng)常出錯,程序無法運行,要因此要異常處理,比如說 用 try,catch 對異常處理。

38.在昨天上午的文件操作中,第一個問題就是路徑的合法性檢測。由于不知道 c#是否存在像 c++ access()一樣的方法。最后使用 OpenFileDialog 來限定用戶輸入。可以使用正則表達式驗證 39.再有就是文件操作異常處理。C#的異常處理還是很方便的,昨天 的經(jīng)驗是只要使用 try catch 語句,在 catch 中不作任何處理程 序也不會當?shù)簟?/p>

40.MessageBox.Show(“你還沒有保存上次的修改,是否保存修改?”, “保存”, MessageBoxButtons.YesNoCancel);選項的判斷 問 同 學 得 : 返 回 值 為 DialogResult.No DialogResult.Yes DialogResult.Cancel 41.2 . private

void

Form1_FormClosing(object

sender, FormClosingEventArgs e)中如何取消關閉 問同學得:e.Cancel = true;42.如何實現(xiàn)修改后 “新建”“打開”等提示保存 點打開保存后記 錄存儲路徑

43.新建 2 個成員記錄:private string Pathname;private bool savetxt=true;

44.如何在兩個窗體間傳遞值。通過向同學和老師詢問,知道傳值有 很多種方法,我用的就是調(diào)用構(gòu)造函數(shù),可是老師也說了如果值 比較多的時候,這種方法還是有缺陷的。使用構(gòu)造函數(shù)可以,只是也用對象會好一些。

45.當漢字改成英文時,函數(shù)中仍然是漢字,如果直接在 program.cs 中改容易出問題,怎么改? 利用 VS 的代碼重構(gòu)

46.如何改窗體的默認主題樣式 可以使用第三方控件完成

47.TextBox 中文本值改變時的響應事件(可以添加事件響應函數(shù))。

48.文 件 打 開 中 將 文 件 寫 到 textbox 中 : 定 義 string[] 調(diào) 用 File.ReadAllLines 函數(shù)將文件內(nèi)容逐行讀入到字符串數(shù)組中,然后利用 for 循環(huán)將文件內(nèi)容逐行寫出到控件中。

49.填寫郵箱和文件路徑的時候需要有正確的規(guī)格,因為之前接觸過 c#不知道該如何完成,后來在經(jīng)過詢問他人和網(wǎng)上資料查詢得知 用正則表達式來規(guī)范正確

的格式,便避免了這方面因格式而導致 的問題。

50.當輸入多行字符串,保存過后進行查看時,當有換行就會出現(xiàn)黑 乎乎的正方形。換行:rn

51.假設某個事件已經(jīng)做好,可以作為一個模塊使用,那么如何在其 他代碼中調(diào)用此模塊

52.記事本的查找、替換模塊不知道如何實現(xiàn) 需要使用字符串函數(shù)

53.打開 txt 文件的時候中文沒有亂碼但是保持了之后在打開就有了 亂碼 實現(xiàn):原先是因為保持的時候沒有使用 encoding.default 54.保存時文件不能自動保存文件 ?自動保存文件

55.在做簡易記事本的途中,創(chuàng)建快捷鍵的時候遇到一點問題,當在 雜項的 Shortcutkeys 中選擇 shift 的時候,它會報屬性值無效的 錯誤,后來無奈之下選擇了 Ctrl,居然就正確了

56.還有在 TextBox 的屬性 Dock 選擇了 fill 的時候,依然沒有變成 全部填充,真是百思不得其解!需要將 TextBox 設置為多行文本

57.發(fā)現(xiàn)了一種比較簡便的方法: 已經(jīng)做完的某個控件出發(fā)的事件,可以作為一個法方來使用。例: private void SaveMenu_Click(object sender, EventArgs e){ //判斷文件是否已有路徑,若無,選定路徑,若有,保存在最新操作的文件路徑下 if(path == string.Empty){ if(SolgSave.ShowDialog()== DialogResult.OK){ path = SolgSave.FileName;File.AppendAllText(path, txtEdit.Text, Encoding.Default);} } else { File.AppendAllText(path, txtEdit.Text, Encoding.Default);} } private void ExitMenu_Click(object sender, EventArgs e){ if(txtEdit.Text!= string.Empty){ DialogResult dr = MessageBox.Show(“是否保存當前文本至

”+path,“

”, MessageBoxButtons.OKCancel,MessageBoxIcon.Warning);if(dr == DialogResult.OK){ //使用保存文本方法 this.SaveMenu_Click(sender,e);//SaveMenu 的單擊事件 } } //退出程序 Application.Exit();}

58.RichTextBox 與 TextBox 的換行區(qū)別問題。在 TextBox 中是以rn 為換行標志,而在 RichTextBox 中是以n 為換行標志。我在 C#程序中使用 RichTextBox 將其文本內(nèi)容保存為文本文件,即(*.txt)類型的文件時,文本不能正常的換行,且出現(xiàn)一些亂碼。這是由于文本文件是以rn 作為換行符,當我改用 TextBox 時,就 沒有這樣的問題了。

59.文本的選中問題。SelectedText 這一屬性可以指向當前控件中選中的文本,60.通常我們會發(fā)現(xiàn),保存操作在文本沒有保存的情況下時才會出現(xiàn) 保存文件對話框,當文本已經(jīng)保存過時,新增的文本會保存到原 文件中,這兩個操作需要對文本是否保存過進行判斷。我采用了 判斷路徑的方法對我文本是否保存過進行了判斷,當文本路徑存 在時,表明文本已經(jīng)保存過,反之,則沒有保存過。

第二篇:《C專家編程》總結(jié)

《C專家編程》總結(jié)

開始讀《C專家編程》之前,有一個很擔心的問題:94年出的講語言的書,在現(xiàn)在(2012)還有多少是適用的。因此,一邊讀,一邊用VS2010做實驗。最后發(fā)現(xiàn)大部分內(nèi)容都還在用。讀完后,覺得最精彩的部分有二:一是講解如何理解聲明,二是深入地講解數(shù)組名與指針。下文是將看書過程中所做的筆記進行的整理。

p.s: 以下代碼均在VS2010測試過

1.使用無符號數(shù)時要特別注意(不推薦使用無符號數(shù))當無符號數(shù)與有符號數(shù)在同一條表達式中出現(xiàn)時,有符號數(shù)會被轉(zhuǎn)換為無符號數(shù)。e.g:

int feng =-1;unsigned int yan = 5;bool result =(feng < yan)? true : false;//最后的結(jié)果會是false 原因是C語言在計算含有不同類型的表達式時,會將類型向上提升。在本例中,int被提升了unsigned int,從而使-1的補碼被解析為很大的整數(shù) 2.相鄰的字符串常量會被自動合并成一個字符串 e.g:

char *str[] = {“feng” “yan”, “zero”};//“feng”和“yan”被合并成一個了:“fengyan” 3.易出錯的優(yōu)先級

.高于* e.g: *p.f 正確的理解:*(p.f)

[]高于* e.g: int *ap[] 正確的理解:int *(ap[])ap是個數(shù)組,其元素為int* 函數(shù)()高于* e.g: int *fp()正確的理解:int* fp()fp是返回int*的函數(shù)

==和!=高于位操作符 e.g: val & mask!= 0 正確的理解:val &(mask!= 0)==和!=高于賦值符 e.g: c = getchar()!= EOF 正確的理解:c =(getchar()!= EOF)

算術運算高于移位運算 e.g: msb<<4 + lsb 正確的理解:msb <<(4 + lsb)逗號運算符優(yōu)先級最低 e.g: i = 1, 2 正確的理解:(i = 1), 2 4.理解聲明,定義,typedef語句的步驟

a.找標識符

b.找被括號括起來的部分

c.找后綴操作符,如果是(),則表示是函數(shù);如果是[],則表示是數(shù)組 d.找前綴操作符,如果是*,則表示“指向XX的指針”

e.找const和volatile,如果const,volatile后面緊跟類型(如int,long),那么它作用于類型,其它情況下,作用于它左邊緊鄰的項 e.g:

int const * zero;//zero是一個指針,指向一個常量整形

char(*zero)[20];//zero是一個指針,指向一個有20個char元素的數(shù)組

typedef void(*ptr_to_func)(int);//ptr_to_func是新類型名,這種類型是一個函數(shù)指針,指向接收一個int參數(shù),并返回void的函數(shù)

char* const *(*zero)();//zero是一個函數(shù)指針,該函數(shù)無參數(shù),并返回一個指針,返回的指針指向一個常量指針

char*(*zero[10])(int **p);//zero是一個數(shù)組,元素是函數(shù)指針,其指向的函數(shù)授受一個二維指針,并返回一個指向char的指針 void(*signal(int sig, void(*func)(int)))(int);//signal是一個函數(shù),該函數(shù)接收一個int,一個函數(shù)指針,并返回一個函數(shù)指針

5.左值與右值

左值通常表示存儲結(jié)果的地方(地址),其值在編譯時可知 右值通常表示地址的內(nèi)容,其值通常要到運行時才知道

6.指針與數(shù)組名不等同的情況(定義為數(shù)組,卻聲明為指針,或者反過來)前提知識(假設有定義:int array[10], *ptr;):

a.使用數(shù)組名下標訪問(如:array[1]),會直接將數(shù)組名的地址加上偏移值作為變量的地址(即array[1]的地址)

b.使用指針下標訪問(如:ptr[1]),會先取指針指向的內(nèi)容,然后將這個內(nèi)容加上偏移值作為變量的地址(即ptr[1]的地址)

不等同的原因:

當定義為數(shù)組,卻聲明為指針時,相當于用指針下標訪問的方法來解析一個數(shù)組名下標,即先取數(shù)組第0號元素的內(nèi)容,然后將這個內(nèi)容加上偏移值作為變量的地址,從而訪問了不該訪問的東西。反之亦然。7.指針與數(shù)組等同的情況

a.編譯器將表達式中的數(shù)組名當作指向該數(shù)組第0號元素的指針,下標當作指針的偏移量,即array[i]會被當作*(array + i)

b.編譯器將函數(shù)參數(shù)聲明中的數(shù)組名當作指向該數(shù)組第0號元素的指針,即在函數(shù)內(nèi)部得到的是指針,而不是數(shù)組名

基于a情況,可知這條謠言是假的(至少在一維數(shù)組中一定不成立): 用指針迭代數(shù)組比用下標迭代數(shù)組更快

基于b情況,可解釋為什么在傳遞數(shù)組后,不能用以下方法計算數(shù)組長度

int ArrayLength(int arr[]){ return sizeof(arr)/ sizeof(arr[0]);//返回值必定是1,因為此時的arr是一個指針,而不是數(shù)組名 } 注意b情況的將數(shù)組改寫為指針并不是遞歸定義的,e.g:

實參 char zero[10][10] 被改寫為 char(*zero)[10],這里將數(shù)組的數(shù)組改寫為數(shù)組的指針

實參 char *zero[10] 被改寫為 char **zero,這里將指針數(shù)組改寫為指針的指針

實參 cahr(*zero)[10] 不改變,因為此時的zero是指針,而不是數(shù)組 8.interposition interposition指用戶定義的函數(shù)取代庫中聲明完全相同的函數(shù),注意這不是指重載,而是指下面這種:

void zero();//user defined function void zero();//library function 出現(xiàn)interposition時,要特別注意以下情況:

void zero();//user defined function int main(){ zero();//調(diào)用用戶定義的函數(shù)zero,而不是庫函數(shù)zero

FengYan();//假設這是另一個庫函數(shù),并且函數(shù)內(nèi)調(diào)用庫函數(shù)zero,此時由于interposition,變成調(diào)用用戶定義的zero return 0;} 備注:

出現(xiàn)interposition時,在VS2010會出現(xiàn)warning: inconsistent dll linkage 9.堆棧段作用

a.存儲局部變量

b.函數(shù)調(diào)用時,存儲有關的維護信息

c.用作暫時存儲區(qū)。e.g: 計算一個很長的表達式時,會把部分結(jié)果先壓到堆棧中

第三篇:C語言編程自我總結(jié)

1.編譯器選擇8級優(yōu)化時,可能會出現(xiàn)錯誤。剛寫好的程序,建議先用0級優(yōu)化看能否正常運行,再用更高的優(yōu)化等級進行優(yōu)化。

2.a、寫中斷程序一定要用using語句指定寄存器組。第1、2、3組都可以,不能是0,否則可能會main()函數(shù)沖突。從一個中斷程序中調(diào)用函數(shù)必須和中斷使用相同的寄存器組(摘自《Keil Cx51 編譯器用戶手冊中文版》P129)。建議把原本中斷函數(shù)需要調(diào)用的函數(shù)直接寫在中斷函數(shù)里,無須調(diào)用。

b、51單片機的中斷有兩個優(yōu)先級。一個中斷不會打斷另一個相同優(yōu)先級的中斷。這樣相同級別中斷可以使用同一個組。比如:低優(yōu)先級的中斷函數(shù)都用 using 1,高優(yōu)先級的中斷都用 using 2。這樣不會沖突。

3.C語言無符號數(shù)容易犯的錯誤。若定義成有符號數(shù)char,則不會陷入死循環(huán)。

main(){ unsigned char i;for(i = 2;i>=0;i--){ printf(“%d”,i);} }

4.C51忌諱使用絕對定位_at_,因為只要定義變量和變量的作用域,編譯器就會把一個固定地址給這個變量,無須人工將其絕對定位,這樣可能引發(fā)其他問題。

5.bit與sbit的區(qū)別:bit定義的位標量的地址是隨機的,而sbit定義的位標量的地址是確定的。bit只能訪問芯片內(nèi)部RAM中的可尋址位20H-2FH,而sbit可以訪問芯片內(nèi)部RAM中的可尋址位和特殊功能寄存器中的可尋址位。注意不能直接在程序里用P1^0等位變量,需要經(jīng)過sbit定義才可以使用。例如:

bit

tem;sbit led=P1^0;tem的地址是隨機分配的,而led的地址則固定為0x90.0。sbit變量后面需要跟等號=。6.為了避免由于使用參數(shù)宏而帶來意外的錯誤,需要注意以下幾點:

6.1 宏的參數(shù)必須帶括號,例如 #define CIRCLE_SQUARE(R)3.141*(R)*(R)6.2 對所使用的參數(shù)宏進行簡單地展開檢查;

6.3 使用簡單表達式、對參數(shù)加括號、避免節(jié)外生枝的使用方式(例如“++”、“--”一類都屬于不必要的附件運算);

6.4 在參數(shù)宏定義時,對于運算順序通過括號進行明確的限定,只要遵循以上幾點,就可以避免大多數(shù)應用場合的意外錯誤。

手把手教你寫程序

內(nèi)容:從最簡單的程序入手,手把手教你寫程序,讓同學們拿到一個復雜的程序或者任務,能快速找到切入點,寫出程序,再在此基礎上優(yōu)化程序。當拿到一個單片機任務時,不要急于動手寫程序,先仔細分析它的以下幾個點:

1、它要單片機整體實現(xiàn)什么功能

2、功能細分(模塊化),先干什么,再干什么,最后干什么

3、畫初步流程圖,(把幾個模塊畫出即可)

4、模塊之間的分析:一個模塊到另一個模塊之間,怎么變換,怎么連接(優(yōu)化流程圖)

5、單個模塊分析:每個模塊要做什么(流程圖細化)

6、所有模塊結(jié)合連接,細化所有流程圖

7、分析單個模塊每步要用到的方法或者指令

8、總流程圖定型

9、紙上寫程序,對照流程圖分析其可行性,若不可行則返回

10、上機調(diào)試,加注釋

11、從小到大,一個功能一個功能地調(diào)試;

以上十一步,缺一不可(小程序例外)切記:流程圖的確定很重要,需反復修改

大忌:拿到任務,不仔細分析就寫程序。即使是小程序,我們也要養(yǎng)成良好的編程習慣,不要一味的追求結(jié)果。寫小程序可能比別人快,若是大程序,一旦出現(xiàn)思維混亂,或者出現(xiàn)程序調(diào)試不出結(jié)果,那么你花在調(diào)試上的時間,要比別人的多。!!!磨刀不誤砍柴工!!!程序的優(yōu)化:屬于后期工作,只有調(diào)試出來后,才去優(yōu)化,如果一開始優(yōu)化和寫程序同時進行,一是加重你的思考量,二是出現(xiàn)問題無從下手。無疑增加了寫程序的難度。對于一個初學者,寫一個程序,本身頭腦就處于緊張的狀態(tài),思考的問題就很多,如果此時把優(yōu)化程序也考慮進去,你腦袋的負荷無疑加重,若你頭腦精明,你可以把優(yōu)化的地方,先在紙上記下來,等到調(diào)試結(jié)果正常,再把你想到的,優(yōu)化的地方加進去。

7、如果在中斷程序中改變了多字節(jié)類型的變量,那么中斷程序以外的程序中(主程序,子函數(shù))要使用該多字節(jié)類型變量的話,讀寫前要關中斷,讀寫后再開中斷。否則會導致偶爾讀寫錯誤。(實質(zhì)為資源沖突)舉一反三:

其他的數(shù)據(jù)類型也可能有這種影響。例如:長整型、浮點型。例如:

unsigned int ms_counter;void T0(){ //定時器程序每100毫秒中斷一次,程序略 if(ms_counter<1000)ms_counter++;} void main(void){ //初始化定時器程序每100毫秒中斷一次,程序略 unsigned char tt;ms_counter=0;tt=0;//用tt控制只響一次 while(1){ if(ms_counter<400){ if(tt==0){ tt=1;Sound_on();

} } else { Sound_off();} //其他程序 } }

8、sbit變量不能使用extern關鍵字,使其在不同的文件中被使用,如要在led.c和main.c文件中使用同一個變量led0,有以下下兩種辦法:

1.在各種文件中重復定義變量,如在led.c中定義sbit led0=P1^0;同樣在main.c中定義sbit led0=P1^0;這樣,led0就變成了全局變量,可以在兩個文件中使用。

2.將sbit led0=P1^0定義到led.h頭文件中,均在led.c和main.c中包含led.h這個頭文件。

9、在多文件的程序中聲明外部變量(extern和)

如果一個程序包含兩個文件,在兩個文件中都要用到同一個外部變量Num,不能分別在兩個文件中各自定義一個外部變量Num,否則在進行程序的連接時會出現(xiàn)“重復定義”的 錯誤。正確的做法是:在任一個文件中定義外部變量Num,而在另一個文件中用extern對Num作“外部變量聲明”。即extern Num;注意若Num為uchar類型,應當寫為“extern uchar Num”,否則會當為int,而導致出錯。

當使用static聲明變量和函數(shù)時,需要在定義變量和函數(shù)的基礎上加上此關鍵字,而不能單獨使用。例如:

static int a;//定義性聲明,需要時,直接使用變量a即可 a = 0x01;

static int funA(int a, int b);//聲明,且static不起作用 int funA(int a ,int b)//定義,即使funA有static關鍵字修飾,但由于static不能單獨使用,//故funA仍為外部函數(shù)。

{ …… } extern對變量進行聲明時,如沒有初始化,則為引用性聲明,不含定義,如需使用此變量,需要進行定義。例如:

extern int a;//引用性聲明,不含定義

extern int a = 0x01;//定義性聲明,需要時,直接使用變量a即可 int a;//定義

extern對函數(shù)進行聲明時,如沒有函數(shù)體,則為引用性聲明,不含定義。

extern int funB(int a ,int b);//引用性聲明,不含定義,且extern聲明可以省略

extern int funC(int a, int b)//定義性聲明 { …… }

10、一般的,要盡量減少中斷服務程序的內(nèi)容和長度。因為在主程序中可以還需要隨時響應其他的中斷或事件。如果一個中斷服務程序過程,很可能會影響到主程序?qū)ν獠啃盘柕臋z測和響應。通常,在中斷程序中只是改變一些變量或標志位,在主程序中再根據(jù)變量或標志位的值進行判斷,處理相應的事件。

11、在A/D和D/A轉(zhuǎn)換電路中,電源電壓和基準電壓的穩(wěn)定性,對轉(zhuǎn)換的精度影響很大。另外,A/D和D/A轉(zhuǎn)換電路中要特別注意地線的正確連接,否則轉(zhuǎn)換結(jié)果將是不正確的,干擾影響將很嚴重。

12、根據(jù)C語言標準,左移“<<”和右移“>>”運算要求操作數(shù)至少是int,如果不滿int,自動轉(zhuǎn)換成int(C語言整型提升)。因此 uchar a=0x01;a<<8;實際運算,并不是8位數(shù)左移8位,而是int型左移8位。

13、在中斷里調(diào)用其他函數(shù),且要進行參數(shù)傳遞時,必須保證被調(diào)用函數(shù)所使用的寄存器組與中斷函數(shù)一樣,否則會產(chǎn)生不正確的結(jié)果。為了保證被調(diào)用的函數(shù)與中斷函數(shù)使用的寄存器一致,可對被調(diào)用函數(shù)使用using,不過此函數(shù)只能被中斷函數(shù)調(diào)用。

14、函數(shù)不使用using 時,所使用寄存器組保持與此函數(shù)被調(diào)用前相同,不對RS0和RS1的值進行修改;當使用了using 關鍵字后,此函數(shù)所使用的寄存器組與using所定義的一樣。

15、當指定中斷程序的工作寄存器組時,保護工作寄存器的工作就可以被省略。使用關鍵 字using 后跟一個0 到3 的數(shù)對應著4 組工作寄存器當指定工作寄存器組的時候默 認的工作寄存器組就不會被推入堆棧這將節(jié)省32 個處理周期,因為入棧和出棧都需要2 個處理周期。為中斷程序指定工作寄存器組的缺點是所有被中斷調(diào)用的過程都必須使用 同一個寄存器組否則參數(shù)傳遞會發(fā)生錯誤。

16、如何使用pdata 類型的變量?當要使用到pdata 類型的變量,如下: void main(void){ uchar pdata a;a=0x01;}

則需要進行如下設置,否則pdata 的變量a則會無效。

a、修改STARTUP.A51的內(nèi)容。默認時,PPAGEENALBE為0,表示不允許pdata類型的變量,須將其值改為1;PPAGE表示pdata類型的變量存儲在哪一頁,01H表示存放在外部存儲器的第1頁,地址范圍100H至1FFH,此時P2經(jīng)STARTUP.A51處理后的值為0x01;此項設置需和BL51連接器的設置一致。

b、修改BL51連接器。根據(jù)STARTUP.A51中PPAGE所設置的值來填寫Pdata的值,如下圖。圖中Pdata的值可以填寫100H至1FFH中任意一個,表示pdata類型的變量從所填

寫的值開始存儲。例如,當Pdata填寫的值為108H時,表示pdata類型的變量從108H開始存儲,因此,存儲范圍變?yōu)榱?08H至1FFH。

另外,存儲模式Compact的作用是將沒有指定存儲類型的變量定義為pdata類型,對uchar pdata a;變量的定義沒有影響,但對uchar a;則有影響。

17、XBYTE的用法。XBYTE存在于#include 頭文件中。

XBYTE[0x000F]=data; // 此語句表示將data寫到外部RAM中的0x000F data=XBYTE[0x000F] // 此語句表示讀取外部RAM中0x000F的數(shù)據(jù) 以下語句與上面的語句等效:

#define EX_RAM XBYTE[0x000F] //將EX_RAM定義為外部RAM的地址0x000F EX_RAM=data;// 此語句表示將data寫到外部RAM中的0x000F data=EX_RAM // 此語句表示讀取外部RAM中0x000F的數(shù)據(jù)

18、如何在keil中用匯編實現(xiàn)51中沒有的指令

部分MCU與8051兼容,但會增加8051中沒有的指令,如華邦的W77E58和N79E352等芯片,具有8051中沒有的指令DEC DPTR。如何才Keil中實現(xiàn)此指令呢? 方法1:

在需要執(zhí)行該指令的地方放置相應的機器碼 MAIN:

MOV DPTR,#02H DB 0A5H;由于從數(shù)據(jù)手冊上得知,DEC DPTR的機器碼為0A5H,故此處相當于執(zhí)行了DEC DPTR指令。

AJMP $ END

方法2:

使用宏定義的方法

/*宏定義,表示用DEC_DPTR代替MACRO與ENDM之間的內(nèi)容*/ DEC_DPTR MACRO

DB 0A5H;此處不能與MACRO同一行 ENDM

MAIN: MOV DPTR,#02H DEC_DPTR;放置機器碼0A5H,相當于執(zhí)行DEC DPTR AJMP $ END

通過將以上兩種方法生成的hex文件調(diào)入到編程器中,發(fā)現(xiàn)代碼一樣。經(jīng)測試,同樣可以用以上兩種方法代替8051中已有的指令。

例如,從數(shù)據(jù)手冊可知,MOV A,#0FH的長度為2字節(jié),機器碼的值為74H,0FH。因此,經(jīng)驗證,以下三個程序等效,產(chǎn)生的HEX文件一樣 MAIN: MOV A,#55H DB 74H DB 0FH MOV P1,A AJMP $ END

MAIN: MOV A,#55H MOV A,#0FH MOV P1,A AJMP $ END

TEST MACRO DB 74H DB 0FH ENDM MAIN: MOV A,#55H TEST MOV P1,A AJMP $ END

18、匯編中包含頭步驟:

例如,T2CON為定時器2的特殊功能寄存器,地址為0C8H,要對此寄存器賦值01H,除了

MOV 0C8H,#01H 和

T2CON EQU 0C8H MOV T2CON,#01H 外,還有用包含頭文件的方法 #include MOV T2CON,#01H 此時,需要將A51中的“Defines 8051 SFR Names”的勾去掉。

19、指針

C51 提供一個3 字節(jié)的通用存儲器指針。通用指針的頭一個字節(jié)表明指針所指的存儲 區(qū)空間,另外兩個字節(jié)存儲16 位偏移量。對于DATA IDATA 和PDATA 段只需要8 位偏移量。Keil 允許使用者規(guī)定指針指向的存儲段,這種指針叫具體指針。使用具體指針的好處是節(jié)省了存儲空間編譯器不用為存儲器選擇和決定正確的存儲器操作指令產(chǎn)生代碼這樣就使代碼更加簡短但你必須保證指針不指向你所聲明的存儲區(qū)以外的地方否則會產(chǎn)生錯誤而且很難調(diào)試。

由于使用具體指針能夠節(jié)省不少時間所以我們一般都不使用通用指針。

20、EEPROM存放開關機(復位)次數(shù)方法:每次開機(復位)讀取EEPROM存放開關機的數(shù)據(jù),并加1后重新寫入EEPROM。

21、C51中,將printf函數(shù)與串口輸出結(jié)合注意事項:

a、關串口中斷;

b、初始化串口,并使TI=1;

c、KEIL里擴展出了b(8位),h(16位),l(32位)來對輸入字節(jié)寬的設置

在Keil C51中用printf輸出一個單字節(jié)變量時要使用%bd,若使用%d,則默認為雙字節(jié)寬度,輸出可能會出錯。如

unsigned char counter;printf(“Current count: %bdn”, counter);而在標準C語言中都是使用%d: printf(“Current count: %dn”, counter);d、輸出數(shù)據(jù)類型的長度應與定義的數(shù)據(jù)類型長度一致,如:

uint tem2=97;

printf(“%c,%bdn”,tem2,tem2);第一個輸出會出錯。

22、我一般不刻意的注意這個,都是從軟件自身找問題的。

我寫程序時對于軟件抗干擾都是在程序狀態(tài)上考慮意外情況的,例如:

if(a == 1){...} else if(a == 2){....} else{//這個else 一定得加的,即使自己認為不可能出現(xiàn)的情況也要加上

..//經(jīng)過好多程序走飛的情況發(fā)現(xiàn):大多情況都是缺少這個語句條件的,這 //語句可以寫成重新初始化a } 還有程序出現(xiàn)堆棧比較深的運算(例如浮點乘除法后)或中斷比較深,我加2個_nop_();

23、STC12C5410AD外部RAM使用方法:

a.在Keil中設置外部RAM的起始地址和大小,如下圖

b.將變量定義為xdata即可。

24、中斷嵌套

當有外部中斷0時,中斷標志位IE0由硬件自動置1,進入中斷服務程序后,IE0被自動清0。若外部中斷0觸發(fā)信號在執(zhí)行完中斷服務程序后仍沒有撤除,就會再次使已經(jīng)變0的中斷標志位IE0置1,再次進入中斷服務程序;若在響應中斷服務程序期間,再次產(chǎn)生外部中斷0觸發(fā)信號時,此中斷不能被識別,因為CPU在響應中斷時會自動關閉同一中斷。

如果外部中斷0比外部中斷1的優(yōu)先級高,當在響應外部中斷0期間產(chǎn)生外部中斷1時,如果執(zhí)行完外部中斷0后,外部中斷1的中斷請求標志位IE1仍沒有清除的話,將會響應外部中斷1的請求;但是如果在響應外部中斷0期間,外部中斷1的觸發(fā)信號產(chǎn)生后又撤除的話,IE1也會自動清除,也就是說,執(zhí)行完外部中斷0后,不會去響應外部中斷1。

當多個中斷源同時向CPU請求中斷時,CPU就可以通過中斷優(yōu)先權(quán)電路率先響應中斷優(yōu)先權(quán)高的中斷請求,而把中斷優(yōu)先權(quán)低的中斷請求暫時擱置起來,等到處理完優(yōu)先權(quán)高的中斷請求后再來響應優(yōu)先權(quán)低的中斷。

如果某一中斷源提出中斷請求后,CPU不能立即響應,只要該中斷請求標志位不被軟件人為清除,中斷請求的狀態(tài)就將一直保持,直到CPU響應中斷為止。但是對于串行口中斷,即使CPU響應了中斷,其中斷標志位RI/TI也不會自動清零,而必須在中斷服務程序中設置

清除RI/TI的指令后,才會再一次地提出中斷請求。

25、在滿足應用要求的前提下,選擇配較低的單片機,較小的RAM/ROM、較低的ADC分辨率、較低的ADC速率,較少的IO管腳都可以降低單片機的整體功耗。當然了,這個得能滿足你產(chǎn)品需求的前提下。

26、對于一個數(shù)字系統(tǒng)而言,其功耗大致滿足公式:P=CV2f。其中C為系統(tǒng)的負載電容,V為電源電壓,f為系統(tǒng)工作頻率[2]。功耗與電源電壓的平方成正比,因此電源電壓對系統(tǒng)的功耗影響最大,其次是工作頻率,再次就是負載電容。負載電容對設計人員而言,一般是不可控的,因此設計一個低功耗系統(tǒng),在不影響系統(tǒng)性能的前提下,盡可能地降低電源的電壓和工作頻率。對于大多數(shù)低功耗單片機來說,工作頻率越低,意味著消耗的電流也越小,但是不能認為頻率越低,系統(tǒng)整體功耗越小,因為工作頻率降低,意味著需要更長的處理時間,其他外圍電路消耗的電能就越多。目前有很多單片機都允許有兩個或者兩個以上的時鐘源,低頻時鐘作為如UART、定時器等外圍功能器件的時鐘源,高頻時鐘作為系統(tǒng)的主時鐘。在不需要高速運行的場合下,低頻時鐘也可以作為系統(tǒng)主時鐘使用。對于需要在工作狀態(tài)與空閑狀態(tài)之間頻繁切換的應用,在考慮單片機本身低功耗的同時,應該考慮切換時間和切換電流。考慮到有些場合單片機的工作特點,選擇單片機不光要關注工作電流,更應該關注單片機休眠時的靜態(tài)電流。單片機豐富的低功耗模式和極低的靜態(tài)電流,在滿足特定應用功能的同時,有效降低系統(tǒng)的功耗。盡量關閉MCU內(nèi)部不用的資源,比如ATmega8內(nèi)部的模擬比較器,默認是開著的,還有ATmega88內(nèi)部的大多數(shù)資源都可以在不用的時候用軟件關閉。

27、定時/ 計數(shù)器的實時性

定時/ 計數(shù)器啟動計數(shù)后,當計滿回0 溢出向主機請求中斷處理,由內(nèi)部硬件自動進行。但從回0 溢出請求中斷到主機響應中斷并作出處理存在時間延遲,且這種延時隨中斷請求時的現(xiàn)場環(huán)境的不同而不同,一般需延時3 個機器周期以上,這就給實時處理帶來誤差差。大多數(shù)應用場合可忽略不計,但對某些要求實時性苛刻的場合,可采用動態(tài)補償措施。

所謂動態(tài)補償,即在中斷服務程序中對THx、TLx 重新置計數(shù)初值時,應將THx、TLx 從回0 溢出又重新從0 開始繼續(xù)計數(shù)的值讀出,并補償?shù)皆嫈?shù)初值中去進行重新設置。可考慮如下補償方法: CLR EA ;禁止中斷

MOV A,T L x ;讀TLx 中已計數(shù)值 ADD A,#LOW ;LOW 為原低字節(jié)計數(shù)初值 MOV T L x,A ;設置低字節(jié)計數(shù)初值 MOV A,#HIGH ;原高字節(jié)計數(shù)初值送A ADDC A,T H x ;高字節(jié)計數(shù)初值補償 MOV T H x,A ;置高字節(jié)計數(shù)初值 SETB EA ;開中斷

28、動態(tài)讀取運行中的定時器/計數(shù)值

在動態(tài)讀取運行中的定時/ 計數(shù)器的計數(shù)值時,如果不加注意,就可能出錯。這是因為不可能在同一時刻同時讀取THx 和TLx 中的計數(shù)值。比如,先讀TLx 后讀THx,因為定時/ 計數(shù)器處于運行狀態(tài),在讀TLx 時尚未產(chǎn)生向THx 進位,而在讀THx 前已產(chǎn)生進位,這時讀得的THx 就不對了;同樣,先讀THx 后讀TLx 也可能出錯。

一種可避免讀錯的方法是:先讀THx,后讀TLx,將兩次讀得的THx 進行比較;若兩次讀得的值相等,則可確定讀的值是正確的,否則重復上述過程,重復讀得的值一般不會再錯。此法的軟件編程如下:

RDTM: MOV A,THx ;讀取THx 存A 中 MOV R0,TLx ; 讀取TLx 存R0 中

CJNE A,THx,RDTM ;比較兩次THx 值,若相等,則讀得的值正確,否則重讀 MOV R1,A ;將THx 存于R1 中

29、掉電及空閑模式

掉電方式

當PCON中的第二位PD為1時,進入掉電模式,不會執(zhí)行任何指令,外部時鐘停振,片內(nèi)所有功能部件停止工作,如定時器,串行口,外部中斷(部分增強型8051的外部中斷可以工作),但片內(nèi)RAM和SFR的內(nèi)容保持不變。標準8051從掉電狀態(tài)退出的惟一方法是硬件復位(部分增強型8051還可以通過外部中斷來退出掉電狀態(tài)),復位后,SFR被重新初始化,但RAM的內(nèi)容不變。因此,若要使得8051在供電恢復正常后繼續(xù)執(zhí)行掉電前的程序,那就必須在掉電前預先把SFR中的內(nèi)容保護到片內(nèi)RAM,并在供電恢復正常后為SFR恢復到掉電前的狀態(tài)。

當PCON的第一位IDEL為1時,進入空閑模式,CPU停止工作,不會執(zhí)行任何指令,但中斷、串行口和定時器可以繼續(xù)工作。此時,CPU現(xiàn)場(即SP、PC、PSW和ACC等)、片內(nèi)RAM和SFR中其他寄存器內(nèi)容均維持不變。退出空閑模式有兩種方法:

一、被允許中斷的中斷源發(fā)出中斷請求;

二、硬件復位。30、看門狗應用

將喂狗操作(取反指令,如 CPL P1.0)分成兩步,放在主程序和中斷里執(zhí)行。如將SETB P1.0放在主程序中,將CLR P1.0放在中斷里,這樣可以避免主程序跑飛,中斷功能正常或者主程序正常,而中斷跑飛的情況導致看門狗失效。

31、volatile作用

如果將將變量加上volatile修飾,則編譯器保證對此變量的讀寫操作都不會被優(yōu)化(肯定執(zhí)行)。此例中i也應該如此說明。

一般說來,volatile用在如下的幾個地方:

1、中斷服務程序中修改的供其它程序檢測的變量需要加volatile;

2、多任務環(huán)境下各任務間共享的標志應該加volatile;

3、存儲器映射的硬件寄存器通常也要加volatile說明,因為每次對它的讀寫都可能由不同意義;

另外,以上這幾種情況經(jīng)常還要同時考慮數(shù)據(jù)的完整性(相互關聯(lián)的幾個標志讀了一半被打斷了重寫),在1中可以通過關中斷來實現(xiàn),2中可以禁止任務調(diào)度,3中則只能依靠硬件的良好設計了。32、51準雙向口讀取:

只有1條指令:

MOV A,P1為讀端口寄存器 有兩條指令: MOV A,#0FFH MOV A,P1為讀引腳

33、采用C語言和匯編語言混合編程是最佳的選擇;

34、系統(tǒng)投入運行的最初時刻,應對系統(tǒng)進行自檢和初始化。開機自檢在初始化前執(zhí)行,如果自檢無誤,則對系統(tǒng)進行正常初始化,通常包括硬件初始化和軟件初始化兩個部分。硬件初始化是指對系統(tǒng)中的各種硬件資源設定明確的初始狀態(tài),如對各種可編程芯片進行編程、對各I/O端口設定初始狀態(tài)和為單片機的硬件資源分配任務等。軟件初始化包括,對中斷的安排、對堆棧的安排、狀態(tài)變量的初始化、各種軟件標志的初始化、系統(tǒng)時鐘的初始化和各

種變量儲存單元的初始化等。

35、按鍵連擊速度一般為3-4次/s。

36、復合鍵利用兩個以上按鍵同時按下時產(chǎn)生的按鍵效果,但實際情況下,不可能做到真正的“同時按下”,它們的時間差可以長達50ms左右,故當檢測到有KEY1按鍵按下時,還要等待超過50ms,再判斷是否還有其他按鍵按下,再解析按鍵。

37、數(shù)碼管顯示閃爍效果時,一般閃爍速度為1-4次/s。

38、大電流和高電壓設備的啟動和關閉都是由軟件指令來完成,這些指令執(zhí)行后,必然引起強烈的干擾,這些干擾不能算隨機干擾,它們與軟件完全相關;可以在最后才執(zhí)行這些可能引起強烈干擾的I/O操作,之后立即進入睡眠狀態(tài),這樣就不會干擾到CPU,等CPU醒來后,干擾的高峰也基本過去。

39、用積分時間為20ms整數(shù)倍的雙積分型A/D轉(zhuǎn)換方式,能有效地抑制50Hz工頻干擾。40、掉電檢測電路必須在電壓下降到CPU最低工作電壓之前就提出中斷申請,提前時間為幾百us到數(shù)ms,以便掉電中斷程序進行掉電保護。

41、用定時器作看門狗:當為專職看門狗時,在主程序中周期性清0定時器計數(shù)值,以使定時器中斷不能產(chǎn)生,當產(chǎn)生定時器中斷時,表明看門狗溢出,此時應執(zhí)行出錯處理程序或者進行復位。當為兼職看門狗時,可以在定時器中斷程序?qū)τ嫈?shù)值進行加1,若計數(shù)值大于某值時,表明看門狗溢出,而主程序中應周期性地對計數(shù)值進行清0。

42、中斷中,沖突發(fā)生的條件:

1)某一資源為中斷程序和主程序所使用;該資源可以為1個變量,也可以為1個數(shù)組或者1個緩沖區(qū)。

2)中斷程序或主程序?qū)υ撡Y源進行了寫操作;

3)主程序不能用一條指令對資源完成讀或者寫操作。(這條不對,參考深入淺出AVR單片機P100的例子)

當這三個條件均滿足時,即有可能發(fā)生資源沖突,導致程序偶然運行不正常。為了避免發(fā)生沖突,可以在主程序中先關中斷,再對資源進行讀或?qū)懀Y(jié)束后再開中斷。

當主程序?qū)Y源的訪問比較費時,長期關中斷可能影響系統(tǒng)的實時性;解決的辦法是盡可能縮短關中斷的時間,將一邊訪問,一邊處理的工作方式改為集中訪問,分批處理。如果是讀該資源,則關中斷迅速將該資源的內(nèi)容轉(zhuǎn)移到緩沖區(qū),再開中斷,然后再對緩沖區(qū)中的信息進行處理;如果是寫該資源,則先邊運算邊寫緩沖區(qū),全部寫好后再關中斷,然后迅速將緩沖區(qū)中的內(nèi)容復制到該資源中,邊可以開中斷了。

43、A/B*C的運算方案不如(A*C)/B的運算方案精度高。因此,應盡可能將出現(xiàn)偏差的運算往后排,先進行無偏差或偏差小的運算。在定點運算系統(tǒng)中,加減法只要不超限,是沒有偏差的,乘法運算的結(jié)果會使字長增加,如雙字節(jié)乘雙字節(jié),積為四字節(jié),如果保留全部結(jié)果,則沒有偏差的。乘法運算的結(jié)果會使字長增加,如雙字節(jié)乘雙字節(jié),積為四字節(jié),如果保留全部結(jié)果,則沒有偏差;如果受字長限制,則要舍去低位字節(jié),從而產(chǎn)生舍入偏差。除法幾乎都是有偏差的能夠剛好整除的情況是很少的。在浮點運算系統(tǒng)中,加減法由于要進行對階操作,當兩操作數(shù)的階碼相差較大時,絕對值大的數(shù)有可能將絕對值小的數(shù)淹沒,使運算的結(jié)果仍為絕對值大的數(shù),一點兒也看不出絕對值小的數(shù)對結(jié)果的影響。相比之下,浮點乘法和浮點除法引起的偏差就比較小,它們能夠保持一個比較穩(wěn)定的運算精度。另外,不管在定點系統(tǒng)中還是在浮點系統(tǒng)中,都要盡可能避免兩個數(shù)值相近的數(shù)過早相減,因為他們都可能是近似值,相減以后,差的有效數(shù)值大大減少,必然帶來更大的相對誤差。經(jīng)過后續(xù)運算之后,結(jié)果可能離真實值相差甚遠。再有,盡可能不要用絕對值小的數(shù)作分母,否則引起的誤差也是很大的。

44、要對軟件標志位的使用進行說明;對于全局定義的軟件標志,它有惟一的定義;對于局

部定義的軟件標志,必須注明其有效范圍。

45、軟件理論已經(jīng)證明:任何一個程序(除某些短小的子程序外)都存在錯誤(缺陷),人們可以通過合理的測試來證明它仍然存在錯誤,卻無法證明它已經(jīng)沒有錯誤。軟件測試應該把發(fā)現(xiàn)錯誤作為目的,而不能把“程序調(diào)通”作為目的。

1.P0口能驅(qū)動8個TTL電路意思: 8051單片機P0口驅(qū)動8個TTL電路的意思,TTL電路輸入懸浮時相當于輸入高電平,因此P0口輸出高電平驅(qū)動TTL電路幾乎不需輸出電流。TTL電路輸入為低電平時最少要釋放1mA電流,因此P0口輸出低電平時吸收的電流大于8mA。TTL輸出高電平最大1.6mA,輸出低電平時吸收的最大電流 16mA。51輸出最好用低電平有效,推動PNP管,因為51復位后IO為高電平,如果用高電平有效推N管的話上電復位后會先讓外部電路動做。

2.在51里,有一條指令沒有寫進書本,機器碼為A5,執(zhí)行操作:將下一個字節(jié)跳過而不管它是單字節(jié)指令還是雙字節(jié)或三字節(jié)指令的一部分.如果反匯編工具不識別A5指令的話,你在A5以后的程序反匯編后就錯亂無章.當成個數(shù)據(jù),用db a5 即可

3.有些51系統(tǒng)容易復位,一般是電路設計上的問題。很多電路介紹的復位電路都是10u和8.2k,但是在實踐過程中我們發(fā)現(xiàn)該電路在電源不穩(wěn)時很容易復位,特別是附近有大干擾時,如繼電器動作等。我建議使用22u和1k的復位電路,有許多電路改為該數(shù)值后就工作穩(wěn)定了。當然,最好的辦法還是使用專用復位電路或三極管電路,但是那樣要增加成本和體積。

4.電路中的濾波電容一定要注意加上,最好每個芯片都再加一個約0.1uf的電容,這樣對電路的穩(wěn)定性很有好處。如果使用了看門狗電路,就有可能是軟件問題,程序工作到某些環(huán)節(jié)時忘記了復位看門狗,結(jié)果計數(shù)滿了就復位了。

5.如果在中斷程序中改變了多字節(jié)類型的變量,那么中斷程序以外的程序中(主程序,子函數(shù)),讀寫前要關中斷,讀寫后再開中斷。舉一反三:

其他的數(shù)據(jù)類型也可能有這種影響。例如:長整型、浮點型。

上面的例子是中斷里寫,主程序中讀。相反主程序?qū)懀袛嗬镒x也可能出錯。

6.教你一招,別說我損。。

寫一個測試代碼,反復向EEPROM中的某幾個不用的空位字節(jié)寫入0x55,直到把它干到壽命終結(jié)不能寫為止,如果按照10MS寫一個字節(jié)計算的話,大約只需要20分鐘就能干掉它。然后向這個芯片中燒入你的正常代碼,當然了,這個代碼中應該有一段上電檢測EEPROM這幾個字節(jié)的代碼,先嘗試向它寫入0Xaa,然后再讀出來看看是否寫入成功,如果沒寫入則再來兩次,如果始終不能寫入,這當作檢查通過,如果就判斷為檢查失敗,這個時候代碼要裝著‘不知情’繼續(xù)執(zhí)行正確代碼,下面的‘破壞’行為應該如何做就不要我講了把? 破壞行為要裝的掩蔽一點,例如調(diào)一段代碼檫除FLASH的代碼,嘿嘿,那對方肯定以為CHIP質(zhì)量不好容易出現(xiàn)FLASH數(shù)據(jù)丟失,如果對方使用了AD什么的,可以偶爾人為讓它波動大一點,這樣對方一般只會懷疑PCB和硬件電路弄的不好,而不會想到是代碼動手腳了,長久以后他的用戶肯定也會認為他們的產(chǎn)品質(zhì)量不好,你這個時候就可以向他的客戶推廣你的產(chǎn)品了。。

上電寫EEPROM的次數(shù)要在你自己的產(chǎn)品質(zhì)量承諾的壽命時間之內(nèi),否則你自己的產(chǎn)品也

可能增加維修。。

這個方法特別適合在外接單掙錢的工程師,你可能給了對方幾個CHIP做測試,對方測試通過偏說不行,就是不給你余款,然后把CHIP拿去CRACK,妄想省掉這個錢,NND,讓他們見鬼去把,俺這招已經(jīng)對付了不少不良分子。。

7.AD鍵盤

8.防解密高招

高招, 解密

使用一些帶內(nèi)部晶振和內(nèi)部EEPROM的單片機,如PIC16F913和ATMEGA8等,帶內(nèi)部晶振的單片機有一個寄存器OSCTUNE(或OSCCAL),這個是芯片廠家用來校準內(nèi)部晶振的,范圍從0-31,出廠時同型號的單片機這個寄存器的值是不一樣.我們可以利用一些隱藏功能,將OSCTUNE寄存器的值存入內(nèi)部的EEPROM中,開機時讀取EEPROM的值,再與OSCTUNE的值相比較, 若二者相同系統(tǒng)正常工作,若不

相同則不正常工作.解密者將解密的程序燒寫進單片機中后,會發(fā)大部分的芯片不能正常工作,因為他們不知道這個隱藏的功能.舉例說明: 芯片為PIC16F913,這個廠品有4個按鍵(KEY0、KEY1、KEY2、KEY3),內(nèi)部我們可以設定這樣子一個隱藏的功能,如果KEY0與KEY1同時按下3秒鐘以上,會將OSCTUNE寄存器存入單片機的EEPROM中。

開機復位后,讀取EEPROM中的數(shù)據(jù),與OSCTUNE寄存器相比較,若二者相同系統(tǒng)正常工作,若不相同則不正常工作。以上有三個重點:

1、對于OSCTUNE寄存器不要進行寫的操作,只進行讀的操作,因為寫了一次以后,就一直是你寫的這個數(shù)據(jù)的。

2、剛才介紹的KEY0、KEY1同時按下3秒鐘這個功能,可不能讓解密者(包括產(chǎn)品的用戶)知道,當然大家可以用別的隱藏的功能。

3、單片機中的OSCTUNE寄存器(或OSCCAL)的值,同一種型號的單片機不是每一個都是一樣的,有32個數(shù)據(jù),也就是說32個芯片中有一個是與解密的單片機是一樣的。這樣子造成的后果是:解密者解密了你的程序以后,卻發(fā)現(xiàn)有些單片機可以正常工作,可有些單片機不能正常工作,可以說是大部分的單片機不能正常工作。

不過需要注意一下:要是遇到強干擾把EEPROM中的數(shù)據(jù)改變了看客戶怎么收拾你!

9.PIC16F887A中,要求SLEEP指令后的下一條指令為NOP;不知51和AVR的芯片是否需要注意這一點。經(jīng)查,AVR的datasheet無此要求,可能是其喚醒時,存在啟動延時。

10.中斷隨時隨刻都有可能產(chǎn)生,故編寫程序時,需要時刻注意中斷的影響。

11.注意以下語句在某些編譯器下,結(jié)果可能出錯:

unsigned char a,b;

unsigned int sum;

a=0x80;

b=0x80;

sum=a+b;

12.編程序最重要是好維護。幾個執(zhí)行時間和程序的可讀性比,和開發(fā)時間比,我認為是不用考慮的。為了幾個機器周期而把程序搞得很復雜,是非常愚蠢的行為。可是很多人多樂此不疲啊。總體系統(tǒng)的算法是要考慮優(yōu)化的問題的,這點我是贊同的。天天在技術上對著幾行程序去優(yōu)化,而導致開發(fā)速度減慢,是非常愚蠢的行為。

13.串口通信協(xié)議:引導碼/識別碼+長度+命令字+data+校驗

通過引導碼/識別碼、長度、校驗三步檢測 每當出錯則丟棄當前數(shù)據(jù)并還原接收狀態(tài)和空間…………

14.當準備調(diào)試一塊板的時候,一定要先認真的做好目視檢查,檢查在焊接的過程中是否有可見的短路和管腳搭錫等故障,檢查是否有元器件型號放置錯誤,第

一腳放置錯誤,漏裝配等問題,然后用萬用表測量各個電源到地的電阻,以檢查是否有短路,這個好習慣可以避免貿(mào)然上電后損壞單板。調(diào)試的過程中要有平和的心態(tài),遇見問題是非常正常的,要做的就是多做比較和分析,逐步的排除可能的原因,要堅信“凡事都是有辦法解決的”和“問題出現(xiàn)一定有它的原因”,這樣最后一定能調(diào)試成功。

做一個硬件設計人員要鍛煉出良好的溝通能力,面對壓力的調(diào)節(jié)能力,同一時間處理多個事務的協(xié)調(diào)和決斷能力和良好平和的心態(tài),還有細心和認真等等。

對初學者來說,在新的領域面前一窮二白,要學的東西太多太多,一味機械地試圖學習這些“高深的語法”和“看不懂的技巧”,胡亂模仿些別人的“優(yōu)秀風格”,不能說完全無所得,只能說會學得很累,而且往往事倍功半---久而久之,信心喪失殆盡,半途而廢。學習編程,做自己便好。學習眼前能夠看懂的內(nèi)容,多寫自己會寫的程序。對于已經(jīng)學到的東西,仔細地體會、思考,發(fā)掘其中的發(fā)展;學會用一種研究的心態(tài)去考察你的每一個疑問;不可以輕易地人云亦云,在網(wǎng)上看了別人無責任的經(jīng)驗,甚至是寫錯了,丟在一邊,懶得改的東西之后,就放棄了自己的探索;要學會堅持自我,遇到別人先進的做法,在自己還沒有體會到自己當前這種做法的劣勢之前,不要輕易盲從;同樣,使用了先進的方法,在沒有同時理解這種做法的優(yōu)點和缺點之前,請不要輕易地感嘆“這種方法好,跟帖頂一下!”“大師高手寧有種乎?”堅持“體會到了才是學到了”的態(tài)度,最終形成自己的風格,形成自己的技巧。

第四篇:多線程編程知識總結(jié)

多線程編程

一、問題的提出

1.1問題的引出

編寫一個耗時的單線程程序:

新建一個基于對話框的應用程序SingleThread,在主對話框IDD_SINGLETHREAD_DIALOG添加一個按鈕,ID為IDC_SLEEP_SIX_SECOND,標題為“延時6秒”,添加按鈕的響應函數(shù),代碼如下:

void CSingleThreadDlg::OnSleepSixSecond(){ Sleep(6000);//延時6秒 } 編譯并運行應用程序,單擊“延時6秒”按鈕,你就會發(fā)現(xiàn)在這6秒期間程序就象“死機”一樣,不在響應其它消息。為了更好地處理這種耗時的操作,我們有必要學習——多線程編程。

1.2多線程概述

進程和線程都是操作系統(tǒng)的概念。進程是應用程序的執(zhí)行實例,每個進程是由私有的虛擬地址空間、代碼、數(shù)據(jù)和其它各種系統(tǒng)資源組成,進程在運行過程中創(chuàng)建的資源隨著進程的終止而被銷毀,所使用的系統(tǒng)資源在進程終止時被釋放或關閉。

線程是進程內(nèi)部的一個執(zhí)行單元。系統(tǒng)創(chuàng)建好進程后,實際上就啟動執(zhí)行了該進程的主執(zhí)行線程,主執(zhí)行線程以函數(shù)地址形式,比如說main或WinMain函數(shù),將程序的啟動點提供給Windows系統(tǒng)。主執(zhí)行線程終止了,進程也就隨之終止。

每一個進程至少有一個主執(zhí)行線程,它無需由用戶去主動創(chuàng)建,是由系統(tǒng)自動創(chuàng)建的。用戶根據(jù)需要在應用程序中創(chuàng)建其它線程,多個線程并發(fā)地運行于同一個進程中。一個進程中的所有線程都在該進程的虛擬地址空間中,共同使用這些虛擬地址空間、全局變量和系統(tǒng)資源,所以線程間的通訊非常方便,多線程技術的應用也較為廣泛。

多線程可以實現(xiàn)并行處理,避免了某項任務長時間占用CPU時間。要說明的一點是,對于單處理器(CPU)的,為了運行所有這些線程,操作系統(tǒng)為每個獨立線程安排一些CPU時間,操作系統(tǒng)以輪換方式向線程提供時間片,這就給人一種假象,好象這些線程都在同時運行。由此可見,如果兩個非常活躍的線程為了搶奪對CPU的控制權(quán),在線程切換時會消耗很多的CPU資源,反而會降低系統(tǒng)的性能。這一點在多線程編程時應該注意。

Win32 SDK函數(shù)支持進行多線程的程序設計,并提供了操作系統(tǒng)原理中的各種同步、互斥和臨界區(qū)等操作。Visual C++中,使用MFC類庫也實現(xiàn)了多線程的程序設計,使得多線程編程更加方便。1.3 Win32 API對多線程編程的支持

Win32 提供了一系列的API函數(shù)來完成線程的創(chuàng)建、掛起、恢復、終結(jié)以及通信等工作。下面將選取其中的一些重要函數(shù)進行說明。

1、HANDLE CreateThread(LPSECURITY_ATTRIBUTES lpThreadAttributes,DWORD dwStackSize,LPTHREAD_START_ROUTINE lpStartAddress,LPVOID lpParameter,DWORD dwCreationFlags,LPDWORD lpThreadId);該函數(shù)在其調(diào)用進程的進程空間里創(chuàng)建一個新的線程,并返回已建線程的句柄,其中各參數(shù)說明如下:

lpThreadAttributes:指向一個 SECURITY_ATTRIBUTES 結(jié)構(gòu)的指針,該結(jié)構(gòu)決定了線程的安全屬性,一般置為 NULL;

dwStackSize:指定了線程的堆棧深度,一般都設置為0;

lpStartAddress:表示新線程開始執(zhí)行時代碼所在函數(shù)的地址,即線程的起始地址。一般情況為(LPTHREAD_START_ROUTINE)ThreadFunc,ThreadFunc 是線程函數(shù)名;

lpParameter:指定了線程執(zhí)行時傳送給線程的32位參數(shù),即線程函數(shù)的參數(shù);

dwCreationFlags:控制線程創(chuàng)建的附加標志,可以取兩種值。如果該參數(shù)為0,線程在被創(chuàng)建后就會立即開始執(zhí)行;如果該參數(shù)為CREATE_SUSPENDED,則系統(tǒng)產(chǎn)生線程后,該線程處于掛起狀態(tài),并不馬上執(zhí)行,直至函數(shù)ResumeThread被調(diào)用;

lpThreadId:該參數(shù)返回所創(chuàng)建線程的ID;

如果創(chuàng)建成功則返回線程的句柄,否則返回NULL。

2、DWORD SuspendThread(HANDLE hThread);該函數(shù)用于掛起指定的線程,如果函數(shù)執(zhí)行成功,則線程的執(zhí)行被終止。

3、DWORD ResumeThread(HANDLE hThread);該函數(shù)用于結(jié)束線程的掛起狀態(tài),執(zhí)行線程。

4、VOID ExitThread(DWORD dwExitCode);該函數(shù)用于線程終結(jié)自身的執(zhí)行,主要在線程的執(zhí)行函數(shù)中被調(diào)用。其中參數(shù)dwExitCode用來設置線程的退出碼。

5、BOOL TerminateThread(HANDLE hThread,DWORD dwExitCode);

一般情況下,線程運行結(jié)束之后,線程函數(shù)正常返回,但是應用程序可以調(diào)用TerminateThread強行終止某一線程的執(zhí)行。各參數(shù)含義如下: hThread:將被終結(jié)的線程的句柄;

dwExitCode:用于指定線程的退出碼。

使用TerminateThread()終止某個線程的執(zhí)行是不安全的,可能會引起系統(tǒng)不穩(wěn)定;雖然該函數(shù)立即終止線程的執(zhí)行,但并不釋放線程所占用的資源。因此,一般不建議使用該函數(shù)。

6、BOOL PostThreadMessage(DWORD idThread,UINT Msg,WPARAM wParam,LPARAM lParam);該函數(shù)將一條消息放入到指定線程的消息隊列中,并且不等到消息被該線程處理時便返回。idThread:將接收消息的線程的ID;

Msg:指定用來發(fā)送的消息;

wParam:同消息有關的字參數(shù);

lParam:同消息有關的長參數(shù);

調(diào)用該函數(shù)時,如果即將接收消息的線程沒有創(chuàng)建消息循環(huán),則該函數(shù)執(zhí)行失敗。

1.4.Win32 API多線程編程例程

例程1 [MultiThread1] 一個簡單的線程。注意事項:

? Volatile:關鍵字:

volatile是要求C++編譯器不要自作聰明的把變量緩沖在寄存器里.因為該變量可能會被意外的修改。(多個線程或其他原因)

如從串口讀數(shù)據(jù)的場合,把變量緩沖在寄存器里,下次去讀寄存器就沒有意義了.因為串口的數(shù)據(jù)可能隨時會改變的.加鎖訪問用于多個線程的場合.在進入臨界區(qū)時是肯定要加鎖的.volatile也加上,以保證從內(nèi)存中讀取變量的值.? 終止線程:

Windows終止線程運行的四種方法 終止線程運行

若要終止線程的運行,可以使用下面的方法:

? 線程函數(shù)返回(最好使用這種方法)。

? 通過調(diào)用 ExitThread 函數(shù),線程將自行撤消(最好不要使用這種方法)。

? 同一個進程或另一個進程中的線程調(diào)用 TerminateThread 函數(shù)(應該避免使用這種方法)。

? 包含線程的進程終止運行(應該避免使用這種方法)。

下面將介紹終止線程運行的方法,并且說明線程終止運行時會出現(xiàn)什么情況。

? 線程函數(shù)返回

始終都應該將線程設計成這樣的形式,即當想要線程終止運行時,它們就能夠返回。這是確保所有線程資源被正確地清除的唯一辦法。

如果線程能夠返回,就可以確保下列事項的實現(xiàn):

? 在線程函數(shù)中創(chuàng)建的所有 C++ 對象均將通過它們的撤消函數(shù)正確地撤消。

? 操作系統(tǒng)將正確地釋放線程堆棧使用的內(nèi)存。

? 系統(tǒng)將線程的退出代碼(在線程的內(nèi)核對象中維護)設置為線程函數(shù)的返回值。

? 系統(tǒng)將遞減線程內(nèi)核對象的使用計數(shù)。? 使用 ExitThread 函數(shù)

可以讓線程調(diào)用 ExitThread 函數(shù),以便強制線程終止運行:

VOID ExitThread(DWORD dwExitCode);

該函數(shù)將終止線程的運行,并導致操作系統(tǒng)清除該線程使用的所有操作系統(tǒng)資源。但是,C++ 資源(如 C++ 類對象)將不被撤消。由于這個原因,最好從線程函數(shù)返回,而不是通過調(diào)用 ExitThread 來返回。

當然,可以使用 ExitThread 的 dwExitThread 參數(shù)告訴系統(tǒng)將線程的退出代碼設置為什么。ExitThread 函數(shù)并不返回任何值,因為線程已經(jīng)終止運行,不能執(zhí)行更多的代碼。? 使用 TerminateThread 函數(shù)

調(diào)用 TerminateThread 函數(shù)也能夠終止線程的運行:

BOOL TerminateThread(HANDLE hThread, DWORD dwExitCode);

與 ExitThread 不同,ExitThread 總是撤消調(diào)用的線程,而 TerminateThread 能夠撤消任何線程。hThread 參數(shù)用于標識被終止運行的線程的句柄。當線程終止運行時,它的退出代碼成為你作為 dwExitCode 參數(shù)傳遞的值。同時,線程的內(nèi)核對象的使用計數(shù)也被遞減。

注意 TerminateThread 函數(shù)是異步運行的函數(shù),也就是說,它告訴系統(tǒng)你想要線程終止運行,但是,當函數(shù)返回時,不能保證線程被撤消。如果需要確切地知道該線程已經(jīng)終止運行,必須調(diào)用 WaitForSingleObject 或者類似的函數(shù),傳遞線程的句柄。

設計良好的應用程序從來不使用這個函數(shù),因為被終止運行的線程收不到它被撤消的通知。線程不能正確地清除,并且不能防止自己被撤消。

注意 當使用返回或調(diào)用 ExitThread 的方法撤消線程時,該線程的內(nèi)存堆棧也被撤消。但是,如果使用 TerminateThread,那么在擁有線程的進程終止運行之前,系統(tǒng)不撤消該線程的堆棧。Microsoft故意用這種方法來實現(xiàn) TerminateThread。如果其他仍然正在執(zhí)行的線程要引用強制撤消的線程堆棧上的值,那么其他的線程就會出現(xiàn)訪問違規(guī)的問題。如果將已經(jīng)撤消的線程的堆棧留在內(nèi)存中,那么其他線程就可以繼續(xù)很好地運行。

此外,當線程終止運行時,DLL 通常接收通知。如果使用 TerminateThread 強迫線程終止,DLL 就不接收通知,這能阻止適當?shù)那宄谶M程終止運行時撤消線程。當線程終止運行時,會發(fā)生下列操作:

? 線程擁有的所有用戶對象均被釋放。在 Windows 中,大多數(shù)對象是由包含創(chuàng)建這些對象的線程的進程擁有的。但是一個線程擁有兩個用戶對象,即窗口和掛鉤。當線程終止運行時,系統(tǒng)會自動撤消任何窗口,并且卸載線程創(chuàng)建的或安裝的任何掛鉤。其他對象只有在擁有線程的進程終止運行時才被撤消。

? 線程的退出代碼從 STILL_ACTIVE 改為傳遞給 ExitThread 或 TerminateThread 的代碼。

? 線程內(nèi)核對象的狀態(tài)變?yōu)橐淹ㄖ?/p>

? 如果線程是進程中最后一個活動線程,系統(tǒng)也將進程視為已經(jīng)終止運行。

? 線程內(nèi)核對象的使用計數(shù)遞減 1。

當一個線程終止運行時,在與它相關聯(lián)的線程內(nèi)核對象的所有未結(jié)束的引用關閉之前,該內(nèi)核對象不會自動被釋放。

一旦線程不再運行,系統(tǒng)中就沒有別的線程能夠處理該線程的句柄。然而別的線程可以調(diào)用 GetExitcodeThread 來檢查由 hThread 標識的線程是否已經(jīng)終止運行。如果它已經(jīng)終止運行,則確定它的退出代碼:

BOOL GetExitCodeThread(HANDLE hThread, PDOWRD pdwExitCode);退出代碼的值在 pdwExitCode 指向的 DWORD 中返回。如果調(diào)用 GetExitCodeThread 時線程尚未終止運行,該函數(shù)就用 STILL_ACTIVE 標識符(定義為 0x103)填入 DWORD。如果該函數(shù)運行成功,便返回 TRUE。

? 線程的定義:

例程2[MultiThread2] 傳送一個一個整型的參數(shù)到一個線程中,以及如何等待一個線程完成處理。

DWORD WaitForSingleObject(HANDLE hHandle,DWORD dwMilliseconds);

hHandle:為要監(jiān)視的對象(一般為同步對象,也可以是線程)的句柄;

dwMilliseconds:為hHandle對象所設置的超時值,單位為毫秒;

當在某一線程中調(diào)用該函數(shù)時,線程暫時掛起,系統(tǒng)監(jiān)視hHandle所指向的對象的狀態(tài)。如果在掛起的dwMilliseconds毫秒內(nèi),線程所等待的對象變?yōu)橛行盘枲顟B(tài),則該函數(shù)立即返回;如果超時時間已經(jīng)到達dwMilliseconds毫秒,但hHandle所指向的對象還沒有變成有信號狀態(tài),函數(shù)照樣返回。參數(shù)dwMilliseconds有兩個具有特殊意義的值:0和INFINITE。若為0,則該函數(shù)立即返回;若為INFINITE,則線程一直被掛起,直到hHandle所指向的對象變?yōu)橛行盘枲顟B(tài)時為止。

例程3[MultiThread3] 傳送一個結(jié)構(gòu)體給一個線程函數(shù),可以通過傳送一個指向結(jié)構(gòu)體的指針參數(shù)來完成。補充一點:如果你在void CMultiThread3Dlg::OnStart()函數(shù)中添加/* */語句,編譯運行你就會發(fā)現(xiàn)進度條不進行刷新,主線程也停止了反應。什么原因呢?這是因為WaitForSingleObject函數(shù)等待子線程(ThreadFunc)結(jié)束時,導致了線程死鎖。因為WaitForSingleObject函數(shù)會將主線程掛起(任何消息都得不到處理),而子線程ThreadFunc正在設置進度條,一直在等待主線程將刷新消息處理完畢返回才會檢測通知事件。這樣兩個線程都在互相等待,死鎖發(fā)生了,編程時應注意避免。

例程4[MultiThread4] 測試在Windows下最多可創(chuàng)建線程的數(shù)目。

二、MFC中的多線程開發(fā)

2.1 MFC對多線程編程的支持

MFC中有兩類線程,分別稱之為工作者線程和用戶界面線程。二者的主要區(qū)別在于工作者線程沒有消息循環(huán),而用戶界面線程有自己的消息隊列和消息循環(huán)。

工作者線程沒有消息機制,通常用來執(zhí)行后臺計算和維護任務,如冗長的計算過程,打印機的后臺打印等。用戶界面線程一般用于處理獨立于其他線程執(zhí)行之外的用戶輸入,響應用戶及系統(tǒng)所產(chǎn)生的事件和消息等。但對于Win32的API編程而言,這兩種線程是沒有區(qū)別的,它們都只需線程的啟動地址即可啟動線程來執(zhí)行任務。

在MFC中,一般用全局函數(shù)AfxBeginThread()來創(chuàng)建并初始化一個線程的運行,該函數(shù)有兩種重載形式,分別用于創(chuàng)建工作者線程和用戶界面線程。兩種重載函數(shù)原型和參數(shù)分別說明如下:

(1)CWinThread* AfxBeginThread(AFX_THREADPROC pfnThreadProc,LPVOID pParam,nPriority=THREAD_PRIORITY_NORMAL,UINT nStackSize=0,DWORD dwCreateFlags=0,LPSECURITY_ATTRIBUTES lpSecurityAttrs=NULL);

PfnThreadProc:指向工作者線程的執(zhí)行函數(shù)的指針,線程函數(shù)原型必須聲明如下: UINT ExecutingFunction(LPVOID pParam);請注意,ExecutingFunction()應返回一個UINT類型的值,用以指明該函數(shù)結(jié)束的原因。一般情況下,返回0表明執(zhí)行成功。

pParam:傳遞給線程函數(shù)的一個32位參數(shù),執(zhí)行函數(shù)將用某種方式解釋該值。它可以是數(shù)值,或是指向一個結(jié)構(gòu)的指針,甚至可以被忽略;

nPriority:線程的優(yōu)先級。如果為0,則線程與其父線程具有相同的優(yōu)先級;

nStackSize:線程為自己分配堆棧的大小,其單位為字節(jié)。如果nStackSize被設為0,則線程的堆棧被設置成與父線程堆棧相同大小; dwCreateFlags:如果為0,則線程在創(chuàng)建后立刻開始執(zhí)行。如果為CREATE_SUSPEND,則線程在創(chuàng)建后立刻被掛起;

lpSecurityAttrs:線程的安全屬性指針,一般為NULL;

(2)CWinThread* AfxBeginThread(CRuntimeClass* pThreadClass,int nPriority=THREAD_PRIORITY_NORMAL,UINT nStackSize=0,DWORD dwCreateFlags=0,LPSECURITY_ATTRIBUTES lpSecurityAttrs=NULL);

pThreadClass 是指向 CWinThread 的一個導出類的運行時類對象的指針,該導出類定義了被創(chuàng)建的用戶界面線程的啟動、退出等;其它參數(shù)的意義同形式1。使用函數(shù)的這個原型生成的線程也有消息機制,在以后的例子中我們將發(fā)現(xiàn)同主線程的機制幾乎一樣。下面對CWinThread類的數(shù)據(jù)成員及常用函數(shù)進行簡要說明。

? ? ? m_hThread:當前線程的句柄;

m_nThreadID:當前線程的ID;

m_pMainWnd:指向應用程序主窗口的指針

virtual BOOL CWinThread::InitInstance();重載該函數(shù)以控制用戶界面線程實例的初始化。初始化成功則返回非0值,否則返回0。用戶界面線程經(jīng)常重載該函數(shù),工作者線程一般不使用InitInstance()。

virtual int CWinThread::ExitInstance();在線程終結(jié)前重載該函數(shù)進行一些必要的清理工作。該函數(shù)返回線程的退出碼,0表示執(zhí)行成功,非0值用來標識各種錯誤。同InitInstance()成員函數(shù)一樣,該函數(shù)也只適用于用戶界面線程。

2.2 MFC多線程編程實例

例程5 MultiThread5 為了與Win32 API對照,使用MFC 類庫編程實現(xiàn)例程3 MultiThread3。

例程6 MultiThread6[用戶界面線程] ? 創(chuàng)建用戶界面線程的步驟:

1.使用ClassWizard創(chuàng)建類CWinThread的派生類(以CUIThread類為例)class CUIThread : public CWinThread { DECLARE_DYNCREATE(CUIThread)protected: CUIThread();// protected constructor used by dynamic creation

// Attributes public: // Operations public:

// Overrides // ClassWizard generated virtual function overrides //{{AFX_VIRTUAL(CUIThread)public: virtual BOOL InitInstance();virtual int ExitInstance();//}}AFX_VIRTUAL // Implementation protected: virtual ~CUIThread();// Generated message map functions //{{AFX_MSG(CUIThread)

// NOTE-the ClassWizard will add and remove member functions here.//}}AFX_MSG

DECLARE_MESSAGE_MAP()};

2.重載函數(shù)InitInstance()和ExitInstance()。BOOL CUIThread::InitInstance(){ CFrameWnd* wnd=new CFrameWnd;wnd->Create(NULL,“UI Thread Window”);wnd->ShowWindow(SW_SHOW);wnd->UpdateWindow();m_pMainWnd=wnd;return TRUE;}

3.創(chuàng)建新的用戶界面線程 void CUIThreadDlg::OnButton1(){

}

請注意以下兩點:

A、在UIThreadDlg.cpp的開頭加入語句: #include “UIThread.h” B、把UIThread.h中類CUIThread()的構(gòu)造函數(shù)的特性由 protected 改為 public。CUIThread* pThread=new CUIThread();pThread->CreateThread();

用戶界面線程的執(zhí)行次序與應用程序主線程相同,首先調(diào)用用戶界面線程類的InitInstance()函數(shù),如果返回TRUE,繼續(xù)調(diào)用線程的Run()函數(shù),該函數(shù)的作用是運行一個標準的消息循環(huán),并且當收到WM_QUIT消息后中斷,在消息循環(huán)過程中,Run()函數(shù)檢測到線程空閑時(沒有消息),也將調(diào)用OnIdle()函數(shù),最后Run()函數(shù)返回,MFC調(diào)用ExitInstance()函數(shù)清理資源。

你可以創(chuàng)建一個沒有界面而有消息循環(huán)的線程,例如:你可以從CWinThread派生一個新類,在InitInstance函數(shù)中完成某項任務并返回FALSE,這表示僅執(zhí)行InitInstance函數(shù)中的任務而不執(zhí)行消息循環(huán),你可以通過這種方法,完成一個工作者線程的功能。

三、線程間通訊

3.1通訊方式

一般而言,應用程序中的一個次要線程總是為主線程執(zhí)行特定的任務,這樣,主線程和次要線程間必定有一個信息傳遞的渠道,也就是主線程和次要線程間要進行通信。這種線程間的通信不但是難以避免的,而且在多線程編程中也是復雜和頻繁的,下面將進行說明。

3.1.1使用全局變量進行通信

由于屬于同一個進程的各個線程共享操作系統(tǒng)分配該進程的資源,故解決線程間通信最簡單的一種方法是使用全局變量。對于標準類型的全局變量,建議使用volatile 修飾符,它告訴編譯器無需對該變量作任何的優(yōu)化,即無需將它放到一個寄存器中,并且該值可被外部改變。如果線程間所需傳遞的信息較復雜,可以定義一個結(jié)構(gòu),通過傳遞指向該結(jié)構(gòu)的指針進行傳遞信息。

3.1.2使用自定義消息

可以在一個線程的執(zhí)行函數(shù)中向另一個線程發(fā)送自定義的消息來達到通信的目的。一個線程向另外一個線程發(fā)送消息是通過操作系統(tǒng)實現(xiàn)的。利用Windows操作系統(tǒng)的消息驅(qū)動機制,當一個線程發(fā)出一條消息時,操作系統(tǒng)首先接收到該消息,然后把該消息轉(zhuǎn)發(fā)給目標線程,接收消息的線程必須已經(jīng)建立了消息循環(huán)。

3.2例程

例程GlobalObjectTest 該例程演示了如何利用全局變量進行通信

例程7[MultiThread7] 該例程演示了如何使用自定義消息進行線程間通信。首先,主線程向CCalculateThread線程發(fā)送消息WM_CALCULATE,CCalculateThread線程收到消息后進行計算,再向主線程發(fā)送WM_DISPLAY消息,主線程收到該消息后顯示計算結(jié)果。步驟:

四、線程的同步

4.1基本概念

雖然多線程能給我們帶來好處,但是也有不少問題需要解決。例如,對于像磁盤驅(qū)動器這樣獨占性系統(tǒng)資源,由于線程可以執(zhí)行進程的任何代碼段,且線程的運行是由系統(tǒng)調(diào)度自動完成的,具有一定的不確定性,因此就有可能出現(xiàn)兩個線程同時對磁盤驅(qū)動器進行操作,從而出現(xiàn)操作錯誤;又例如,對于銀行系統(tǒng)的計算機來說,可能使用一個線程來更新其用戶數(shù)據(jù)庫,而用另外一個線程來讀取數(shù)據(jù)庫以響應儲戶的需要,極有可能讀數(shù)據(jù)庫的線程讀取的是未完全更新的數(shù)據(jù)庫,因為可能在讀的時候只有一部分數(shù)據(jù)被更新過。

使隸屬于同一進程的各線程協(xié)調(diào)一致地工作稱為線程的同步。MFC提供了多種同步對象,下面只介紹最常用的四種:

臨界區(qū)(CCriticalSection)

事件(CEvent)

互斥量(CMutex)

信號量(CSemaphore)

通過這些類,可以比較容易地做到線程同步。

4.2使用 CCriticalSection 類

當多個線程訪問一個獨占性共享資源時,可以使用“臨界區(qū)”對象。任一時刻只有一個線程可以擁有臨界區(qū)對象,擁有臨界區(qū)的線程可以訪問被保護起來的資源或代碼段,其他希望進入臨界區(qū)的線程將被掛起等待,直到擁有臨界區(qū)的線程放棄臨界區(qū)時為止,這樣就保證了不會在同一時刻出現(xiàn)多個線程訪問共享資源。

CCriticalSection類的用法非常簡單,步驟如下:

1.定義CCriticalSection類的一個全局對象(以使各個線程均能訪問),如CCriticalSection critical_section;

2.在訪問需要保護的資源或代碼之前,調(diào)用CCriticalSection類的成員Lock()獲得臨界區(qū)對象: critical_section.Lock();3.在線程中調(diào)用該函數(shù)來使線程獲得它所請求的臨界區(qū)。如果此時沒有其它線程占有臨界區(qū)對象,則調(diào)用Lock()的線程獲得臨界區(qū);否則,線程將被掛起,并放入到一個系統(tǒng)隊列中等待,直到當前擁有臨界區(qū)的線程釋放了臨界區(qū)時為止。

4.訪問臨界區(qū)完畢后,使用CCriticalSection的成員函數(shù)Unlock()來釋放臨界區(qū):critical_section.Unlock();通俗講,就是線程A執(zhí)行到critical_section.Lock();語句時,如果其它線程(B)正在執(zhí)行critical_section.Lock();語句后且critical_section.Unlock();語句前的語句時,線程A就會等待,直到線程B執(zhí)行完critical_section.Unlock();語句,線程A才會繼續(xù)執(zhí)行。

例程8 MultiThread8 4.3使用 CEvent 類

CEvent 類提供了對事件的支持。事件是一個允許一個線程在某種情況發(fā)生時,喚醒另外一個線程的同步對象。例如在某些網(wǎng)絡應用程序中,一個線程(記為A)負責監(jiān)聽通訊端口,另外一個線程(記為B)負責更新用戶數(shù)據(jù)。通過使用CEvent 類,線程A可以通知線程B何時更新用戶數(shù)據(jù)。每一個CEvent 對象可以有兩種狀態(tài):有信號狀態(tài)和無信號狀態(tài)。線程監(jiān)視位于其中的CEvent 類對象的狀態(tài),并在相應的時候采取相應的操作。

在MFC中,CEvent 類對象有兩種類型:人工事件和自動事件。一個自動CEvent 對象在被至少一個線程釋放后會自動返回到無信號狀態(tài);而人工事件對象獲得信號后,釋放可利用線程,但直到調(diào)用成員函數(shù)ReSetEvent()才將其設置為無信號狀態(tài)。在創(chuàng)建CEvent 類的對象時,默認創(chuàng)建的是自動事件。CEvent 類的各成員函數(shù)的原型和參數(shù)說明如下:

1、CEvent(BOOL bInitiallyOwn=FALSE,BOOL bManualReset=FALSE,LPCTSTR lpszName=NULL,LPSECURITY_ATTRIBUTES lpsaAttribute=NULL);bInitiallyOwn:指定事件對象初始化狀態(tài),TRUE為有信號,F(xiàn)ALSE為無信號;

bManualReset:指定要創(chuàng)建的事件是屬于人工事件還是自動事件。TRUE為人工事件,F(xiàn)ALSE為自動事件;

后兩個參數(shù)一般設為NULL,在此不作過多說明。

2、BOOL CEvent::SetEvent();

將 CEvent 類對象的狀態(tài)設置為有信號狀態(tài)。如果事件是人工事件,則 CEvent 類對象保持為有信號狀態(tài),直到調(diào)用成員函數(shù)ResetEvent()將 其重新設為無信號狀態(tài)時為止。如果CEvent 類對象為自動事件,則在SetEvent()將事件設置為有信號狀態(tài)后,CEvent 類對象由系統(tǒng)自動重置為無信號狀態(tài)。

如果該函數(shù)執(zhí)行成功,則返回非零值,否則返回零。

3、BOOL CEvent::ResetEvent();

該函數(shù)將事件的狀態(tài)設置為無信號狀態(tài),并保持該狀態(tài)直至SetEvent()被調(diào)用時為止。由于自動事件是由系統(tǒng)自動重置,故自動事件不需要調(diào)用該函數(shù)。如果該函數(shù)執(zhí)行成功,返回非零值,否則返回零。一般通過調(diào)用WaitForSingleObject函數(shù)來監(jiān)視事件狀態(tài)。前面已經(jīng)介紹了該函數(shù)。由于語言描述的原因,CEvent 類的理解確實有些難度,只要通過下面例程,多看幾遍就可理解。例程9 MultiThread9 仔細分析這兩個線程函數(shù), 就會正確理解CEvent 類。線程WriteD執(zhí)行到 WaitForSingleObject(eventWriteD.m_hObject,INFINITE);處等待,直到事件eventWriteD為有信號該線程才往下執(zhí)行,因為eventWriteD對象是自動事件,則當WaitForSingleObject()返回時,系統(tǒng)自動把eventWriteD對象重置為無信號狀態(tài)。

4.4使用CMutex 類

互斥對象與臨界區(qū)對象很像.互斥對象與臨界區(qū)對象的不同在于:互斥對象可以在進程間使用,而臨界區(qū)對象只能在同一進程的各線程間使用。當然,互斥對象也可以用于同一進程的各個線程間,但是在這種情況下,使用臨界區(qū)會更節(jié)省系統(tǒng)資源,更有效率。

4.5使用CSemaphore 類

當需要一個計數(shù)器來限制可以使用某個線程的數(shù)目時,可以使用“信號量”對象。CSemaphore 類的對象保存了對當前訪問某一指定資源的線程的計數(shù)值,該計數(shù)值是當前還可以使用該資源的線程的數(shù)目。如果這個計數(shù)達到了零,則所有對這個CSemaphore 類對象所控制的資源的訪問嘗試都被放入到一個隊列中等待,直到超時或計數(shù)值不為零時為止。一個線程被釋放已訪問了被保護的資源時,計數(shù)值減1;一個線程完成了對被控共享資源的訪問時,計數(shù)值增1。這個被CSemaphore 類對象所控制的資源可以同時接受訪問的最大線程數(shù)在該對象的構(gòu)建函數(shù)中指定。

CSemaphore 類的構(gòu)造函數(shù)原型及參數(shù)說明如下:

CSemaphore(LONG lInitialCount=1,LONG lMaxCount=1,LPCTSTR pstrName=NULL,LPSECURITY_ATTRIBUTES lpsaAttributes=NULL);lInitialCount:信號量對象的初始計數(shù)值,即可訪問線程數(shù)目的初始值;

lMaxCount:信號量對象計數(shù)值的最大值,該參數(shù)決定了同一時刻可訪問由信號量保護的資源的線程最大數(shù)目;

后兩個參數(shù)在同一進程中使用一般為NULL,不作過多討論;

在用CSemaphore 類的構(gòu)造函數(shù)創(chuàng)建信號量對象時要同時指出允許的最大資源計數(shù)和當前可用資源計數(shù)。一般是將當前可用資源計數(shù)設置為最大資源計數(shù),每增加一個線程對共享資源的訪問,當前可用資源計數(shù)就會減1,只要當前可用資源計數(shù)是大于0的,就可以發(fā)出信號量信號。但是當前可用計數(shù)減小到0時,則說明當前占用資源的線程數(shù)已經(jīng)達到了所允許的最大數(shù)目,不能再允許其它線程的進入,此時的信號量信號將無法發(fā)出。線程在處理完共享資源后,應在離開的同時通過ReleaseSemaphore()函數(shù)將當前可用資源數(shù)加1。例程10 MultiThread10 為了文件中能夠正確使用同步類,在文件開頭添加: #include “afxmt.h” 定義信號量對象和一個字符數(shù)組,為了能夠在不同線程間使用,定義為全局變量:CSemaphore semaphoreWrite(2,2);//資源最多訪問線程2個,當前可訪問線程數(shù)2個

在信號量對象有信號的狀態(tài)下,線程執(zhí)行到WaitForSingleObject語句處繼續(xù)執(zhí)行,同時可用線程數(shù)減1;若線程執(zhí)行到WaitForSingleObject語句時信號量對象無信號,線程就在這里等待,直到信號量對象有信號線程才往下執(zhí)行。

第五篇:C++編程知識總結(jié)

1.數(shù)組

1.1數(shù)組定義時的注意點

1在C++中不提供可變化大小的數(shù)組,○即數(shù)組定義中的常量表達式不能包含變量。(來源:C++書6.1.1)

int n;cin>>n;float t[n];上例在定義數(shù)組t時,變量n沒有確定的值,即在程序執(zhí)行之前,無法知道數(shù)組t的元素個數(shù),所以這種聲明不被允許。但是可以用new動態(tài)分配,如: int n;cin>>n;float *t;t=new float[n];

2在定義數(shù)組時,可以不直接指定數(shù)組的大小,由C++編譯器根據(jù)初值表中元素的個數(shù)來自○動確定數(shù)組元素的個數(shù)。例如: int z[]={0,1,2,3,4,5,6,7,8} 3C++語言規(guī)定只能對數(shù)組中的元素進行賦值或引用,不能把整個數(shù)組作為一個整體進行賦○值或引用。(2.3是一個實例)(來源:C++書4同類型的數(shù)組之間不能相互賦值 ○如int a[5],b[5];a=b;//錯誤

strcpy(b,a);//正確

6.1.1)

1.2數(shù)組和指針的關系(來源:C++書8.2節(jié)8.2.1)

char s[5];在C++中說明了一個數(shù)組后,數(shù)組名可以作為一個指針來使用,因此s可作為一個指針使用(但它不同于指針,不能賦值運算、算術運算等)。

2.字符數(shù)組

2.1輸入字符數(shù)據(jù) char c;cin>>c;// cin不能將輸入的空格賦給字符型變量。

cin.get();//可獲得鍵盤上輸入的每一個字符,包括空格和回車鍵。

2.2字符數(shù)組的輸入/輸出(來源:C++書6.2.4)2.2.1逐個字符輸入 char c[10];for(int i=0;i<10;i++)cin>>c[i];2.2.2字符串輸入 方法1 char c[10];cin>>c;//即在輸入輸出時只給數(shù)組名

此法在輸入字符串時,遇到空格和回車就認為一個字符結(jié)束。方法2 cin.getline(字符數(shù)組名,允許輸入的最大字符個數(shù))此法可把輸入的一行作為一個字符串送到字符數(shù)組中。

2.3字符數(shù)組和字符指針的初始化 2.3.1字符數(shù)組初始化 char tx[5]=“";2.3.2字符指針初始化 char *ptx=new char[5];ptx[0]='

主站蜘蛛池模板: 国产成人亚洲精品另类动态| 免费一区二区三区成人免费视频| 国产精品国产av片国产| 性色av一区二区三区v视界影院| 中文字幕无码乱人伦免费| 亚洲综合色aaa成人无码| 精品无人国产偷自产在线| 亚洲毛片无码不卡av在线播放| 亚洲欧洲av一区二区久久| 无码国产精成人午夜视频| 国产女人喷潮视频在线观看| 一本无码久本草在线中文字幕dvd| 综合激情丁香久久狠狠| 国产成人无码区免费内射一片色欲| 不满足出轨的人妻中文字幕| 日本熟妇大乳| 99久久伊人精品综合观看| 亚洲变态另类天堂av手机版| 亚洲精品偷拍无码不卡av| 国产精品欧美久久久久无广告| 伊人久久精品无码二区麻豆| 精品久久久久久成人av| 中文区中文字幕免费看| 撕开奶罩揉吮奶头视频| 亚洲欧美日韩精品久久| 久久精品免费观看国产| 国产日韩亚洲大尺度高清| 乱人伦人妻精品一区二区| 无码精品视频| 亚洲AV无码成人精品区国产| 蜜桃视频在线观看免费视频网站www| 18禁无遮挡免费视频网站| 国产二级一片内射视频播放| 少妇激情av一区二区| 国产精品成年片在线观看| 国产精品青青在线观看爽香蕉| 无码色偷偷亚洲国内自拍| av老司机亚洲精品天堂| 韩国三级中文字幕hd久久精品| 西西午夜无码大胆啪啪国模| 亚洲自偷自偷在线制服|