色哟哟视频在线观看-色哟哟视频在线-色哟哟欧美15最新在线-色哟哟免费在线观看-国产l精品国产亚洲区在线观看-国产l精品国产亚洲区久久

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

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

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

嵌入式C語(yǔ)言開(kāi)發(fā)基礎(chǔ)

玩轉(zhuǎn)單片機(jī)與嵌入式 ? 來(lái)源:玩轉(zhuǎn)單片機(jī)與嵌入式 ? 2023-02-16 11:10 ? 次閱讀

1 劍宗氣宗之爭(zhēng)

《笑傲江湖》中華山派的劍宗和氣宗之爭(zhēng),可謂異常激烈。那么問(wèn)題就來(lái)了,既然有劍宗氣宗之爭(zhēng),到底應(yīng)該先練劍,還是先練氣呢?引申到軟件開(kāi)發(fā)行業(yè)有沒(méi)劍氣之爭(zhēng)呢?

前面發(fā)布很多理論方面的文章,諸如4篇:基于RTOS的軟件開(kāi)發(fā)理論嵌入式軟件的設(shè)計(jì)模式(上)嵌入式軟件的設(shè)計(jì)模式(下)嵌入式軟件分層隔離的典范這些都是具備一定基礎(chǔ)后在架構(gòu)上的描述,類似于氣宗性質(zhì),這種比較抽象、見(jiàn)效慢。但高質(zhì)量的軟件開(kāi)發(fā),也是存在見(jiàn)效快的套路,針對(duì)有一定嵌入式C語(yǔ)言開(kāi)發(fā)基礎(chǔ)的,以劍宗之法進(jìn)行描述,可重點(diǎn)關(guān)注if判斷和內(nèi)存管理相關(guān)的講解,拋磚引玉。

2 文件結(jié)構(gòu)

1、C 程序通常分為兩類文件,一種是程序的聲明稱為頭文件,以“.h”為后綴,另一種是程序的實(shí)現(xiàn),以“.c”為后綴,一般每個(gè)c文件有個(gè)同名的h文件。

2、軟件的頭文件數(shù)目比較多,應(yīng)將頭文件和定義文件分別保存于不同的目錄,例如將頭文件保存于 include或者inc 目錄,將定義文件保存于 source 或src目錄;如果某些頭文件是私有的,它不會(huì)被用戶的程序直接引用,則沒(méi)有必要公開(kāi)其“聲明”。為了加強(qiáng)信息隱藏,這些私有的頭文件可以和定義文件存放于同一個(gè)目錄,即私有的h文件放在src目錄。

3、在文件頭添加版權(quán)和版本的聲明等信息,主要包括版權(quán)和功能,以及修改記錄,必要時(shí)可以為整個(gè)功能文件夾單獨(dú)新建readme說(shuō)明文檔。

4、為了防止頭文件被重復(fù)引用,必須用 ifndef/define/endif 結(jié)構(gòu)產(chǎn)生預(yù)處理塊。

5、頭文件中只存放“聲明”而不存放“定義”,更別提放變量,這是嚴(yán)重的錯(cuò)誤。

6、用 #include 格式來(lái)引用標(biāo)準(zhǔn)庫(kù)的頭文件,用 #include “filename.h” 格式來(lái)引用非標(biāo)準(zhǔn)庫(kù)的頭文件(編譯器將從用戶的工作目錄開(kāi)始搜索)。

7、文件可按層或者功能組件劃分不同的文件夾,便于其他人閱讀。

3 程序版式

版式雖然不會(huì)影響程序的功能,但會(huì)影響可讀性,程序的風(fēng)格統(tǒng)一則是賞心悅目。

代碼排版在編碼時(shí)確實(shí)很難把握,但可以編碼完成后統(tǒng)一用工具格式化,不管編碼使用Keil/MDK、Qt等集成工具,或者純粹的代碼編輯工具Source Insight,一般都支持自定義運(yùn)行可執(zhí)行文件,如Astyle。可以客制化新菜單,一鍵執(zhí)行Astyle,將代碼一鍵格式化,排版統(tǒng)一、層次分明。

Astyle官網(wǎng) http://astyle.sourceforge.net/ 按要求下載安裝,只需要AStyle.exe即可。關(guān)于其使用和參數(shù),可以再進(jìn)入Documentation。對(duì)代碼基本風(fēng)格,{}如何對(duì)齊、是否換行,switch-case如何排版,tab鍵占位寬度,運(yùn)算符或變量前后的空格等等,基本上代碼排版涉及的方方面面都有參數(shù)說(shuō)明。個(gè)人選擇的編碼參數(shù)是

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

效果如下

//微信公眾號(hào):嵌入式系統(tǒng)
intFoo(boolisBar)
{
if(isBar)
{
bar();
return1;
}
else
{
return0;
}
}

也可以參考 代碼的保養(yǎng)第3章。關(guān)于注釋,重要函數(shù)或段落必不可少,修改代碼同時(shí)修改相應(yīng)的注釋,以保證注釋與代碼的一致性。

4 命名規(guī)則

比較著名的命名規(guī)則當(dāng)推 Microsoft 公司的“匈牙利”法,該命名規(guī)則的主要思想是“在變量和函數(shù)名中加入前綴以增進(jìn)人們對(duì)程序的理解”。例如所有的字符變量均以ch 為前綴,若是指針變量則追加前綴 p。但沒(méi)有一種命名規(guī)則可以讓所有的程序員滿意,制定一種令大多數(shù)項(xiàng)目成員滿意的命名規(guī)則,重點(diǎn)是在整個(gè)團(tuán)隊(duì)和項(xiàng)目中貫徹實(shí)施。

事實(shí)上開(kāi)發(fā)大多數(shù)基于SDK,一般底層命名規(guī)則盡量與SDK風(fēng)格保持一致,至于上層就按團(tuán)隊(duì)標(biāo)準(zhǔn),個(gè)人比較傾向全部小寫(xiě)字母,用下劃線分割的風(fēng)格,例如 set_apn、timer_start。

