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

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

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

使用匯編知識排查疑難問題的方法

RTThread物聯(lián)網(wǎng)操作系統(tǒng) ? 來源:RTThread物聯(lián)網(wǎng)操作系統(tǒng) ? 作者:recan ? 2022-07-27 10:31 ? 次閱讀

1 前言


在我的上一篇文章中,有講到掌握匯編知識的重要性,關(guān)鍵時刻可能還會拯救你于泥潭之中。

那么,本篇文章,我將再介紹一個使用匯編知識排查疑難問題的方法,希望對大家有所幫助。

2 問題描述


問題是這樣的,前一段時間我們項目組在進行一項自測試中,偶然發(fā)現(xiàn)我們的代碼好像掛了一樣:現(xiàn)象就是命令行輸入不了,但是沒有看到復位信息輸出。

當時,我們一個小伙伴說:“好像我們的系統(tǒng)掛了?”當我了解到這個現(xiàn)象之后,根據(jù)我之前的排查經(jīng)驗,我當即得出了一個結(jié)論:“可能是我們的代碼跑進死循環(huán)了,好好檢查下”!

于是,我們開始debug代碼,加了一些必要的調(diào)試信息,最終發(fā)現(xiàn)有一個計算校驗的函數(shù),調(diào)進去了但是沒有退出來,而這個校驗的函數(shù)非常之簡單,它就長這樣:

uint16_t checksum(uint8_t *data, uint8_t len){    uint8_t i;    uint16_t sum = 0, res;    for (i = 0; i < len; i++) {        sum += data[i];    }    res = sum ;    return res;}

我想當你看到這段函數(shù)時,肯定也是:“臥槽,這TM不就是算累加校驗和嗎?怎么可能會死循環(huán)?”

沒錯,當時我們的爭論的場景也的確如此!

3 簡單分析


這個checksum函數(shù)真的是非常簡單,入?yún)⒑唵?、實現(xiàn)也簡單、返回值也簡單,根本不存在難點。

一步步來分析:

既然代碼沒有崩潰,證明data指針肯定非NULL的,不會有問題;

倒是這個len有些可疑,len的類型是uint8_t無符號的,它的范圍是0-255;但是如果外面?zhèn)魅氲氖?1呢?

如果傳入-1,強制轉(zhuǎn)換為uint8_t,其值也是255,那么下面的for循環(huán),依然只會跑256次,它必須得退出呀?

有沒有可能for循環(huán)的過程中,棧的值被修改了,然后i的值和len的值都變了,進而for的次數(shù)改變了?

于是我們開始打印i和len的值,發(fā)現(xiàn)他們兩個的值,都是正常變化的,并不是剛剛想的那樣。

這就很奇怪了?。?!

如果說這個for循環(huán)要“無限”循環(huán)下去,造成“死循環(huán)”,必須滿足的條件是len很大很大,但是len不是uint8_t類型嘛?最大也就255呀?

printf大法再來一遍:結(jié)果出乎我們的意料,請看:

6ab63322-0ce3-11ed-ba43-dac502259ad0.png

log輸出:

[12-21 1938]checksum 128 len: 4294967295[12-21 1938]0 4294967295[12-21 1938]1 4294967295[12-21 1938]2 4294967295[12-21 1938]3 4294967295[12-21 1938]4 4294967295[12-21 1938]5 4294967295[12-21 1938]6 4294967295[12-21 1938]7 4294967295[12-21 1938]8 4294967295[12-21 1938]9 4294967295[12-21 1938]10 4294967295    。。。省略很多[12-21 1938]250 4294967295[12-21 1938]251 4294967295[12-21 1938]252 4294967295[12-21 1938]253 4294967295[12-21 1938]254 4294967295[12-21 1938]255 4294967295[12-21 1938]256 4294967295[12-21 1938]257 4294967295[12-21 1938]258 4294967295[12-21 1938]259 4294967295[12-21 1938]260 4294967295

。。。還在不停的打印

看到這里似乎有點眉目了?len的值為4294967295?

這個值不是0xFFFFFFFF嗎?

我們再使用%d打印了一下len,發(fā)現(xiàn)值為-1。

回過頭來看下checksum的調(diào)用之處:

uint16_t res = checksum(&data[0], len - 1);

看似真相了,當len為0的時候,傳入的值不就是-1嗎?

