91欧美超碰AV自拍|国产成年人性爱视频免费看|亚洲 日韩 欧美一厂二区入|人人看人人爽人人操aV|丝袜美腿视频一区二区在线看|人人操人人爽人人爱|婷婷五月天超碰|97色色欧美亚州A√|另类A√无码精品一级av|欧美特级日韩特级

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認識你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

if判斷和內(nèi)存管理相關(guān)的講解

大魚機器人 ? 來源:大魚機器人 ? 2023-01-30 15:11 ? 次閱讀
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

01

劍宗氣宗之爭 《笑傲江湖》中華山派的劍宗和氣宗之爭,可謂異常激烈。那么問題就來了,既然有劍宗氣宗之爭,到底應該先練劍,還是先練氣呢?引申到軟件開發(fā)行業(yè)有沒劍氣之爭呢? 前面發(fā)布很多理論方面的文章,高質(zhì)量的軟件開發(fā),也是存在見效快的套路,針對有一定嵌入式C語言開發(fā)基礎(chǔ)的,以劍宗之法進行描述,可重點關(guān)注if判斷和內(nèi)存管理相關(guān)的講解,拋磚引玉。

02

文件結(jié)構(gòu) 1、C 程序通常分為兩類文件,一種是程序的聲明稱為頭文件,以“.h”為后綴,另一種是程序的實現(xiàn),以“.c”為后綴,一般每個c文件有個同名的h文件。 2、軟件的頭文件數(shù)目比較多,應將頭文件和定義文件分別保存于不同的目錄,例如將頭文件保存于 include或者inc 目錄,將定義文件保存于 source 或src目錄;如果某些頭文件是私有的,它不會被用戶的程序直接引用,則沒有必要公開其“聲明”。為了加強信息隱藏,這些私有的頭文件可以和定義文件存放于同一個目錄,即私有的h文件放在src目錄。 3、在文件頭添加版權(quán)和版本的聲明等信息,主要包括版權(quán)和功能,以及修改記錄,必要時可以為整個功能文件夾單獨新建readme說明文檔。 4、為了防止頭文件被重復引用,必須用 ifndef/define/endif 結(jié)構(gòu)產(chǎn)生預處理塊。 5、頭文件中只存放“聲明”而不存放“定義”,更別提放變量,這是嚴重的錯誤。 6、用 #include 格式來引用標準庫的頭文件,用 #include “filename.h” 格式來引用非標準庫的頭文件(編譯器將從用戶的工作目錄開始搜索)。 7、文件可按層或者功能組件劃分不同的文件夾,便于其他人閱讀。

03

程序版式 版式雖然不會影響程序的功能,但會影響可讀性,程序的風格統(tǒng)一則是賞心悅目。 代碼排版在編碼時確實很難把握,但可以編碼完成后統(tǒng)一用工具格式化,不管編碼使用Keil/MDK、Qt等集成工具,或者純粹的代碼編輯工具Source Insight,一般都支持自定義運行可執(zhí)行文件,如Astyle??梢钥椭苹虏藛?,一鍵執(zhí)行Astyle,將代碼一鍵格式化,排版統(tǒng)一、層次分明。 Astyle官網(wǎng) http://astyle.sourceforge.net/ 按要求下載安裝,只需要AStyle.exe即可。關(guān)于其使用和參數(shù),可以再進入Documentation。對代碼基本風格,{}如何對齊、是否換行,switch-case如何排版,tab鍵占位寬度,運算符或變量前后的空格等等,基本上代碼排版涉及的方方面面都有參數(shù)說明。個人選擇的編碼參數(shù)是

		
			--style=allman-S-U-t-n-K-p-s4-j-q-Y-xW-xVfileName
			效果如下

		//微信公眾號:嵌入式系統(tǒng) intFoo(boolisBar) { if(isBar) { bar(); return1; } else { return0; } }
			關(guān)于注釋,重要函數(shù)或段落必不可少,修改代碼同時修改相應的注釋,以保證注釋與代碼的一致性。

04

命名規(guī)則 比較著名的命名規(guī)則當推 Microsoft 公司的“匈牙利”法,該命名規(guī)則的主要思想是“在變量和函數(shù)名中加入前綴以增進人們對程序的理解”。例如所有的字符變量均以ch 為前綴,若是指針變量則追加前綴 p。但沒有一種命名規(guī)則可以讓所有的程序員滿意,制定一種令大多數(shù)項目成員滿意的命名規(guī)則,重點是在整個團隊和項目中貫徹實施。 事實上開發(fā)大多數(shù)基于SDK,一般底層命名規(guī)則盡量與SDK風格保持一致,至于上層就按團隊標準,個人比較傾向全部小寫字母,用下劃線分割的風格,例如 set_apn、timer_start。 不要出現(xiàn)標識符完全相同的局部變量和全局變量,盡管兩者的作用域不同而不會發(fā)生語法錯誤,但會使人誤解,全局變量也不要過于簡短。 變量的名字應當使用“名詞”或者“形容詞+名詞”,函數(shù)的名字應當使用“動詞”或者“動詞+名詞”,用正確的反義詞組命名具有互斥意義的變量或相反動作的函數(shù)等。