不要出現(xiàn)標(biāo)識(shí)符完全相同的局部變量和全局變量,盡管兩者的作用域不同而不會(huì)發(fā)生語(yǔ)法錯(cuò)誤,但會(huì)使人誤解,全局變量也不要過(guò)于簡(jiǎn)短。

變量的名字應(yīng)當(dāng)使用“名詞”或者“形容詞+名詞”,函數(shù)的名字應(yīng)當(dāng)使用“動(dòng)詞”或者“動(dòng)詞+名詞”,用正確的反義詞組命名具有互斥意義的變量或相反動(dòng)作的函數(shù)等。

5 基本語(yǔ)句

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

5.1 if

if 語(yǔ)句是 C 語(yǔ)言中最簡(jiǎn)單、最常用的語(yǔ)句,然而很多程序員卻用隱含錯(cuò)誤的方式,僅以不同類型的變量與零值比較為例,展開(kāi)討論。

1、布爾變量與零值比較

不可將布爾變量直接與 TRUE、FALSE 或者 1、0 進(jìn)行比較。根據(jù)布爾類型的語(yǔ)義,零值為“假”(記為 FALSE),任何非零值都是“真”(記為T(mén)RUE)。TRUE 的值究竟是什么并沒(méi)有統(tǒng)一的標(biāo)準(zhǔn)。

假設(shè)布爾變量名字為 flag,它與零值比較的標(biāo)準(zhǔn) if 語(yǔ)句如下:

//微信公眾號(hào):嵌入式系統(tǒng)
if(flag)//表示flag為真
if(!flag)//表示flag為假

其它的用法都屬于不良風(fēng)格,例如:

//錯(cuò)誤范例
if(flag==TRUE)
if(flag==1)
if(flag==FALSE)
if(flag==0)

2、整型變量與零值比較

整型變量用“==”或“!=”直接與 0 比較,假設(shè)整型變量的名字為 value,它與零值比較的標(biāo)準(zhǔn) if 語(yǔ)句如下:

if(value==0)
if(value!=0)

不可模仿布爾變量的風(fēng)格而寫(xiě)成

//錯(cuò)誤范例
if(value)//會(huì)讓人誤解value是布爾變量
if(!value)

3、 浮點(diǎn)變量與零值比較

不可將浮點(diǎn)變量用“==”或“!=”與任何數(shù)字比較,無(wú)論是 float 還是 double 類型的變量,都有精度限制。不能將浮點(diǎn)變量用“==”或“!=”與數(shù)字比較,應(yīng)該設(shè)法轉(zhuǎn)化成“>=”或“<=”形式。假設(shè)浮點(diǎn)變量的名字為 x,應(yīng)當(dāng)將

if(x==0.0)//隱含錯(cuò)誤的比較,錯(cuò)誤

轉(zhuǎn)化為

constfloatEPSINON=0.00001
if((x>=-EPSINON)&&(x<=EPSINON))?
//其中EPSINON是允許的誤差(即精度),即x無(wú)限趨近于0.0

4、指針變量與零值比較

指針變量用“==”或“!=”與 NULL 比較, 指針變量的零值是“空”(記為 NULL),盡管 NULL 的值與 0 相同,但是兩者意義不同。假設(shè)指針變量的名字為 p,它與零值比較的標(biāo)準(zhǔn) if 語(yǔ)句如下:

if(p==NULL)//p與NULL顯式比較,強(qiáng)調(diào)p是指針變量
if(p!=NULL)

不要寫(xiě)成

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

5.2 for

在多重循環(huán)中,如果有可能,應(yīng)當(dāng)將最長(zhǎng)的循環(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];
}
}

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

5.3 switch

switch 是多分支選擇語(yǔ)句,而 if 語(yǔ)句只有兩個(gè)分支可供選擇;雖然可以用嵌套的if 語(yǔ)句來(lái)實(shí)現(xiàn)多分支選擇,但那樣的程序冗長(zhǎng)難讀。這是 switch 語(yǔ)句存在的理由。

switch-case 即使不需要 default 處理,也應(yīng)該保留語(yǔ)句 default : break; 這樣做并非多此一舉,而是為了防止別人誤以為你忘了 default 處理。確實(shí)不需要break的case,務(wù)必加上注釋標(biāo)明。

5.4 goto

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

//微信公眾號(hào):嵌入式系統(tǒng)
//代碼只是表意,可能無(wú)法編譯
#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;
}

對(duì)于內(nèi)存申請(qǐng)釋放、文件打開(kāi)關(guān)閉這種成對(duì)操作,或者各種異常處理的統(tǒng)一支持場(chǎng)景,就比較適合goto。類似的還有do-while(0)這種語(yǔ)句。

關(guān)于運(yùn)算優(yōu)先級(jí),熟記運(yùn)算符優(yōu)先級(jí)是比較困難的,如果代碼行中的運(yùn)算符比較多,為了防止產(chǎn)生歧義并提高可讀性,全部加括號(hào)明確表達(dá)式的操作順序,雖然愚笨但是可靠

6 常量

常量是一種標(biāo)識(shí)符,它的值在運(yùn)行期間恒定不變。C 語(yǔ)言用 #define 來(lái)定義常量(稱為宏常量),但用 const 來(lái)定義常量(稱為 const 常量)其實(shí)更佳。

#defineMAX100
constfloatPI=3.14159;

const 常量有數(shù)據(jù)類型,而宏常量沒(méi)有數(shù)據(jù)類型。編譯器可以對(duì)前者進(jìn)行類型安全檢查,而對(duì)后者只進(jìn)行字符替換,沒(méi)有類型安全檢查,并且在字符替換可能會(huì)產(chǎn)生意料不到的錯(cuò)誤,所以復(fù)雜參數(shù)宏必須為每個(gè)參數(shù)加上()限制。

但也有特例

constintSIZE=100;
intarray[SIZE];//有的編譯器認(rèn)為是錯(cuò)誤,這就必須用define了