好像是這么回事,但是-1進去,它是uint8_t的呀,頂多就是255?。吭趺醋兂闪?294967295? 到底是誰干的???

同時也發(fā)現(xiàn)關(guān)鍵問題了,這里并不是真正意義的“死循環(huán)”,而是for循環(huán)執(zhí)行太久了,導致長時間無法結(jié)束,因為我們的主頻才160MHZ,CPU就是猛跑,從1加到0xFFFFFFFF,也需要好長一段時間呢!

4 場景再現(xiàn)


為了充分說明這個問題,我盡可能地還原下當時我們的代碼場景:

/*一個結(jié)構(gòu)體定義數(shù)據(jù)不要急于吐槽它的定義,這代碼是開源的,冤有頭。。。還有不要懷疑是字節(jié)對齊不對齊的問題,曾經(jīng)我也懷疑過,最后知道真相的時候,我被打臉了!*/typedef struct _data_t {    /* result, final result */    uint8_t len;    uint8_t flag;    uint8_t passwd_len;     uint8_t *passwd;     uint8_t ssid_len;     uint8_t *ssid;     uint8_t token_len;     uint8_t *token;     uint8_t bssid_type_len;     uint8_t *bssid;     uint8_t ssid_is_gbk;    uint8_t ssid_auto_complete_disable;    uint8_t data[127];    uint8_t checksum;} data_t;

/* 1.c 調(diào)用checksum的C文件 *//* 定義全局的數(shù)據(jù) */static data_t g_data;/* 設(shè)置全局的數(shù)據(jù) */void set_global_data(void){    g_data.len = 0;}void handle_global_data(void){      uint16_t res = checksum(&g_data.data[0], g_data.len - 0);  //sometimes no return form checksum}void test_func_entry(void){    set_global_data();    handle_global_data();}
/* 2.c 定義checksum函數(shù)的工具類 */uint16_t checksum(uint8_t *data, uint8_t len){    uint8_t i;    uint16_t sum = 0, res;    for (i = 0; i < len; i++) {        sum += data[i];    }    res = sum ;    return res;}

在我的第一次認知里,還是len=-1=255的情況,由于g_data.data只有127字節(jié),但它最后是可以訪問到255下標的,其實這本身就有數(shù)據(jù)非法訪問的問題;但是經(jīng)過仔細論證,得出的結(jié)論是,這并不會導致死循環(huán),或者說并不會改變len的值;因為checksum里面知識讀取data指針的值,并沒改變它的值,即便越界了,頂多訪問了別人,并不會出啥異常(至少在我們的處理器平臺是這樣)。

這個問題對我們來說,真的是百思不得其解,為了規(guī)避掉這個問題,我們在調(diào)用checksum的時候做了判斷,但len為0的時候直接不調(diào)用,也就繞過了這個問題。

但是作為一個深挖底層邏輯的攻城獅來說,我們不應(yīng)該放過這樣的細節(jié),或許還有什么我們未發(fā)現(xiàn)的潛在風險呢?

這個問題一直困擾著我,時不時有空的時候,我就會想想,到底還有什么情況還會導致這個現(xiàn)象?

5 柳暗花明


偶然有一天,我正瀏覽到一篇關(guān)于編譯器做代碼優(yōu)化的文章,它是在知乎上發(fā)出來的,我看到其中一個重要線索:

6adccf5a-0ce3-11ed-ba43-dac502259ad0.png

突然我腦子里,閃過一個疑問:“會不會那段for循環(huán)的checksum函數(shù),正是因為調(diào)用方?jīng)]有申明checksum函數(shù),也就是說沒有include對應(yīng)的頭文件導致編譯器做了默認處理呢?”?

我們都知道,在使用gcc編譯器編譯C代碼時,如果一個函數(shù)未申明就調(diào)用,是會報一個警告的:“warning: implicit declaration of function ‘checksum’ [-Wimplicit-function-declaration]”!

同時,尤其編譯器不知道被調(diào)用函數(shù)的原型,那么它只能依靠你的調(diào)用代碼結(jié)合一些默認值做假設(shè):

比如我們的調(diào)用代碼是:

uint16_t res = checksum(&g_data.data[0], g_data.len - 0);

這里,我猜測編譯器的行為就是,你有一個叫checksum的函數(shù),但我找不到它的原型,那么我就按“返回值是uint16_t類型,第一個參數(shù)是int型,第二個參數(shù)也是int型”來吧!