05

基本語句 表達式和語句都屬于C 語法基礎(chǔ),看似簡單,但使用時隱患比較多,提供一些建議。

5.1 if

if 語句是 C 語言中最簡單、最常用的語句,然而很多程序員卻用隱含錯誤的方式,僅以不同類型的變量與零值比較為例,展開討論。5.1.1 布爾變量與零值比較 不可將布爾變量直接與 TRUE、FALSE 或者 1、0 進行比較。根據(jù)布爾類型的語義,零值為“假”(記為 FALSE),任何非零值都是“真”(記為TRUE)。TRUE 的值究竟是什么并沒有統(tǒng)一的標準。 假設(shè)布爾變量名字為 flag,它與零值比較的標準 if 語句如下:

		//微信公眾號:嵌入式系統(tǒng) if(flag)//表示flag為真 if(!flag)//表示flag為假
			其它的用法都屬于不良風格,例如:

		//錯誤范例 if(flag==TRUE) if(flag==1) if(flag==FALSE) if(flag==0)2、整型變量與零值比較
			整型變量用“==”或“!=”直接與 0 比較,假設(shè)整型變量的名字為 value,它與零值比較的標準 if 語句如下:

		if(value==0) if(value!=0)
			不可模仿布爾變量的風格而寫成

		//錯誤范例 if(value)//會讓人誤解value是布爾變量 if(!value)3、 浮點變量與零值比較
			不可將浮點變量用“==”或“!=”與任何數(shù)字比較,無論是 float 還是 double 類型的變量,都有精度限制。不能將浮點變量用“==”或“!=”與數(shù)字比較,應該設(shè)法轉(zhuǎn)化成“>=”或“<=”形式。假設(shè)浮點變量的名字為 x,應當將

		if(x==0.0)//隱含錯誤的比較,錯誤
			轉(zhuǎn)化為

		constfloatEPSINON=0.00001 if((x>=-EPSINON)&&(x<=EPSINON))? //其中EPSINON是允許的誤差(即精度),即x無限趨近于0.04、指針變量與零值比較
			指針變量用“==”或“!=”與 NULL 比較, 指針變量的零值是“空”(記為 NULL),盡管 NULL 的值與 0 相同,但是兩者意義不同。假設(shè)指針變量的名字為 p,它與零值比較的標準 if 語句如下:

		
			if(p==NULL)//p與NULL顯式比較,強調(diào)p是指針變量 if(p!=NULL)
			不要寫成

		if(p==0)//容易讓人誤解p是整型變量 if(p!=0) if(p)//容易讓人誤解p是布爾變量 if(!p)

5.2 for

在多重循環(huán)中,如果有可能,應當將最長的循環(huán)放在最內(nèi)層,最短的循環(huán)放在最外層,以減少 CPU 切換循環(huán)層的次數(shù)。

		//不良范例 for(row=0;row<100;row++) { for(col=0;col<5;col++) { sum=sum+a[row][col]; } } //微信公眾號:嵌入式系統(tǒng) 較高效率 for(col=0;col<5;col++) { for(row=0;row<100;row++) { sum=sum+a[row][col]; } }

5.3 switch

switch 是多分支選擇語句,而 if 語句只有兩個分支可供選擇;雖然可以用嵌套的if 語句來實現(xiàn)多分支選擇,但那樣的程序冗長難讀。這是 switch 語句存在的理由。 switch-case 即使不需要 default 處理,也應該保留語句 default : break; 這樣做并非多此一舉,而是為了防止別人誤以為你忘了 default 處理。確實不需要break的case,務必加上注釋標明。

5.4 goto

很多人建議禁止使用 goto 語句,但實事求是地說,錯誤是程序員自己造成的,不是 goto 的過錯。goto 語句至少有一處可顯神通,它能從多重循環(huán)體中一下子跳到外面,特殊場景下可以使用,在很多if嵌套的場景,比如都有同樣的錯誤處理,或者成對操作的文件開關(guān),或者內(nèi)存申請釋放,就比較適合goto統(tǒng)一處理。

		//微信公眾號:嵌入式系統(tǒng) //代碼只是表意,可能無法編譯 #include voidtest(void) { char*p1,*p2; p1=(char*)malloc(100); p1=(char*)malloc(200); if(0) { //dosomething gotoexit; } elseif(0) { //dosomething gotoexit; } //dosomething //... exit: free(p1); free(p2); } intmain() { goto_test(); return0; }
			對于內(nèi)存申請釋放、文件打開關(guān)閉這種成對操作,或者各種異常處理的統(tǒng)一支持場景,就比較適合goto。類似的還有do-while(0)這種語句。
			關(guān)于運算優(yōu)先級,熟記運算符優(yōu)先級是比較困難的,如果代碼行中的運算符比較多,為了防止產(chǎn)生歧義并提高可讀性,全部加括號明確表達式的操作順序,雖然愚笨但是可靠