需要對(duì)外公開(kāi)的常量放在頭文件中,不需要對(duì)外公開(kāi)的常量放在定義文件的頭部。為便于管理,可以把不同模塊的常量集中存放在一個(gè)公共的頭文件中。

7函數(shù)

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

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

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

voidset_size(intwidth,intheight);//良好的風(fēng)格
voidset_size(int,int);//不良的風(fēng)格
intget_size(void);//良好的風(fēng)格
intget_size();//不良的風(fēng)格

參數(shù)命名要恰當(dāng),順序要合理。例如字符串拷貝函數(shù)

char*strcpy(char*dest,constchar*src);

從名字上就可以看出應(yīng)該把 src 拷貝到 dest。還有一個(gè)問(wèn)題,兩個(gè)參數(shù)哪個(gè)該在前哪個(gè)該在后?參數(shù)的順序要遵循程序員的習(xí)慣。一般地,應(yīng)將目的參數(shù)放在前面,源參數(shù)放在后面。

這里也說(shuō)明下const的意義,如果參數(shù)僅作輸入用,則應(yīng)在類型前加 const,以防止在函數(shù)體內(nèi)被意外修改。

避免函數(shù)有太多的參數(shù),參數(shù)個(gè)數(shù)盡量控制在 5 個(gè)以內(nèi),如果參數(shù)太多,在使用時(shí)容易將參數(shù)類型或順序搞錯(cuò),可以定為結(jié)構(gòu)體指針,但盡量帶上參數(shù)注釋。

除了printf、sprintf標(biāo)準(zhǔn)庫(kù)或基于這類的日志輸出接口,盡量不要使用類型和數(shù)目不確定的參數(shù)。

7.2 返回值的規(guī)則

不要省略返回值的類型,默認(rèn)不加類型說(shuō)明的函數(shù)一律自動(dòng)按整型處理。為了避免混亂,如果函數(shù)沒(méi)有返回值,應(yīng)聲明為 void 類型。

不要將正常值和錯(cuò)誤標(biāo)志混在一起返回。正常值用輸出參數(shù)獲得,而錯(cuò)誤標(biāo)志用 return 語(yǔ)句返回。

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

不同功能的函數(shù)其內(nèi)部實(shí)現(xiàn)各不相同,看起來(lái)似乎無(wú)法就“內(nèi)部實(shí)現(xiàn)”達(dá)成一致的觀點(diǎn)。但根據(jù)經(jīng)驗(yàn),我們可以在函數(shù)體的“入口處”和“出口處”從嚴(yán)把關(guān),從而提高函數(shù)的質(zhì)量。

在函數(shù)體的“入口處”,對(duì)參數(shù)的有效性進(jìn)行檢查,很多程序錯(cuò)誤是由非法參數(shù)引起的,我們應(yīng)該充分理解并正確使用“斷言”(assert)來(lái)防止此類錯(cuò)誤。

在函數(shù)體的“出口處”,對(duì) return 語(yǔ)句的正確性和效率進(jìn)行檢查。如果函數(shù)有返回值,那么函數(shù)的“出口處”是 return 語(yǔ)句。調(diào)用處應(yīng)該盡量關(guān)注返回值,對(duì)異常進(jìn)行處理

關(guān)于return的值,不可返回指向“棧內(nèi)存”的“指針,該內(nèi)存在函數(shù)體結(jié)束時(shí)被自動(dòng)銷毀。例如

char*Func(void)
{
charstr[]=“helloworld”;//str的內(nèi)存位于棧上returnstr;//將導(dǎo)致錯(cuò)誤
}

盡量避免函數(shù)帶有“記憶”功能,相同的輸入應(yīng)當(dāng)產(chǎn)生相同的輸出。帶有“記憶”功能的函數(shù),其行為可能是不可預(yù)測(cè)的,因?yàn)樗男袨榭赡苋Q于某種“記憶狀態(tài)”。這樣的函數(shù)既不易理解又不利于測(cè)試和維護(hù)。在 C語(yǔ)言中,函數(shù)的 static 局部變量是函數(shù)的“記憶”存儲(chǔ)器。建議盡量少用 static 局部變量,除非必需。

7.4 斷言

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

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 不應(yīng)該產(chǎn)生任何副作用。所以 assert 不是函數(shù),而是宏。可以把a(bǔ)ssert 看成一個(gè)在任何系統(tǒng)狀態(tài)下都可以安全使用的無(wú)害測(cè)試手段。如果程序在 assert處終止了,并不是說(shuō)含有該 assert 的函數(shù)有錯(cuò)誤,而是調(diào)用者出了差錯(cuò),assert 有助于找到發(fā)生錯(cuò)誤的原因。

軟件有必要進(jìn)行防錯(cuò)設(shè)計(jì),如果“不可能發(fā)生”的事情的確發(fā)生了,則要使用斷言進(jìn)行報(bào)警。

8 內(nèi)存管理

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

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

內(nèi)存分配方式有三種:

1、從靜態(tài)存儲(chǔ)區(qū)域分配。內(nèi)存在程序編譯的時(shí)候就已經(jīng)分配好,這塊內(nèi)存在程序的整個(gè)運(yùn)行期間都存在。例如全局變量,static 變量。

2、在棧上創(chuàng)建。在執(zhí)行函數(shù)時(shí),函數(shù)內(nèi)局部變量的存儲(chǔ)單元都可以在棧上創(chuàng)建,函數(shù)執(zhí)行結(jié)束時(shí)這些存儲(chǔ)單元自動(dòng)被釋放。棧內(nèi)存分配運(yùn)算內(nèi)置于處理器的指令集中,效率很高,但是分配的內(nèi)存容量有限。

3、從堆上分配,亦稱動(dòng)態(tài)內(nèi)存分配。程序在運(yùn)行的時(shí)候用 malloc 或 new 申請(qǐng)任意多少的內(nèi)存,程序員自己負(fù)責(zé)在何時(shí)用 free 或 delete 釋放內(nèi)存。動(dòng)態(tài)內(nèi)存的生存期由我們決定,使用非常靈活,但風(fēng)險(xiǎn)也大。