為何,gcc默認參數(shù)列表都是int類型?這是我的假想猜測,下面我們再論證,究竟是不是這樣?

有了這個假設(shè)之后,我們回到ARM匯編在函數(shù)調(diào)用時的參數(shù),這時R0應(yīng)該等于&g_data.data[0],R1應(yīng)該等于-1。

由于R0/R1都是32位的寄存器,在存儲數(shù)據(jù)的時候,無所謂有符號和無符號一說,且本問題R0沒有出現(xiàn)問題,我們僅討論R1。

這個時候R1的寄存器值,應(yīng)該是“-1 = 0xFFFFFFFF”,這個假設(shè)很關(guān)鍵,如果分析地很順利,那么這個for循環(huán)不停地循環(huán)下去,才可以有理論進行下去。

6 找到證據(jù)


既然上面我們發(fā)現(xiàn)了端倪,那么我們應(yīng)該進一步找到相關(guān)的證據(jù),證明我們的想法;同時,如果這個問題根源在于include頭文件,那么當我們添加了頭文件之后,這個問題應(yīng)該不會再復現(xiàn)。我們來看下,究竟是不是這樣?

6.1 究竟是不是警告

由于我們的代碼實在太多警告了,就屬于那種 0 error N warnings 那種,屬于你需要找一個警告往往好費好大勁!

6b15b914-0ce3-11ed-ba43-dac502259ad0.png

經(jīng)過好一番檢索,果不其然,還真的報警告了,的確是“warning: implicit declaration of function ‘checksum’ [-Wimplicit-function-declaration]”!

6.2 盤根問底

看編譯器的行為,我們肯定是要看其對應(yīng)的匯編文件,這里有兩個地方需要看,一個是checksum函數(shù)的匯編,還有一個調(diào)用checksum函數(shù)附近的匯編。

我們一起看看:

/* checksum 函數(shù)的匯編代碼 */    .section    .text.checksum,"ax",%progbits    .align    1    .global    checksum    .code    16    .thumb_func    .type    checksum, %functionchecksum:.LFB4:    .loc 1 125 0    .cfi_startproc    @ args = 0, pretend = 0, frame = 0    @ frame_needed = 0, uses_anonymous_args = 0.LVL27:    push    {r4, r5, r6, lr}    .cfi_def_cfa_offset 16    .cfi_offset 4, -16    .cfi_offset 5, -12    .cfi_offset 6, -8    .cfi_offset 14, -4    .loc 1 125 0    movs    r4, r0    movs    r5, r1  // r1 -> r5 ,即 len的值存在r5中    .loc 1 129 0    movs    r2, r1    ldr    r0, .L29.LVL28:    bl    printf  //打印len的值.LVL29:    movs    r3, r4    .loc 1 127 0    movs    r0, #0    adds    r5, r4, r5.LVL30:.L26:    .loc 1 130 0 discriminator 1    cmp    r3, r5  //for循環(huán)里面的關(guān)鍵判斷,即 i < len    beq    .L28 // 退出for循環(huán)    .loc 1 131 0 discriminator 3 //下面就是for循環(huán)的循環(huán)執(zhí)行體    ldrb    r2, [r3]    adds    r3, r3, #1.LVL31:    adds    r0, r0, r2.LVL32:    lsls    r0, r0, #16    lsrs    r0, r0, #16.LVL33:    b    .L26.LVL34:.L28:    .loc 1 136 0    @ sp needed.LVL35:    pop    {r4, r5, r6, pc}.L30:    .align    2.L29:    .word    .LC12    .cfi_endproc.LFE4:    .size    checksum, .-checksum

由它的匯編代碼可知,for循環(huán)執(zhí)行多少次,關(guān)鍵在于r5寄存器的值,也就是len的值。