06

常量 常量是一種標識符,它的值在運行期間恒定不變。C 語言用 #define 來定義常量(稱為宏常量),但用 const 來定義常量(稱為 const 常量)其實更佳。

		#defineMAX100 constfloatPI=3.14159;
			const 常量有數(shù)據(jù)類型,而宏常量沒有數(shù)據(jù)類型。編譯器可以對前者進行類型安全檢查,而對后者只進行字符替換,沒有類型安全檢查,并且在字符替換可能會產(chǎn)生意料不到的錯誤,所以復雜參數(shù)宏必須為每個參數(shù)加上()限制。
			但也有特例

		
			constintSIZE=100; intarray[SIZE];//有的編譯器認為是錯誤,這就必須用define了
			需要對外公開的常量放在頭文件中,不需要對外公開的常量放在定義文件的頭部。為便于管理,可以把不同模塊的常量集中存放在一個公共的頭文件中。

07

函數(shù) 函數(shù)設(shè)計的細微缺點很容易導致該函數(shù)被錯用,函數(shù)接口的兩個要素是參數(shù)和返回值,C 語言中函數(shù)的參數(shù)和返回值的傳遞方式有值傳遞(pass by value)和指針傳遞(pass by pointer)兩種。

7.1參數(shù)的規(guī)則

參數(shù)的書寫要完整,不要貪圖省事只寫參數(shù)的類型而省略參數(shù)名字,如果函數(shù)沒有參數(shù),則用 void 填充。

		voidset_size(intwidth,intheight);//良好的風格 voidset_size(int,int);//不良的風格 intget_size(void);//良好的風格 intget_size();//不良的風格
			參數(shù)命名要恰當,順序要合理。例如字符串拷貝函數(shù)

		char*strcpy(char*dest,constchar*src);
			從名字上就可以看出應該把 src 拷貝到 dest。還有一個問題,兩個參數(shù)哪個該在前哪個該在后?參數(shù)的順序要遵循程序員的習慣。一般地,應將目的參數(shù)放在前面,源參數(shù)放在后面。
			這里也說明下const的意義,如果參數(shù)僅作輸入用,則應在類型前加 const,以防止在函數(shù)體內(nèi)被意外修改。
			避免函數(shù)有太多的參數(shù),參數(shù)個數(shù)盡量控制在 5 個以內(nèi),如果參數(shù)太多,在使用時容易將參數(shù)類型或順序搞錯,可以定為結(jié)構(gòu)體指針,但盡量帶上參數(shù)注釋。
			除了printf、sprintf標準庫或基于這類的日志輸出接口,盡量不要使用類型和數(shù)目不確定的參數(shù)。

7.2 返回值的規(guī)則

不要省略返回值的類型,默認不加類型說明的函數(shù)一律自動按整型處理。為了避免混亂,如果函數(shù)沒有返回值,應聲明為 void 類型。 不要將正常值和錯誤標志混在一起返回。正常值用輸出參數(shù)獲得,而錯誤標志用 return 語句返回。

7.3 函數(shù)內(nèi)部實現(xiàn)的規(guī)則