8.2 內(nèi)存錯(cuò)誤及其對(duì)策

發(fā)生內(nèi)存錯(cuò)誤是件非常麻煩的事情。編譯器不能自動(dòng)發(fā)現(xiàn)這些錯(cuò)誤,通常是在程序運(yùn)行時(shí)才能捕捉到,而這些錯(cuò)誤大多沒(méi)有明顯的癥狀,時(shí)隱時(shí)現(xiàn),增加了改錯(cuò)的難度。常見(jiàn)的內(nèi)存錯(cuò)誤及其對(duì)策如下:

1、內(nèi)存分配未成功,卻使用了它

編程新手常犯這種錯(cuò)誤,因?yàn)樗麄儧](méi)有意識(shí)到內(nèi)存分配會(huì)不成功。常用解決辦法是,在使用內(nèi)存之前檢查指針是否為 NULL。如果指針 p 是函數(shù)的參數(shù),可在函數(shù)的入口處用 assert(p!=NULL)進(jìn)行檢查,或者用 if(p==NULL)或 if(p!=NULL)進(jìn)行防錯(cuò)處理。

2、內(nèi)存分配雖然成功,但是尚未初始化就引用它

犯這種錯(cuò)誤主要有兩個(gè)起因:一是沒(méi)有初始化的觀念;二是誤以為內(nèi)存的缺省初值全為零,導(dǎo)致引用初值錯(cuò)誤。內(nèi)存的缺省初值究竟是什么并沒(méi)有統(tǒng)一的標(biāo)準(zhǔn)(盡管有些時(shí)候?yàn)榱阒担瑸榱税踩瑢?duì)分配的內(nèi)存都進(jìn)行清零。

3、內(nèi)存分配成功并且已經(jīng)初始化,但操作越過(guò)了內(nèi)存的邊界

數(shù)組使用時(shí)經(jīng)常會(huì)發(fā)生下標(biāo)“多 1”或“少 1”的操作。特別是在 for 循環(huán)語(yǔ)句中,循環(huán)次數(shù)很容易搞錯(cuò),導(dǎo)致數(shù)組操作越界。

4、忘記釋放內(nèi)存,造成內(nèi)存泄露

含有這種錯(cuò)誤的函數(shù)每被調(diào)用一次就丟失一塊內(nèi)存。剛開(kāi)始時(shí)系統(tǒng)的內(nèi)存充足,運(yùn)行正常,但隨著運(yùn)行時(shí)間加長(zhǎng),程序突然死掉,內(nèi)存耗盡。動(dòng)態(tài)內(nèi)存的申請(qǐng)與釋放必須配對(duì),程序中 malloc 與 free 的成對(duì)使用。

5、已經(jīng)釋放的內(nèi)存卻繼續(xù)使用它

程序中的調(diào)用關(guān)系過(guò)于復(fù)雜,邏輯順序錯(cuò)誤,或者使用了指向“棧內(nèi)存”的“臨時(shí)指針,使用 free 或 delete 釋放了內(nèi)存后,務(wù)必將指針設(shè)置為 NULL,使用前判斷是否為NULL。

關(guān)于指針的使用建議,用 malloc 申請(qǐng)內(nèi)存之后,應(yīng)該立即檢查指針值是否為 NULL,非NULL的賦初值;使用結(jié)束后用 free 釋放,且將指針設(shè)置為 NULL,防止誤用“野指針”。對(duì)動(dòng)態(tài)內(nèi)存的一些防護(hù)性操作,可以參考微信公眾號(hào)【嵌入式系統(tǒng)】的文章動(dòng)態(tài)內(nèi)存管理及防御性編程

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

C 程序中指針和數(shù)組在不少地方可以相互替換著用,讓人產(chǎn)生一種錯(cuò)覺(jué),以為兩者是等價(jià)的。

數(shù)組要么在靜態(tài)存儲(chǔ)區(qū)被創(chuàng)建(如全局?jǐn)?shù)組),要么在棧上被創(chuàng)建。數(shù)組名對(duì)應(yīng)著(而不是指向)一塊內(nèi)存,其地址與容量在生命期內(nèi)保持不變,只有數(shù)組的內(nèi)容可以改變。

指針可以隨時(shí)指向任意類型的內(nèi)存塊,它的特征是“可變”,所以我們常用指針來(lái)操作動(dòng)態(tài)內(nèi)存。指針遠(yuǎn)比數(shù)組靈活,但也更危險(xiǎn)。

下面以字符串為例比較指針與數(shù)組的特性。

1、修改內(nèi)容

字符數(shù)組 a 的容量是 6 個(gè)字符,其內(nèi)容為 hello。a 的內(nèi)容可以改變,如 a[0]= ‘X’。指針 p 指向常量字符串“world”(位于靜態(tài)存儲(chǔ)區(qū),內(nèi)容為 world),常量字符串的內(nèi)容是不可以被修改的。從語(yǔ)法上看,編譯器并不覺(jué)得語(yǔ)句 p[0]= ‘X’有什么不妥,但是該語(yǔ)句企圖修改常量字符串的內(nèi)容而導(dǎo)致運(yùn)行錯(cuò)誤。

chara[]=“hello”;
a[0]=‘X’;
cout<endl;
char*p=“world”;//注意p指向常量字符串
p[0]=‘X’;//編譯器不能發(fā)現(xiàn)該錯(cuò)誤
cout<endl;

2、 內(nèi)容復(fù)制與比較