注意在匯編代碼這里,是看不到r5是uint8_t還是uint32_t的,它僅僅是一個32位的寄存器。

    .section    .text.verify_checksum,"ax",%progbits    .align    1    .global    verify_checksum    .code    16    .thumb_func    .type    verify_checksum, %functionverify_checksum:.LFB5:    .loc 1 81 0    .cfi_startproc    @ args = 0, pretend = 0, frame = 0    @ frame_needed = 0, uses_anonymous_args = 0.LVL17:    push    {r4, lr}    .cfi_def_cfa_offset 8    .cfi_offset 4, -8    .cfi_offset 14, -4    .loc 1 83 0    ldr    r4, .L20    .loc 1 91 0    @ sp needed    .loc 1 83 0    movs    r0, r4   //r0存儲結(jié)構(gòu)體g_data的地址    ldrb    r1, [r4] //將g_data的第一個字節(jié),即g_data.len賦值為r1    adds    r0, r0, #34 //r0的地址偏移34個字節(jié),即偏移到g_data.data的位置;    subs    r1, r1, #1  //關(guān)鍵的一步:r1 = r1 - 1 由于我們復現(xiàn)問題的時候,g_data.len是為0的,所以此時r1的值就是0xFFFFFFFF    bl    checksum    //調(diào)用checksum函數(shù),第1-2個入?yún)ⅲ謩e是r0和r1.LVL18:    .loc 1 84 0    adds    r4, r4, #160    .loc 1 89 0    ldrb    r3, [r4]    lsls    r0, r0, #24.LVL19:    lsrs    r0, r0, #24    subs    r0, r0, r3    .loc 1 91 0    pop    {r4, pc}.L21:    .align    2.L20:    .word    .LANCHOR4    .cfi_endproc.LFE5:    .size    verify_checksum, .-verify_checksum

了解匯編知識的,看到上面的匯編代碼,結(jié)合checksum函數(shù)的匯編代碼,就應(yīng)該明白,我前面的假設(shè)成立了,但len傳入到checksum函數(shù)時,它的值真的是0xFFFFFFFF,而使用%u打印出來,就是4294967295。

到此,罪魁禍首其實已經(jīng)找到了,與其說是編譯器的無故優(yōu)化,倒不如說是程序猿寫代碼不嚴謹,沒有正確處理掉這個編譯警告。

6.3 解除風險

既然找到了問題根源,那么我們嘗試下解除這個風險。

方法其實也很簡單,直接需要在調(diào)用checksum函數(shù)的1.c中,include一下checksum函數(shù)所在的頭文件即可。

添加之后,我們看下發(fā)生的變化,很顯然,checksum函數(shù)的匯編代碼肯定是沒有任何不變的,應(yīng)該它壓根沒有改;

而調(diào)用checksum的匯編就發(fā)生了些許的變化,同時編譯輸出的地方,那個編譯警告也都消失了。

* 添加頭文件之后的匯編代碼 */        .section    .text.verify_checksum,"ax",%progbits    .align    1    .global    verify_checksum    .code    16    .thumb_func    .type    verify_checksum, %functionverify_checksum:.LFB5:    .loc 1 81 0    .cfi_startproc    @ args = 0, pretend = 0, frame = 0    @ frame_needed = 0, uses_anonymous_args = 0.LVL17:    push    {r4, lr}    .cfi_def_cfa_offset 8    .cfi_offset 4, -8    .cfi_offset 14, -4    .loc 1 83 0    ldr    r4, .L20    .loc 1 91 0    @ sp needed    .loc 1 83 0    movs    r0, r4    ldrb    r1, [r4]    adds    r0, r0, #34    subs    r1, r1, #1   //r1寄存器的一樣的操作 r1 = r1 - 1    lsls    r1, r1, #24  //關(guān)鍵改變!??!r1 = r1 * (2的24次冪),也就是算術(shù)左移24位    lsrs    r1, r1, #24  //關(guān)鍵改變?。。1 = r1 / (2的24次冪),也就是算術(shù)右移24位    bl    checksum.LVL18:    .loc 1 84 0    adds    r4, r4, #160    .loc 1 89 0    ldrb    r3, [r4]    lsls    r0, r0, #24.LVL19:    lsrs    r0, r0, #24    subs    r0, r0, r3    .loc 1 91 0    pop    {r4, pc}.L21:    .align    2.L20:    .word    .LANCHOR4    .cfi_endproc.LFE5:    .size    verify_checksum, .-verify_checksum

為了好對比,我直接使用對比工具貼圖上來看下:

6b30f22e-0ce3-11ed-ba43-dac502259ad0.png

查了下多出來的這兩條指令:lsls和lsrs,參考這里。

一個是算術(shù)左移24位,一個是算術(shù)右移24位,倒來倒去,無非就是把高24位給情況,這樣-1的值傳入checksum的時候,就只有0x000000FF了,而不是0xFFFFFFFF。