不同功能的函數(shù)其內(nèi)部實現(xiàn)各不相同,看起來似乎無法就“內(nèi)部實現(xiàn)”達成一致的觀點。但根據(jù)經(jīng)驗,我們可以在函數(shù)體的“入口處”和“出口處”從嚴把關(guān),從而提高函數(shù)的質(zhì)量。 在函數(shù)體的“入口處”,對參數(shù)的有效性進行檢查,很多程序錯誤是由非法參數(shù)引起的,我們應該充分理解并正確使用“斷言”(assert)來防止此類錯誤。 在函數(shù)體的“出口處”,對 return 語句的正確性和效率進行檢查。如果函數(shù)有返回值,那么函數(shù)的“出口處”是 return 語句。調(diào)用處應該盡量關(guān)注返回值,對異常進行處理 關(guān)于return的值,不可返回指向“棧內(nèi)存”的“指針,該內(nèi)存在函數(shù)體結(jié)束時被自動銷毀。例如

		
			char*Func(void) { charstr[]=“helloworld”;//str的內(nèi)存位于棧上returnstr;//將導致錯誤 }
			盡量避免函數(shù)帶有“記憶”功能,相同的輸入應當產(chǎn)生相同的輸出。帶有“記憶”功能的函數(shù),其行為可能是不可預測的,因為它的行為可能取決于某種“記憶狀態(tài)”。這樣的函數(shù)既不易理解又不利于測試和維護。在 C語言中,函數(shù)的 static 局部變量是函數(shù)的“記憶”存儲器。建議盡量少用 static 局部變量,除非必需。

7.4 斷言

程序一般分為 Debug 版本和 Release 版本,Debug 版本用于內(nèi)部調(diào)試,Release 版本發(fā)行給用戶使用。斷言 assert 是僅在 Debug 版本起作用的宏,它用于檢查“不應該”發(fā)生的情況。在運行過程中,如果 assert 的參數(shù)為假,那么程序就會中止。

		void*memcpy(void*pvTo,constvoid*pvFrom,size_tsize) { assert((pvTo!=NULL)&&(pvFrom!=NULL));//【使用斷言】 byte*pbTo=(byte*)pvTo;//防止改變pvTo的地址 byte*pbFrom=(byte*)pvFrom;//防止改變pvFrom的地址 while(size-->0) *pbTo++=*pbFrom++; returnpvTo; }
			assert 不應該產(chǎn)生任何副作用。所以 assert 不是函數(shù),而是宏??梢园補ssert 看成一個在任何系統(tǒng)狀態(tài)下都可以安全使用的無害測試手段。如果程序在 assert處終止了,并不是說含有該 assert 的函數(shù)有錯誤,而是調(diào)用者出了差錯,assert 有助于找到發(fā)生錯誤的原因。
			軟件有必要進行防錯設(shè)計,如果“不可能發(fā)生”的事情的確發(fā)生了,則要使用斷言進行報警。

8 內(nèi)存管理

C語言的內(nèi)存管理既是它的優(yōu)勢,也是劣勢。理解它的原理了才能更好的管理內(nèi)存。

8.1 內(nèi)存分配方式

內(nèi)存分配方式有三種: 1、從靜態(tài)存儲區(qū)域分配。內(nèi)存在程序編譯的時候就已經(jīng)分配好,這塊內(nèi)存在程序的整個運行期間都存在。例如全局變量,static 變量。 2、在棧上創(chuàng)建。在執(zhí)行函數(shù)時,函數(shù)內(nèi)局部變量的存儲單元都可以在棧上創(chuàng)建,函數(shù)執(zhí)行結(jié)束時這些存儲單元自動被釋放。棧內(nèi)存分配運算內(nèi)置于處理器的指令集中,效率很高,但是分配的內(nèi)存容量有限。 3、從堆上分配,亦稱動態(tài)內(nèi)存分配。程序在運行的時候用 malloc 或 new 申請任意多少的內(nèi)存,程序員自己負責在何時用 free 或 delete 釋放內(nèi)存。動態(tài)內(nèi)存的生存期由我們決定,使用非常靈活,但風險也大。

8.2 內(nèi)存錯誤及其對策

發(fā)生內(nèi)存錯誤是件非常麻煩的事情。編譯器不能自動發(fā)現(xiàn)這些錯誤,通常是在程序運行時才能捕捉到,而這些錯誤大多沒有明顯的癥狀,時隱時現(xiàn),增加了改錯的難度。常見的內(nèi)存錯誤及其對策如下:1、內(nèi)存分配未成功,卻使用了它 編程新手常犯這種錯誤,因為他們沒有意識到內(nèi)存分配會不成功。常用解決辦法是,在使用內(nèi)存之前檢查指針是否為 NULL。如果指針 p 是函數(shù)的參數(shù),可在函數(shù)的入口處用 assert(p!=NULL)進行檢查,或者用 if(p==NULL)或 if(p!=NULL)進行防錯處理。2、內(nèi)存分配雖然成功,但是尚未初始化就引用它 犯這種錯誤主要有兩個起因:一是沒有初始化的觀念;二是誤以為內(nèi)存的缺省初值全為零,導致引用初值錯誤。內(nèi)存的缺省初值究竟是什么并沒有統(tǒng)一的標準(盡管有些時候為零值),為了安全,對分配的內(nèi)存都進行清零。3、內(nèi)存分配成功并且已經(jīng)初始化,但操作越過了內(nèi)存的邊界 數(shù)組使用時經(jīng)常會發(fā)生下標“多 1”或“少 1”的操作。特別是在 for 循環(huán)語句中,循環(huán)次數(shù)很容易搞錯,導致數(shù)組操作越界。4、忘記釋放內(nèi)存,造成內(nèi)存泄露 含有這種錯誤的函數(shù)每被調(diào)用一次就丟失一塊內(nèi)存。剛開始時系統(tǒng)的內(nèi)存充足,運行正常,但隨著運行時間加長,程序突然死掉,內(nèi)存耗盡。動態(tài)內(nèi)存的申請與釋放必須配對,程序中 malloc 與 free 的成對使用。5、已經(jīng)釋放的內(nèi)存卻繼續(xù)使用它 程序中的調(diào)用關(guān)系過于復雜,邏輯順序錯誤,或者使用了指向“棧內(nèi)存”的“臨時指針,使用 free 或 delete 釋放了內(nèi)存后,務必將指針設(shè)置為 NULL,使用前判斷是否為NULL。 關(guān)于指針的使用建議,用 malloc 申請內(nèi)存之后,應該立即檢查指針值是否為 NULL,非NULL的賦初值;使用結(jié)束后用 free 釋放,且將指針設(shè)置為 NULL,防止誤用“野指針”。

8.3 指針與數(shù)組的對比

C 程序中指針和數(shù)組在不少地方可以相互替換著用,讓人產(chǎn)生一種錯覺,以為兩者是等價的。 數(shù)組要么在靜態(tài)存儲區(qū)被創(chuàng)建(如全局數(shù)組),要么在棧上被創(chuàng)建。數(shù)組名對應著(而不是指向)一塊內(nèi)存,其地址與容量在生命期內(nèi)保持不變,只有數(shù)組的內(nèi)容可以改變。 指針可以隨時指向任意類型的內(nèi)存塊,它的特征是“可變”,所以我們常用指針來操作動態(tài)內(nèi)存。指針遠比數(shù)組靈活,但也更危險。 下面以字符串為例比較指針與數(shù)組的特性。1、修改內(nèi)容 字符數(shù)組 a 的容量是 6 個字符,其內(nèi)容為 hello。a 的內(nèi)容可以改變,如 a[0]= ‘X’。指針 p 指向常量字符串“world”(位于靜態(tài)存儲區(qū),內(nèi)容為 world),常量字符串的內(nèi)容是不可以被修改的。從語法上看,編譯器并不覺得語句 p[0]= ‘X’有什么不妥,但是該語句企圖修改常量字符串的內(nèi)容而導致運行錯誤。

		chara[]=“hello”; a[0]=‘X’; cout<endl; char*p=“world”;//注意p指向常量字符串 p[0]=‘X’;//編譯器不能發(fā)現(xiàn)該錯誤 cout<endl;2、 內(nèi)容復制與比較
			不能對數(shù)組名進行直接復制與比較,若想把數(shù)組 a 的內(nèi)容復制給數(shù)組 b,不能用語句 b = a ,否則將產(chǎn)生編譯錯誤。應該用標準庫函數(shù) strcpy 進行復制。同理,比較 b 和 a 的內(nèi)容是否相同,不能用 if(b == a) 來判斷,應該用標準庫函數(shù) strcmp進行比較。
			語句 p = a 并不能把 a 的內(nèi)容復制指針 p,而是把 a 的地址賦給了 p。要想復制 a的內(nèi)容,可以先用庫函數(shù) malloc 為 p 申請一塊容量為 strlen(a)+1 個字符的內(nèi)存,再用 strcpy 進行字符串復制。同理,語句 if(p==a) 比較的不是內(nèi)容而是地址,應該用庫函數(shù) strcmp 來比較。

		
			//數(shù)組 chara[]="hello"; charb[10]; strcpy(b,a);//不能用b=a; if(strcmp(b,a)==0)//不能用if(b==a) //指針 intlen=strlen(a); char*p=(char*)malloc(sizeof(char)*(len+1)); strcpy(p,a);//不要用p=a; if(strcmp(p,a)==0)//不要用if(p==a)3、計算內(nèi)存容量
			用運算符 sizeof 可以計算出數(shù)組的容量(字節(jié)數(shù))。sizeof(a)的值是 12(注意別忘了’’)。指針 p 指向 a,但是 sizeof(p)的值卻是 4。這是因為sizeof(p)得到的是一個指針變量的字節(jié)數(shù),相當于 sizeof(char*),而不是 p 所指的內(nèi)存容量。/C 語言沒有辦法知道指針所指的內(nèi)存容量,只能在申請內(nèi)存時記住它。

		
			chara[]="helloworld"; char*p=a; cout<sizeof(a)<endl;//12字節(jié) cout<sizeof(p)<endl;//4字節(jié)
			當數(shù)組作為函數(shù)的參數(shù)進行傳遞時,該數(shù)組自動退化為同類型的指針。不論數(shù)組 a 的容量是多少,sizeof(a)始終等于 sizeof(char *)。

		
			voidFunc(chara[100]) { cout<sizeof(a)<endl;//4字節(jié)而不是100字節(jié) }4、指針參數(shù)是如何傳遞內(nèi)存
			如果函數(shù)的參數(shù)是一個指針,不要指望用該指針去申請動態(tài)內(nèi)存。

		voidget_memory(char*p,intnum) { p=(char*)malloc(sizeof(char)*num); } voidtest(void) { char*str=NULL; get_memory(str,100);//str仍然為NULL strcpy(str,"hello");//運行錯誤 }
			test 函數(shù)的get_memory(str, 100) 并沒有使 str 獲得期望的內(nèi)存,str 依舊是 NULL,為什么?
			問題出在函數(shù) get_memory,編譯器總是要為函數(shù)的每個參數(shù)制作臨時副本,指針參數(shù) p 的副本是 _p,編譯器使 _p = p。如果函數(shù)體內(nèi)的程序修改了_p 的內(nèi)容,就導致參數(shù) p 的內(nèi)容作相應的修改。這就是指針可以用作輸出參數(shù)的原因。而范例中_p 申請了新的內(nèi)存,只是把_p 所指的內(nèi)存地址改變了,但是 p 絲毫未變。所以函數(shù) get_memory并不能輸出任何東西。事實上,每執(zhí)行一次 get_memory就會泄露一塊內(nèi)存,因為沒有用free 釋放內(nèi)存。
			如果非得要用指針參數(shù)去申請內(nèi)存,那么應該改用“指向指針的指針”,正確范例如下:

		voidget_memory2(char**p,intnum) { *p=(char*)malloc(sizeof(char)*num); } voidtest2(void) { char*str=NULL; get_memory2(&str,100);//注意參數(shù)是&str,而不是str strcpy(str,"hello"); free(str); }
			由于“指向指針的指針”這個概念不容易理解,可以用函數(shù)返回值來傳遞動態(tài)內(nèi)存,這種方法更加簡單。

		char*get_memory3(intnum) { char*p=(char*)malloc(sizeof(char)*num); returnp; } voidtest3(void) { char*str=NULL; str=get_memory3(100); //建議增加str指針是否為NULL判斷,并清零內(nèi)容 strcpy(str,"hello"); free(str); }
			用函數(shù)返回值來傳遞動態(tài)內(nèi)存這種方法雖然好用,但是常常有人把 return 語句用錯,不要用 return 語句返回指向“棧內(nèi)存”的指針,因為該內(nèi)存在函數(shù)結(jié)束時自動消亡,錯誤范例如下:

		//錯誤范例 char*get_string(void) { charp[]="helloworld"; returnp;//編譯器將提出警告 } voidtest4(void) { char*str=NULL; str=get_string();//str的內(nèi)容是隨機垃圾 }
			執(zhí)行str = get_string()后 str 不再是 NULL 指針,但是 str 的內(nèi)容不是“hello world”而是垃圾。

		char*get_string2(void) { char*p="helloworld"; returnp; } voidtest5(void) { char*str=NULL; str=get_string2(); }
			函數(shù) test5 運行雖然不會出錯,但是函數(shù) get_string2的設(shè)計概念卻是錯誤的。因為 get_string2內(nèi)的“hello world”是常量字符串,位于靜態(tài)存儲區(qū),它在程序生命期內(nèi)恒定不變。無論什么時候調(diào)用 get_string2,它返回的始終是同一個“只讀”的內(nèi)存塊,也就是test5是無法修改str的。5、 free 把指針怎么了
			free 只是把指針所指的內(nèi)存給釋放掉,但并沒有把指針本身干掉;指針 p 被 free 以后其地址仍然不變(非 NULL),只是該地址對應的內(nèi)存是垃圾,p 成了“野指針”。如果此時不把 p 設(shè)置為 NULL,會讓人誤以為 p 是個合法的指針。
			如果程序比較長,我們有時記不住 p 所指的內(nèi)存是否已經(jīng)被釋放,在繼續(xù)使用 p 之前,通常會用語句 if (p != NULL)進行防錯處理。很遺憾,此時 if 語句起不到防錯作用,此時 p 不是 NULL 指針,但它也不指向合法的內(nèi)存塊。

		char*p=(char*)malloc(100); strcpy(p,“hello”); free(p);//p所指的內(nèi)存被釋放,但是p所指的地址仍然不變 if(p!=NULL)//沒有起到防錯作用 { strcpy(p,“world”);//出錯 }6、動態(tài)內(nèi)存會被自動釋放嗎
			函數(shù)體內(nèi)的局部變量在函數(shù)結(jié)束時自動消亡。

		voidfunc(void) { char*p=(char*)malloc(100);//動態(tài)內(nèi)存會自動釋放嗎? }
			但是,變量p 是局部的指針變量,它消亡的時候并不會讓它所指的動態(tài)內(nèi)存一起完蛋。發(fā)現(xiàn)指針有一些“似是而非”的特征:
			(1)指針消亡了,并不表示它所指的內(nèi)存會被自動釋放。
			(2)內(nèi)存被釋放了,并不表示指針會消亡或者成了 NULL 指針。7、杜絕“野指針”
			“野指針”不是 NULL 指針,是指向“垃圾”內(nèi)存的指針。人們一般不會錯用 NULL指針,因為用 if 語句很容易判斷;但是“野指針”是很危險的,if 語句對它不起作用。“野指針”的成因主要有三種:
			(1)指針變量沒有被初始化。任何指針變量剛被創(chuàng)建時不會自動成為 NULL 指針,它的缺省值是隨機的,所以,指針變量在創(chuàng)建的同時應當被初始化。
			(2)指針 p 被 free 或者 delete 之后,沒有置為 NULL,讓人誤以為 p 是個合法的指針。
			(3)指針操作超越了變量的作用范圍。這種情況讓人防不勝防。8、內(nèi)存耗盡怎么辦
			如果在申請動態(tài)內(nèi)存時找不到足夠大的內(nèi)存塊,malloc 將返回 NULL 指針,宣告內(nèi)存申請失敗。判斷指針是否為 NULL,如果是則馬上用 return 語句終止本函數(shù),或者用 exit(1)終止整個程序的運行。如果發(fā)生“內(nèi)存耗盡”,一般說來應用程序已經(jīng)無藥可救,嵌入式設(shè)備只能重啟了。9、心得體會
			很少有人能拍拍胸脯說通曉指針與內(nèi)存管理,越是怕指針,就越要使用指針。不會正確使用指針,肯定算不上是合格的嵌入式程序員。
			審核編輯 :李倩


聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學習之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報投訴
  • 內(nèi)存
    +關(guān)注

    關(guān)注

    9

    文章

    3212

    瀏覽量

    76389
  • C語言
    +關(guān)注

    關(guān)注

    183

    文章

    7644

    瀏覽量

    145665
  • 函數(shù)
    +關(guān)注

    關(guān)注

    3

    文章

    4417

    瀏覽量

    67566

原文標題:8 內(nèi)存管理

文章出處:【微信號:All_best_xiaolong,微信公眾號:大魚機器人】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。

收藏 人收藏
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

    評論

    相關(guān)推薦
    熱點推薦

    MangoTree Halo Ultra「全新PXI」,標配自動糾錯內(nèi)存#

    內(nèi)存
    芒果樹數(shù)字
    發(fā)布于 :2026年03月06日 15:59:34

    Redis內(nèi)存管理、持久化策略與慢查詢排查分析

    Redis 在生產(chǎn)環(huán)境中承擔著緩存、會話存儲、消息隊列、分布式鎖等多種角色。隨著數(shù)據(jù)量增長和并發(fā)壓力上升,內(nèi)存碎片、持久化 I/O 抖動、慢查詢堆積這三類問題會逐漸顯現(xiàn),直接影響服務延遲和穩(wěn)定性。Redis 8.x 在內(nèi)存管理
    的頭像 發(fā)表于 02-27 11:00 ?159次閱讀

    探秘DS2731:緩存內(nèi)存電池備份管理IC的卓越性能與應用

    探秘DS2731:緩存內(nèi)存電池備份管理IC的卓越性能與應用 在電子設(shè)備的設(shè)計中,電源管理是一個至關(guān)重要的環(huán)節(jié),尤其是對于需要可靠備份電源的應用場景。今天,我們就來深入探討一款功能強大的緩存內(nèi)存
    的頭像 發(fā)表于 02-24 16:40 ?325次閱讀

    RDMA設(shè)計37:RoCE v2 子系統(tǒng)模型設(shè)計

    發(fā)送數(shù)據(jù)包時,將數(shù)據(jù)包存放到輸出緩沖中,輸出緩沖內(nèi)的數(shù)據(jù)包將按照存放順序依次發(fā)出。 虛擬內(nèi)存管理器:用于模擬遠程主機的內(nèi)存。由于 RoCE v2 協(xié)議是直接操作遠程主機內(nèi)存的協(xié)議,為了
    發(fā)表于 02-06 16:19

    判斷前移到現(xiàn)場,AI 如何重構(gòu)大型工地的安全管理方式?

    隨著施工規(guī)模擴大與現(xiàn)場節(jié)奏加快,傳統(tǒng)工地安全管理逐漸暴露出判斷與響應上的局限。本文從大型施工現(xiàn)場的實際變化出發(fā),分析了安全管理向現(xiàn)場 AI 判斷演進的必然性,并梳理了一套可長期運行的本
    的頭像 發(fā)表于 01-23 17:26 ?2089次閱讀
    當<b class='flag-5'>判斷</b>前移到現(xiàn)場,AI 如何重構(gòu)大型工地的安全<b class='flag-5'>管理</b>方式?

    【「Linux 設(shè)備驅(qū)動開發(fā)(第 2 版)」閱讀體驗】+讀深入理解Linux內(nèi)核內(nèi)存分配

    作者引入內(nèi)存相關(guān)術(shù)語,物理地址標識物理內(nèi)存位置。由于虛擬內(nèi)存機制,用戶和內(nèi)核從不直接訪問物理地址,而是通過相應的邏輯地址來訪問的。MMU(內(nèi)存
    發(fā)表于 01-16 20:05

    RDMA設(shè)計21:連接管理模塊設(shè)計

    本博文主要交流設(shè)計思路,在本博客已給出相關(guān)博文約100篇,希望對初學者有用。注意這里只是拋磚引玉,切莫認為參考這就可以完成商用IP設(shè)計。 連接管理模塊由一個連接信息緩存、一個連接管理狀態(tài)機和一個會話
    發(fā)表于 01-12 11:03

    RDMA設(shè)計14:連接管理模塊設(shè)計

    本博文主要交流設(shè)計思路,在本博客已給出相關(guān)博文130多篇,希望對初學者有用。注意這里只是拋磚引玉,切莫認為參考這就可以完成商用IP設(shè)計。連接管理模塊由一個連接信息緩存、一個連接管理狀態(tài)機和一個會話
    發(fā)表于 12-30 16:51

    輕量級參數(shù)的管理框架(C語言)

    嵌入式軟件中的系統(tǒng)數(shù)據(jù)參數(shù)是指在嵌入式系統(tǒng)中用于實現(xiàn)系統(tǒng)功能和控制的各種數(shù)據(jù),如用戶參數(shù)、狀態(tài)、配置信息等;那么如何管理這些數(shù)據(jù)對于嵌入式系統(tǒng)的正確運行和維護非常重要。 該參數(shù)管理框架代碼就是
    發(fā)表于 12-16 06:24

    rk基于linux/android內(nèi)存管理

    一、內(nèi)存分布 ? U-Boot 由前級 Loader 加載到 CONFIG_SYS_TEXT_BASE 地址,初始化時會探明當前系統(tǒng)的總內(nèi)存容 量, 32 位平臺上認為最大 4GB 可用(但是不影響
    的頭像 發(fā)表于 12-15 10:42 ?218次閱讀
    rk基于linux/android<b class='flag-5'>內(nèi)存</b><b class='flag-5'>管理</b>

    WebGL/Canvas 內(nèi)存泄露分析

    在構(gòu)建高性能、長周期運行的 WebGL/Canvas 應用(如 3D 編輯器、數(shù)據(jù)可視化平臺)時,內(nèi)存管理是一個至關(guān)重要且極具挑戰(zhàn)性的課題。 開發(fā)者通常面臨的內(nèi)存泄漏問題,其根源遠比簡單
    的頭像 發(fā)表于 10-21 11:40 ?425次閱讀
    WebGL/Canvas <b class='flag-5'>內(nèi)存</b>泄露分析

    如何判斷一款電源管理IC芯片的性能?

    判斷一款電源管理 IC(PMIC)的性能,需要結(jié)合其核心功能(電壓轉(zhuǎn)換、穩(wěn)定輸出、能效控制等)和應用場景(如消費電子、工業(yè)控制、汽車電子等),從關(guān)鍵技術(shù)指標、實際工況表現(xiàn)、可靠性等多維度綜合評估
    的頭像 發(fā)表于 08-18 09:59 ?1252次閱讀

    靈活高效ZBUFF — C內(nèi)存數(shù)據(jù)操作庫:優(yōu)化內(nèi)存管理的利器

    在C語言開發(fā)中,高效的內(nèi)存管理是提升程序性能的關(guān)鍵。ZBUFF作為一款靈活高效的內(nèi)存數(shù)據(jù)操作庫,通過優(yōu)化內(nèi)存分配與釋放機制,為開發(fā)者提供了更簡潔、更安全的API接口,極大地簡化了復雜數(shù)
    的頭像 發(fā)表于 08-14 18:01 ?708次閱讀
    靈活高效ZBUFF — C<b class='flag-5'>內(nèi)存</b>數(shù)據(jù)操作庫:優(yōu)化<b class='flag-5'>內(nèi)存</b><b class='flag-5'>管理</b>的利器

    HarmonyOS優(yōu)化應用內(nèi)存占用問題性能優(yōu)化四

    一、使用purgeable優(yōu)化C++內(nèi)存 Purgeable Memory是HarmonyOS中native層常用的內(nèi)存管理機制,可用于圖像處理的Bitmap、流媒體應用的一次性數(shù)據(jù)、圖片等
    發(fā)表于 05-24 17:20

    HarmonyOS優(yōu)化應用內(nèi)存占用問題性能優(yōu)化一

    使用相關(guān)接口創(chuàng)建PurgeableMemory對象,從而管理Purgeable內(nèi)存。 圖片加載和渲染:在使用Image組件加載和渲染圖片時,開發(fā)者可以手動調(diào)整圖片源文件的尺寸大小,使其與組件大小一致。這樣
    發(fā)表于 05-21 11:27