不能對(duì)數(shù)組名進(jìn)行直接復(fù)制與比較,若想把數(shù)組 a 的內(nèi)容復(fù)制給數(shù)組 b,不能用語(yǔ)句 b = a ,否則將產(chǎn)生編譯錯(cuò)誤。應(yīng)該用標(biāo)準(zhǔn)庫(kù)函數(shù) strcpy 進(jìn)行復(fù)制。同理,比較 b 和 a 的內(nèi)容是否相同,不能用 if(b == a) 來(lái)判斷,應(yīng)該用標(biāo)準(zhǔn)庫(kù)函數(shù) strcmp進(jìn)行比較。

語(yǔ)句 p = a 并不能把 a 的內(nèi)容復(fù)制指針 p,而是把 a 的地址賦給了 p。要想復(fù)制 a的內(nèi)容,可以先用庫(kù)函數(shù) malloc 為 p 申請(qǐng)一塊容量為 strlen(a)+1 個(gè)字符的內(nèi)存,再用 strcpy 進(jìn)行字符串復(fù)制。同理,語(yǔ)句 if(p==a) 比較的不是內(nèi)容而是地址,應(yīng)該用庫(kù)函數(shù) strcmp 來(lái)比較。

//數(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、計(jì)算內(nèi)存容量

用運(yùn)算符 sizeof 可以計(jì)算出數(shù)組的容量(字節(jié)數(shù))。sizeof(a)的值是 12(注意別忘了’’)。指針 p 指向 a,但是 sizeof(p)的值卻是 4。這是因?yàn)閟izeof(p)得到的是一個(gè)指針變量的字節(jié)數(shù),相當(dāng)于 sizeof(char*),而不是 p 所指的內(nèi)存容量。/C 語(yǔ)言沒(méi)有辦法知道指針?biāo)傅膬?nèi)存容量,只能在申請(qǐng)內(nèi)存時(shí)記住它。

chara[]="helloworld";
char*p=a;
cout<sizeof(a)<endl;//12字節(jié)
cout<sizeof(p)<endl;//4字節(jié)

當(dāng)數(shù)組作為函數(shù)的參數(shù)進(jìn)行傳遞時(shí),該數(shù)組自動(dòng)退化為同類型的指針。不論數(shù)組 a 的容量是多少,sizeof(a)始終等于 sizeof(char *)。

voidFunc(chara[100])
{
cout<sizeof(a)<endl;//4字節(jié)而不是100字節(jié)
}

4、指針參數(shù)是如何傳遞內(nèi)存

如果函數(shù)的參數(shù)是一個(gè)指針,不要指望用該指針去申請(qǐng)動(dòng)態(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");//運(yùn)行錯(cuò)誤
}

test 函數(shù)的get_memory(str, 100) 并沒(méi)有使 str 獲得期望的內(nèi)存,str 依舊是 NULL,為什么?

問(wèn)題出在函數(shù) get_memory,編譯器總是要為函數(shù)的每個(gè)參數(shù)制作臨時(shí)副本,指針參數(shù) p 的副本是 _p,編譯器使 _p = p。如果函數(shù)體內(nèi)的程序修改了_p 的內(nèi)容,就導(dǎo)致參數(shù) p 的內(nèi)容作相應(yīng)的修改。這就是指針可以用作輸出參數(shù)的原因。而范例中_p 申請(qǐng)了新的內(nèi)存,只是把_p 所指的內(nèi)存地址改變了,但是 p 絲毫未變。所以函數(shù) get_memory并不能輸出任何東西。事實(shí)上,每執(zhí)行一次 get_memory就會(huì)泄露一塊內(nèi)存,因?yàn)闆](méi)有用free 釋放內(nèi)存。

如果非得要用指針參數(shù)去申請(qǐng)內(nèi)存,那么應(yīng)該改用“指向指針的指針”,正確范例如下:

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);
}

由于“指向指針的指針”這個(gè)概念不容易理解,可以用函數(shù)返回值來(lái)傳遞動(dòng)態(tài)內(nèi)存,這種方法更加簡(jiǎn)單。

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ù)返回值來(lái)傳遞動(dòng)態(tài)內(nèi)存這種方法雖然好用,但是常常有人把 return 語(yǔ)句用錯(cuò),不要用 return 語(yǔ)句返回指向“棧內(nèi)存”的指針,因?yàn)樵搩?nèi)存在函數(shù)結(jié)束時(shí)自動(dòng)消亡,錯(cuò)誤范例如下:

//錯(cuò)誤范例
char*get_string(void)
{
charp[]="helloworld";
returnp;//編譯器將提出警告
}
voidtest4(void)
{
char*str=NULL;
str=get_string();//str的內(nèi)容是隨機(jī)垃圾
}

執(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 運(yùn)行雖然不會(huì)出錯(cuò),但是函數(shù) get_string2的設(shè)計(jì)概念卻是錯(cuò)誤的。因?yàn)?get_string2內(nèi)的“hello world”是常量字符串,位于靜態(tài)存儲(chǔ)區(qū),它在程序生命期內(nèi)恒定不變。無(wú)論什么時(shí)候調(diào)用 get_string2,它返回的始終是同一個(gè)“只讀”的內(nèi)存塊,也就是test5是無(wú)法修改str的。

5、 free 把指針怎么了

free 只是把指針?biāo)傅膬?nèi)存給釋放掉,但并沒(méi)有把指針本身干掉;指針 p 被 free 以后其地址仍然不變(非 NULL),只是該地址對(duì)應(yīng)的內(nèi)存是垃圾,p 成了“野指針”。如果此時(shí)不把 p 設(shè)置為 NULL,會(huì)讓人誤以為 p 是個(gè)合法的指針。

如果程序比較長(zhǎng),我們有時(shí)記不住 p 所指的內(nèi)存是否已經(jīng)被釋放,在繼續(xù)使用 p 之前,通常會(huì)用語(yǔ)句 if (p != NULL)進(jìn)行防錯(cuò)處理。很遺憾,此時(shí) if 語(yǔ)句起不到防錯(cuò)作用,此時(shí) p 不是 NULL 指針,但它也不指向合法的內(nèi)存塊。

char*p=(char*)malloc(100);
strcpy(p,“hello”);
free(p);//p所指的內(nèi)存被釋放,但是p所指的地址仍然不變