這樣就把uint8_t len拉回正常的邏輯了,自然也就不會出現(xiàn)之前的for循環(huán)一直退不出來了。

7 擴展延伸


上面我提及的場景對應(yīng)的是ARM平臺的,由于我們的代碼是跨平臺的,支持RISC-V架構(gòu),X86架構(gòu)等等。

7.1 RISC-V架構(gòu)

所以我們來對比看下RISC-V架構(gòu)下的情況:

這么看,RISC-V的處理也是夠粗暴的,一個addi指令,把高24位去掉就完事了!??!

7.2 80x86架構(gòu)

我push了一個簡易的工程代碼到github,以便于重現(xiàn)此問題,感興趣的可以看這里。

很遺憾的是,在80x86上竟然沒有復現(xiàn)此問題。

代碼的核心差別就是是否include 2.h:

6b4c960a-0ce3-11ed-ba43-dac502259ad0.png

匯編代碼確實有差異:

6b7bc164-0ce3-11ed-ba43-dac502259ad0.png

但是跑出來的效果確實一樣的:

6ba49e40-0ce3-11ed-ba43-dac502259ad0.png

總結(jié)下沒有復現(xiàn)問題的原因,可能是:

編譯選項沒有使用正確?

80x86編譯器更懂事?更能知道如何合理編譯代碼?

還有未知的編譯特性未了解到?

7.3 其他架構(gòu)

感興趣的可以在其他平臺上驗證下,是否有類似的問題,歡迎討論。

8 經(jīng)驗總結(jié)


請?zhí)嵘愕拇a編譯嚴謹性,如果是gcc編譯器,-Wall -Werror -Os是最低要求;

談優(yōu)化代碼前,請close掉你的代碼編譯異常,先達到 0 error 0 warning 再說;

請重視warning: implicit declaration of function這個編譯警告;

如果使用gcc編譯器,不提示任何編譯警告和錯誤,并不代表編譯器沒有告訴你,也許是你使用-w選項編譯了輸出,你僅僅是在自欺欺人而已;

老老實實在調(diào)用函數(shù)前申明你的函數(shù),或者包含其對應(yīng)的頭文件,有時候編譯器的默認行文不見得就可靠;

代碼細節(jié)很重要,真的是細節(jié)決定成敗;

不放過一絲可能性,作為一個攻城獅,這點專研精神需要時刻掛在心里;

大膽假設(shè),小心求證,亙古不變的方法論。

審核編輯:湯梓紅


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

    關(guān)注

    2

    文章

    214

    瀏覽量

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

    關(guān)注

    30

    文章

    4788

    瀏覽量

    68628

原文標題:【匯編實戰(zhàn)開發(fā)筆記】從匯編代碼中找出一段普通的for循環(huán)變成“死循環(huán)”的根本原因

文章出處:【微信號:RTThread,微信公眾號:RTThread物聯(lián)網(wǎng)操作系統(tǒng)】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。