if(p!=NULL)//沒(méi)有起到防錯(cuò)作用
{
strcpy(p,“world”);//出錯(cuò)
}

6、動(dòng)態(tài)內(nèi)存會(huì)被自動(dòng)釋放嗎

函數(shù)體內(nèi)的局部變量在函數(shù)結(jié)束時(shí)自動(dòng)消亡。

voidfunc(void)
{
char*p=(char*)malloc(100);//動(dòng)態(tài)內(nèi)存會(huì)自動(dòng)釋放嗎?
}

但是,變量p 是局部的指針變量,它消亡的時(shí)候并不會(huì)讓它所指的動(dòng)態(tài)內(nèi)存一起完蛋。發(fā)現(xiàn)指針有一些“似是而非”的特征:

(1)指針消亡了,并不表示它所指的內(nèi)存會(huì)被自動(dòng)釋放。

(2)內(nèi)存被釋放了,并不表示指針會(huì)消亡或者成了 NULL 指針。

7、杜絕“野指針”

“野指針”不是 NULL 指針,是指向“垃圾”內(nèi)存的指針。人們一般不會(huì)錯(cuò)用 NULL指針,因?yàn)橛?if 語(yǔ)句很容易判斷;但是“野指針”是很危險(xiǎn)的,if 語(yǔ)句對(duì)它不起作用。“野指針”的成因主要有三種:

(1)指針變量沒(méi)有被初始化。任何指針變量剛被創(chuàng)建時(shí)不會(huì)自動(dòng)成為 NULL 指針,它的缺省值是隨機(jī)的,所以,指針變量在創(chuàng)建的同時(shí)應(yīng)當(dāng)被初始化。

(2)指針 p 被 free 或者 delete 之后,沒(méi)有置為 NULL,讓人誤以為 p 是個(gè)合法的指針。

(3)指針操作超越了變量的作用范圍。這種情況讓人防不勝防。

8、內(nèi)存耗盡怎么辦

如果在申請(qǐng)動(dòng)態(tài)內(nèi)存時(shí)找不到足夠大的內(nèi)存塊,malloc 將返回 NULL 指針,宣告內(nèi)存申請(qǐng)失敗。判斷指針是否為 NULL,如果是則馬上用 return 語(yǔ)句終止本函數(shù),或者用 exit(1)終止整個(gè)程序的運(yùn)行。如果發(fā)生“內(nèi)存耗盡”,一般說(shuō)來(lái)應(yīng)用程序已經(jīng)無(wú)藥可救,嵌入式設(shè)備只能重啟了。

9、心得體會(huì)

很少有人能拍拍胸脯說(shuō)通曉指針與內(nèi)存管理,越是怕指針,就越要使用指針。不會(huì)正確使用指針,肯定算不上是合格的嵌入式程序員。

9 其它編程經(jīng)驗(yàn)

9.1 使用 const 提高函數(shù)的健壯性

const 是 constant 的縮寫(xiě),“恒定不變”的意思。被 const 修飾的東西都受到強(qiáng)制保護(hù),可以預(yù)防意外的變動(dòng),能提高程序的健壯性。很多 C++程序設(shè)計(jì)書(shū)籍建議:“Use const whenever you need”。

1、用 const 修飾函數(shù)的參數(shù)如果參數(shù)作輸出用,不論它是什么數(shù)據(jù)類型,都不能加 const 修飾,否則該參數(shù)將失去輸出功能。const 只能修飾輸入?yún)?shù),如果輸入?yún)?shù)采用“指針傳遞”,那么加 const 修飾可以防止意外地改動(dòng)該指針,起到保護(hù)作用。例如 strcpy函數(shù):

char*strcpy(char*dest,constchar*src);

其中 src是輸入?yún)?shù),dest是輸出參數(shù)。給 src加上 const修飾后,如果函數(shù)體內(nèi)的語(yǔ)句試圖改動(dòng) src 的內(nèi)容,編譯器將指出錯(cuò)誤。

2、如果輸入?yún)?shù)采用“值傳遞”,由于函數(shù)將自動(dòng)產(chǎn)生臨時(shí)變量用于復(fù)制該參數(shù),該輸入?yún)?shù)本來(lái)就無(wú)需保護(hù),所以不要加 const 修飾。

voidfunc1(intx)寫(xiě)成voidfunc1(constintx)//const無(wú)意義

3、對(duì)于非內(nèi)部數(shù)據(jù)類型的參數(shù)而言,如 void func(A a) 這樣聲明的函數(shù)注定效率比較低,其中 A 為用戶自定義的數(shù)據(jù)類型,可以理解為大結(jié)構(gòu)。

函數(shù)體內(nèi)將產(chǎn)生 A 類型的臨時(shí)對(duì)象用于復(fù)制參數(shù) a,而臨時(shí)對(duì)象的構(gòu)造、復(fù)制、析構(gòu)過(guò)程都將消耗時(shí)間。為了提高效率,可以將函數(shù)聲明改為:

voidfunc(A&a)

因?yàn)椤耙脗鬟f”僅借用一下參數(shù)的別名而已,不需要產(chǎn)生臨時(shí)對(duì)象。但是函數(shù) 存在一個(gè)缺點(diǎn),“引用傳遞”有可能改變參數(shù) a,這是我們不期望的。解決這個(gè)問(wèn)題很容易,加 const修飾即可,因此函數(shù)最終成為

voidfunc(constA&a)

4、用 const 修飾函數(shù)的返回值,如果給以“指針傳遞”方式的函數(shù)返回值加 const 修飾,那么函數(shù)返回值(即指針)的內(nèi)容不能被修改,該返回值只能被賦給加 const 修飾的同類型指針。例如函數(shù)

constchar*get_string(void);
char*str=get_string();//出現(xiàn)編譯錯(cuò)誤:
constchar*str=get_string();//正確的用法

9.2 提高程序的效率

程序的時(shí)間效率是指運(yùn)行速度,空間效率是指程序占用內(nèi)存或者外存的狀況。

不要一味地追求程序的效率,應(yīng)當(dāng)在滿足正確性、可靠性、健壯性、可讀性等質(zhì)量因素的前提下,設(shè)法提高程序的效率。

在優(yōu)化程序的效率時(shí),應(yīng)當(dāng)先找出限制效率的“瓶頸”,不要在無(wú)關(guān)緊要之處優(yōu)化。有時(shí)候時(shí)間效率和空間效率可能對(duì)立,此時(shí)應(yīng)當(dāng)分析那個(gè)更重要,作出適當(dāng)?shù)恼壑浴@缍嗷ㄙM(fèi)一些內(nèi)存來(lái)提高性能。

關(guān)于其它C關(guān)鍵字用法,可以參考C語(yǔ)言關(guān)鍵字應(yīng)用技巧

10 小結(jié)

不論劍宗、氣宗優(yōu)劣,先把功能跑通再反推代碼原理和實(shí)現(xiàn)流程,還是先理清時(shí)序和原理再編碼實(shí)現(xiàn)功能,短期內(nèi)劍宗效率高,加工資快,但后期發(fā)展有限;氣宗則面臨前期可能被淘汰,尤其在勢(shì)利的小公司,不注重新人培養(yǎng),但前期積累,后期融會(huì)貫通,在技術(shù)方面成為權(quán)威。如果合二為一,項(xiàng)目緊急則拿來(lái)就用,空閑時(shí)專研總結(jié),取長(zhǎng)補(bǔ)短,則是高級(jí)程序員的素質(zhì)。

審核編輯 :李倩


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

    關(guān)注

    180

    文章

    7614

    瀏覽量

    137444
  • RTOS
    +關(guān)注

    關(guān)注

    22

    文章

    819

    瀏覽量

    119829
  • 代碼
    +關(guān)注

    關(guān)注

    30

    文章

    4823

    瀏覽量

    68904

原文標(biāo)題:10 小結(jié)

文章出處:【微信號(hào):玩轉(zhuǎn)單片機(jī)與嵌入式,微信公眾號(hào):玩轉(zhuǎn)單片機(jī)與嵌入式】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。