收藏 人收藏

    評論

    相關(guān)推薦

    使用匯編知識排查疑難問題方法

    那么,本篇文章,我將再介紹一個使用匯編知識排查疑難問題方法,希望對大家有所幫助。
    發(fā)表于 07-27 10:31 ?675次閱讀

    如何使用內(nèi)聯(lián)函數(shù)法調(diào)用匯編函數(shù)?

    從C/C++中調(diào)用匯編代碼中的函數(shù)使用內(nèi)聯(lián)函數(shù)法調(diào)用匯編函數(shù)從C/C++中調(diào)用匯編代碼中的變量或者常量
    發(fā)表于 04-02 06:57

    為什么要用匯編語言寫程序

    我們這學期開了單片機的課,不知道為什么我們要用匯編語言寫程序,感覺匯編程序真的挺難寫的,所以把實驗記錄下來把。如果沒有學過匯編的小伙伴建議先去熟悉一下簡單的匯編指令,之前簡單的實驗我就
    發(fā)表于 11-10 08:32

    飛控中的疑難問題分享

    飛控中的疑難問題(一)最近在用無名創(chuàng)新的飛控來進行二次開發(fā)。遇到了一些較為奇怪的問題,部分已經(jīng)解決,但還是有一部分問題較為復雜,特此和大家分享一下。希望大家可以分享、互相幫助解決遇到的問題。(一
    發(fā)表于 01-13 07:31

    如何有效地排查內(nèi)存泄露的疑難問題

    。通過本文的閱讀,你將可以了解到以下幾部分核心內(nèi)容:一種業(yè)內(nèi)常見常用的【內(nèi)存管理】方案介紹;判斷【內(nèi)存泄露】的簡單方法;如何通過鉤子操作替換原生的內(nèi)存操作接口;如果通過編譯器的一些特殊功能,縮減排查方案
    發(fā)表于 09-01 14:47

    如何在C程序中使用匯編

    怎樣在C程序中使用匯編,如何在C程序中使用匯編:方法一:在每個匯編語句前加asm即可。如:void reset_data(void) { asm mov r0,#0dfh asm
    發(fā)表于 09-23 23:43 ?55次下載

    視頻會議系統(tǒng)中常見的疑難問題解答

    視頻會議系統(tǒng)中常見的疑難問題解答 1.我可以同時使用兩支相同頻率的話筒嗎   不能同時使用。無線傳輸不能在空中“相混合”,即
    發(fā)表于 02-21 09:08 ?1074次閱讀

    電腦入門與提高疑難排解大全

    《電腦入門與提高疑難排解大全》主要針對電腦用戶在使用電腦的過程中經(jīng)常遇到的各種疑難問題,從實用、便捷的角度出發(fā),講述具體的應(yīng)對方法及處理技巧。內(nèi)容主要包括操作系統(tǒng)
    發(fā)表于 01-17 17:12 ?81次下載

    模擬電子疑難問題解惑系列(三):成為熟練的電子工程師

    前面我們?yōu)榇蠹铱偨Y(jié)了模擬電子學習中經(jīng)常遇到的系列疑難問題的總結(jié),今天為大家?guī)砹四M電子問題解惑系列三,供大家學習
    發(fā)表于 11-29 18:03 ?3385次閱讀

    模擬電子疑難問題解惑系列(四):濾波器、振蕩器

    電子發(fā)燒友網(wǎng)訊:前面我們?yōu)榇蠹規(guī)砹四M電子學習中經(jīng)常遇到的系列疑難問題的總結(jié),今天為大家?guī)砹四M電子問題解惑系列四,供大家學習。
    發(fā)表于 12-17 11:33 ?4893次閱讀

    關(guān)于高速AD/DAC測量及設(shè)計中82個疑難問題的解答

    本文首先介紹了ADC的參數(shù)、測試方案及對測試結(jié)果進行了分析,其次介紹了DAC參數(shù)、測試方案及結(jié)果分析,其次對AD/DA轉(zhuǎn)換設(shè)計中82個疑難問題解答。
    發(fā)表于 04-20 16:24 ?1.5w次閱讀
    關(guān)于高速AD/DAC測量及設(shè)計中82個<b class='flag-5'>疑難問題</b>的解答

    51單片機C語言調(diào)用匯編子程序的簡便方法程序和工程文件免費下載

    本文檔的主要內(nèi)容詳細介紹的是51單片機C語言調(diào)用匯編子程序的簡便方法程序和工程文件免費下載。
    發(fā)表于 10-10 08:00 ?3次下載
    51單片機C語言調(diào)<b class='flag-5'>用匯編</b>子程序的簡便<b class='flag-5'>方法</b>程序和工程文件免費下載

    單片機使用匯編開發(fā)的簡單介紹

    匯編語言(Assembly Language)是一種用于電子計算機、微處理器、微控制器或其它可編程器件的低級語言。在單片機出現(xiàn)之初,由于性能限制,都是使用匯編進行開發(fā)。隨著技術(shù)的發(fā)展,制程工藝的提升
    發(fā)表于 11-13 19:06 ?13次下載
    單片機使<b class='flag-5'>用匯編</b>開發(fā)的簡單介紹

    在C中使用匯編程序的原因是?

    當使用匯編語言時,我們經(jīng)常將它使用在代碼中的一小部分上。有兩種使用匯編語言的方法:在C中調(diào)用匯編子程序或內(nèi)嵌匯編。內(nèi)嵌
    的頭像 發(fā)表于 04-04 15:00 ?957次閱讀
    在C中使<b class='flag-5'>用匯編</b>程序的原因是?

    21個最常見晶振應(yīng)用疑難問題及解答

    21個最常見晶振應(yīng)用疑難問題及解答
    的頭像 發(fā)表于 06-10 16:56 ?1602次閱讀