收藏 人收藏

    評(píng)論

    相關(guān)推薦

    嵌入式C語(yǔ)言開(kāi)發(fā)教程之用c寫(xiě)CGI_程序簡(jiǎn)要指南

    嵌入式C語(yǔ)言開(kāi)發(fā)教程之用c寫(xiě)CGI_程序簡(jiǎn)要指南
    發(fā)表于 08-20 09:55

    陳正沖—C語(yǔ)言深度解剖(第2版)(電子版)

    ,絕對(duì)受益匪淺。 嵌入式C語(yǔ)言開(kāi)發(fā)經(jīng)驗(yàn)和平時(shí)講解C語(yǔ)言的心得體會(huì)整理而成,其中有很多作者獨(dú)特的見(jiàn)
    發(fā)表于 12-26 13:20

    嵌入式C語(yǔ)言開(kāi)發(fā)嵌入式Linux C開(kāi)發(fā)的區(qū)別

    嵌入式Linux系統(tǒng)開(kāi)發(fā)嵌入式Linux系統(tǒng)開(kāi)發(fā)(應(yīng)用軟件開(kāi)發(fā)):通過(guò)內(nèi)核提供的服務(wù)實(shí)現(xiàn)相應(yīng)功能一、嵌入
    發(fā)表于 11-05 08:12

    數(shù)據(jù)變量的相關(guān)資料推薦

    嵌入式C語(yǔ)言開(kāi)發(fā)入門(mén)——數(shù)據(jù)變量變量的四個(gè)部分:空間、變量名、變量地址、變量類型C語(yǔ)言標(biāo)識(shí)符命名
    發(fā)表于 12-15 07:19

    測(cè)試驅(qū)動(dòng)的嵌入式C語(yǔ)言開(kāi)發(fā)讀書(shū)筆記分享

    測(cè)試驅(qū)動(dòng)的嵌入式C語(yǔ)言開(kāi)發(fā)讀書(shū)筆記1.測(cè)試驅(qū)動(dòng)開(kāi)發(fā)瀑布模型的最后開(kāi)發(fā)人員會(huì)亂作一團(tuán),而縮短
    發(fā)表于 12-15 07:25

    C語(yǔ)言的編譯步驟

    嵌入式C語(yǔ)言開(kāi)發(fā)入門(mén)——程序編譯計(jì)算機(jī)語(yǔ)言發(fā)展過(guò)程C語(yǔ)言
    發(fā)表于 12-15 08:21

    嵌入式C 語(yǔ)言開(kāi)發(fā)ADSP21XX 系列DSP

    詳細(xì)介紹使用VisualDSP 開(kāi)發(fā)工具進(jìn)行ADSP21XX 的C 語(yǔ)言編程的方法;分析其C 語(yǔ)言運(yùn)行庫(kù)的結(jié)構(gòu),并且結(jié)合實(shí)例介紹
    發(fā)表于 05-15 14:42 ?21次下載

    基于C語(yǔ)言嵌入式軟件開(kāi)發(fā)中的錯(cuò)誤追蹤機(jī)制

      引言   本文針對(duì)嵌入式C語(yǔ)言開(kāi)發(fā)的特點(diǎn),提出一種基于堆棧模式的異常追蹤編程模型,能夠?qū)崿F(xiàn)有效的異常現(xiàn)場(chǎng)保存與恢復(fù),并為后期的問(wèn)題分析與解決打好基礎(chǔ)。
    發(fā)表于 08-19 09:25 ?749次閱讀
    基于<b class='flag-5'>C</b><b class='flag-5'>語(yǔ)言</b>的<b class='flag-5'>嵌入式</b>軟件<b class='flag-5'>開(kāi)發(fā)</b>中的錯(cuò)誤追蹤機(jī)制

    嵌入式c語(yǔ)言編程(由淺入深)

    本內(nèi)容詳細(xì)介紹了嵌入式c語(yǔ)言編程的各項(xiàng)知識(shí),包括嵌入式c語(yǔ)言編程,
    發(fā)表于 11-02 14:37 ?0次下載
    <b class='flag-5'>嵌入式</b><b class='flag-5'>c</b><b class='flag-5'>語(yǔ)言</b>編程(由淺入深)

    C語(yǔ)言深度解剖完美PDF電子書(shū)免費(fèi)下載

    C語(yǔ)言深度解剖》是2012年出版的圖書(shū),作者是陳正沖。本書(shū)作者結(jié)合自身多年嵌入式C語(yǔ)言開(kāi)發(fā)經(jīng)驗(yàn)
    發(fā)表于 11-28 15:35 ?24次下載

    基于嵌入式C語(yǔ)言開(kāi)發(fā)中的異常堆棧錯(cuò)誤追蹤機(jī)制的設(shè)計(jì)

    對(duì)于嵌入式軟件來(lái)說(shuō),盡量節(jié)省內(nèi)存資源、降低程序代碼量是十分重要的。因此,將程序中所有錯(cuò)誤、異常情況都進(jìn)行了統(tǒng)一編碼,提高了錯(cuò)誤處理代碼的規(guī)范化與可讀性。設(shè)計(jì)8位整數(shù)編碼格式如下:
    發(fā)表于 03-09 10:35 ?1235次閱讀
    基于<b class='flag-5'>嵌入式</b><b class='flag-5'>C</b><b class='flag-5'>語(yǔ)言</b><b class='flag-5'>開(kāi)發(fā)</b>中的異常堆棧錯(cuò)誤追蹤機(jī)制的設(shè)計(jì)

    汽車電子行業(yè)的MISRA C標(biāo)準(zhǔn)分享

    的、高可靠性的嵌入式軟件。MISRA C則是由MISRA提出的針對(duì)嵌入式C語(yǔ)言開(kāi)發(fā)標(biāo)準(zhǔn),目的是提
    的頭像 發(fā)表于 05-11 13:43 ?1930次閱讀

    嵌入式linux c語(yǔ)言,嵌入式LinuxC語(yǔ)言開(kāi)發(fā)工具.pdf

    2 章 嵌入式Linux C 語(yǔ)言開(kāi)發(fā)工具本章目標(biāo)任何應(yīng)用程序的開(kāi)發(fā)都離不開(kāi)編輯器、編譯器及調(diào)試器,嵌入
    發(fā)表于 11-01 17:38 ?12次下載
    <b class='flag-5'>嵌入式</b>linux <b class='flag-5'>c</b><b class='flag-5'>語(yǔ)言</b>,<b class='flag-5'>嵌入式</b>LinuxC<b class='flag-5'>語(yǔ)言</b><b class='flag-5'>開(kāi)發(fā)</b>工具.pdf

    嵌入式系統(tǒng)設(shè)計(jì)--課堂總結(jié)(嵌入式Linux系統(tǒng)開(kāi)發(fā)

    嵌入式Linux系統(tǒng)開(kāi)發(fā)嵌入式Linux系統(tǒng)開(kāi)發(fā)(應(yīng)用軟件開(kāi)發(fā)):通過(guò)內(nèi)核提供的服務(wù)實(shí)現(xiàn)相應(yīng)功能一、嵌入
    發(fā)表于 11-02 12:21 ?21次下載
    <b class='flag-5'>嵌入式</b>系統(tǒng)設(shè)計(jì)--課堂總結(jié)(<b class='flag-5'>嵌入式</b>Linux系統(tǒng)<b class='flag-5'>開(kāi)發(fā)</b>)

    嵌入式軟件開(kāi)發(fā)工程師要求

    年終獎(jiǎng)金績(jī)效獎(jiǎng)金定期體檢申請(qǐng)職位競(jìng)爭(zhēng)力分析收藏職位信息1、2年以上嵌入式C語(yǔ)言開(kāi)發(fā)經(jīng)驗(yàn)或2年以上車身電子產(chǎn)品開(kāi)發(fā)經(jīng)驗(yàn)2、有良好的英語(yǔ)或日語(yǔ)文
    發(fā)表于 11-03 12:06 ?3次下載
    <b class='flag-5'>嵌入式</b>軟件<b class='flag-5'>開(kāi)發(fā)</b>工程師要求
    主站蜘蛛池模板: 蜜桃成熟时2电影免费观看d | 久久青青草原精品国产软件 | 在线视频 日韩视频二区 | 亚洲人女同志video | 久草热在线 | 国产全部视频列表支持手机 | xxxx69动漫| 人人草人人草 | 強姧伦久久久久久久久 | 肉多的小说腐小说 | 久久综合狠狠综合狠狠 | 高H内射NP古文 | 欧美特级另类xxx | 芳草地在线观看免费视频 | 黄色a三级免费看 | 亚洲精品久久一区二区三区四区 | 最近免费中文字幕完整版HD | 蜜柚视频网在线观看免费 | 中文成人在线 | 青柠在线电影高清免费观看 | 伊人狠狠丁香婷婷综合尤物 | 忘忧草日本在线社区WWW电影 | 亚洲综合久久一本伊伊区 | 小草高清视频免费直播 | 河南老太XXXXXHD | www色小姐 | 国内精品自线在拍2020不卡 | 国产欧美一区二区三区免费 | 永久免费精品影视网站 | 伊人久久综合谁合综合久久 | 河南老太XXXXXHD | 亚洲黄色免费观看 | 国产亚洲精品a在线观看app | 攵女yin乱合集高h | 无码爽死成人777在线观看网站 | 在线免费观看毛片网站 | 激情欧美日韩一区二区 | 日本十八禁无遮无挡漫画 | 美女挑战50厘米长的黑人 | 鬼灭之刃花街篇免费樱花动漫 | 幸福草电视剧演员表介绍 |