Lab2 : ARM指令 - 還是實(shí)驗(yàn)報(bào)告誒

連接圖

由于在實(shí)驗(yàn)一中已經(jīng)配置好Acadia的網(wǎng)絡(luò)設(shè)置,所以這次直接插上網(wǎng)線,使用ssh進(jìn)行遠(yuǎn)程登錄。

實(shí)驗(yàn)?zāi)康?/h1>
  1. 深入理解ARM指令和Thumb指令的區(qū)別和編譯選項(xiàng);
  2. 深入理解某些特殊的ARM指令,理解如何編寫C代碼來得到這些指令;
  3. 深入理解ARM的BL指令和C函數(shù)的堆棧保護(hù);
  4. 深入理解如何實(shí)現(xiàn)C和匯編函數(shù)的互相調(diào)用。

實(shí)驗(yàn)步驟

1. arm與thumb指令集

經(jīng)過查閱gcc的編譯選項(xiàng),找到了編譯為arm的編譯指令-marm以及編譯為thumb的編譯指令-mthumb。

使用程序驗(yàn)證如下,

root@Acadia:~/tmp/hello# cat hello.c 
#include<stdio.h>
#include<time.h>

int hello(){
    printf("Hello Acadia! At %lu\n", time(NULL));
}

int main(){
    hello();
    return 0;
}
root@Acadia:~/tmp/hello# gcc hello.c -marm -o arm.out
root@Acadia:~/tmp/hello# gcc hello.c -mthumb -o thumb.out
root@Acadia:~/tmp/hello# ll *.out
-rwxr-xr-x 1 root root 7856 Mar 26 10:44 arm.out*
-rwxr-xr-x 1 root root 7856 Mar 26 10:45 thumb.out*
root@Acadia:~/tmp/hello# echo disas hello | gdb arm.out > arm_hello.s
root@Acadia:~/tmp/hello# echo disas hello | gdb thumb.out > thumb_hello.s
root@Acadia:~/tmp/hello# vimdiff thumb_hello.s arm_hello.s 
vimdiff兩個(gè)文件的結(jié)果

首先,測(cè)試程序中調(diào)用了內(nèi)部的函數(shù)hello,同時(shí)也調(diào)用了外部函數(shù)printf以及time。而通過gcc編譯后,兩個(gè)文件大小無差異,故不能從中直接獲知是否為不同指令集的文件。

此時(shí),需要使用gdb反匯編hello,進(jìn)行比較后方可以看出生成程序的不同。測(cè)試命令中使用echo是為了命令的清晰,并且容易使用輸出重定向比較結(jié)果。

程序之間最大的區(qū)別是在函數(shù)主體部分。主要區(qū)別有幾點(diǎn):

  1. thumb的指令地址一次只增加2,即每條指令只占16位。相對(duì)的,arm每個(gè)地址增加4,每條指令占位32位。
  2. thumb的指令集訪問寄存器 r8 ~ r15 受到一定限制,所以在thumb程序中可以看出,使用的是r7寄存器代替了arm程序中的r11寄存器。
  3. thumb程序可以調(diào)用arm指令集的函數(shù)庫,但是地址跳轉(zhuǎn)需要使用blx進(jìn)行指令集轉(zhuǎn)換。而arm程序直接使用bl跳轉(zhuǎn)即可。

2. arm上的條件命令

root@Acadia:~/tmp/if# cat if.c 
#include <stdio.h>

int main(){
    int n, m;
    scanf("%d", &m);
    if (m > 32){
        n = 5;
    }else{
        n = 1;
    }
    printf("%d\n", n);
    return 0;
}
root@Acadia:~/tmp/if# gcc if.c -marm -o if.out -O2
...
root@Acadia:~/tmp/if# echo disas main | gdb if.out > if_main.s
root@Acadia:~/tmp/if# cat if_main.s 
...
(gdb) Dump of assembler code for function main:
   0x00008374 <+0>: push    {lr}        ; (str lr, [sp, #-4]!)
   0x00008378 <+4>: sub sp, sp, #12
   0x0000837c <+8>: add r1, sp, #4
   0x00008380 <+12>:    ldr r0, [pc, #40]   ; 0x83b0 <main+60> 
   0x00008384 <+16>:    bl  0x835c <__isoc99_scanf>   ; r0 = "%d", r1 = &m, 調(diào)用scanf
   0x00008388 <+20>:    ldr r2, [sp, #4]
   0x0000838c <+24>:    mov r0, #1
   0x00008390 <+28>:    ldr r1, [pc, #28]   ; 0x83b4 <main+64>
   0x00008394 <+32>:    cmp r2, #32  ; 執(zhí)行if命令,使用cmp后條件賦值
   0x00008398 <+36>:    movle   r2, r0
   0x0000839c <+40>:    movgt   r2, #5
   0x000083a0 <+44>:    bl  0x8350 <__printf_chk> ; r2直接傳入printf進(jìn)行輸出
   0x000083a4 <+48>:    mov r0, #0
   0x000083a8 <+52>:    add sp, sp, #12
   0x000083ac <+56>:    pop {pc}
   0x000083b0 <+60>:    andeq   r8, r0, r4, lsl #9
   0x000083b4 <+64>:    andeq   r8, r0, r8, lsl #9
End of assembler dump.
(gdb) quit

可以看出,if指令被編譯成了3條指令,即cmp, movle, movgt分別用以條件賦值。其中,mov為基礎(chǔ)指令,后方跟著的le表示小于等于,gt表示大于。

3. 設(shè)計(jì) C 的代碼場(chǎng)景,觀察是否產(chǎn)生了寄存器移位尋址

root@Acadia:~/tmp/shift# cat shift.c 
#include <stdio.h>

int main(){
    int a;
    scanf("%d", &a);
    a = a * 9;
    printf("%d\n", a);
    return 0;
}
root@Acadia:~/tmp/shift# gcc shift.c -marm -O2 -o shift.out
root@Acadia:~/tmp/shift# echo disas main | gdb shift.out > shift_main.s
root@Acadia:~/tmp/shift# cat shift_main.s 
...
(gdb) Dump of assembler code for function main:
   0x00008374 <+0>: push    {lr}        ; (str lr, [sp, #-4]!)
   0x00008378 <+4>: sub sp, sp, #12
   0x0000837c <+8>: add r1, sp, #4
   0x00008380 <+12>:    movw    r0, #33920  ; 0x8480
   0x00008384 <+16>:    movt    r0, #0
   0x00008388 <+20>:    bl  0x835c <__isoc99_scanf>
   0x0000838c <+24>:    ldr r2, [sp, #4]
   0x00008390 <+28>:    mov r0, #1
   0x00008394 <+32>:    movw    r1, #33924  ; 0x8484
   0x00008398 <+36>:    movt    r1, #0
   0x0000839c <+40>:    add r2, r2, r2, lsl #3    ; a = a * 9 = a << 3 + a
   0x000083a0 <+44>:    str r2, [sp, #4]
   0x000083a4 <+48>:    bl  0x8350 <__printf_chk>
   0x000083a8 <+52>:    mov r0, #0
   0x000083ac <+56>:    add sp, sp, #12
   0x000083b0 <+60>:    pop {pc}

由于9在二進(jìn)制中表示為1001,所以使用移位加法的方式能夠很好得避過消耗較大的乘法操作。此時(shí)add中使用的就是寄存器移位尋址功能。

4. 設(shè)計(jì) C 的代碼場(chǎng)景,觀察一個(gè)復(fù)雜的 32 位數(shù)是如何裝載到寄存器的

root@Acadia:~/tmp/loadword# cat loadword.c 
#include <stdio.h>

int main(){
    int a = 2123456789;
    printf("%d\n", a);
    return 0;
}
root@Acadia:~/tmp/loadword# gcc loadword.c -O2 -marm -o loadword.out
root@Acadia:~/tmp/loadword# echo disas main | gdb loadword.out > loadword_main.s
root@Acadia:~/tmp/loadword# cat loadword_main.s 
...
(gdb) Dump of assembler code for function main:
   0x00008320 <+0>: push    {r3, lr}
   0x00008324 <+4>: mov r0, #1
   0x00008328 <+8>: movw    r1, #33808  ; 0x8410
   0x0000832c <+12>:    movw    r2, #24853  ; 0x6115 ; r2 = 0x00006115
   0x00008330 <+16>:    movt     r1, #0
   0x00008334 <+20>:    movt     r2, #32401 ; 0x7e91 ; r2 = 0x7e916115
   0x00008338 <+24>:    bl  0x8308 <__printf_chk>
   0x0000833c <+28>:    mov r0, #0
   0x00008340 <+32>:    pop {r3, pc}

使用計(jì)算器,可以求得2123456789 = 0x7E916115。所以,代碼中的movw與movt分別將低位與高位載入寄存器中進(jìn)行運(yùn)算。而r1的movw主要是提供了printf的第一個(gè)操作數(shù)即格式字符串的位置。

5. 寫一個(gè) C 的多重函數(shù)調(diào)用的程序,觀察和分析

  1. 調(diào)用時(shí)的返回地址在哪里?
  2. 傳入的參數(shù)在哪里?
  3. 本地變量的堆棧分配是如何做的?
  4. 寄存器是 caller 保存還是 callee 保存?是全體保存還是部分保存?
root@Acadia:~/tmp/func# cat func.c 
#include <stdio.h>

int fibo(int n){
    if (n <= 1) return 1;
    return fibo(n-2) + fibo(n-1);
}

int main(){
    int a;
    scanf("%d", &a);
    a = fibo(a);
    printf("%d\n", a);
    return 0;
}
root@Acadia:~/tmp/func# vim func.c 
root@Acadia:~/tmp/func# gcc func.c -O2 -marm -o func.out
...
root@Acadia:~/tmp/func# echo disas main | gdb func.out > func_main.s
root@Acadia:~/tmp/func# echo disas fibo | gdb func.out > func_fibo.s
root@Acadia:~/tmp/func# cat func_fibo.s
...
(gdb) Dump of assembler code for function fibo:
   0x00008434 <+0>: cmp r0, #1
   0x00008438 <+4>: push    {r3, r4, r5, lr}
   0x0000843c <+8>: ble 0x8468 <fibo+52> ; n <= 1 直接退出并返回1
   0x00008440 <+12>:    sub r4, r0, #2
   0x00008444 <+16>:    mov r5, #0
   0x00008448 <+20>:    mov r0, r4
   0x0000844c <+24>:    sub r4, r4, #1  ; r4 = r0 - 1 即 r4 = n - 1
   0x00008450 <+28>:    bl  0x8434 <fibo>
   0x00008454 <+32>:    cmn r4, #1  ; r4 -= 1, 即r4 = n - 2
   0x00008458 <+36>:    add r5, r5, r0
   0x0000845c <+40>:    bne 0x8448 <fibo+20> ; 尾遞歸優(yōu)化
   0x00008460 <+44>:    add r0, r5, #1
   0x00008464 <+48>:    pop {r3, r4, r5, pc}
   0x00008468 <+52>:    mov r0, #1
   0x0000846c <+56>:    pop {r3, r4, r5, pc}
root@Acadia:~/tmp/func# cat func_main.s
...
(gdb) Dump of assembler code for function main:
   0x00008374 <+0>: push    {lr}        ; (str lr, [sp, #-4]!)
   0x00008378 <+4>: sub sp, sp, #12
   0x0000837c <+8>: add r1, sp, #4
   0x00008380 <+12>:    movw    r0, #33988  ; 0x84c4
   0x00008384 <+16>:    movt    r0, #0
   0x00008388 <+20>:    bl  0x835c <__isoc99_scanf>
   0x0000838c <+24>:    ldr r0, [sp, #4]
   0x00008390 <+28>:    bl  0x8434 <fibo> ; 調(diào)用fibo函數(shù)
   0x00008394 <+32>:    movw    r1, #33992  ; 0x84c8
   0x00008398 <+36>:    movt    r1, #0
   0x0000839c <+40>:    mov r3, r0
   0x000083a0 <+44>:    mov r0, #1
   0x000083a4 <+48>:    mov r2, r3
   0x000083a8 <+52>:    str r3, [sp, #4]
   0x000083ac <+56>:    bl  0x8350 <__printf_chk>
   0x000083b0 <+60>:    mov r0, #0
   0x000083b4 <+64>:    add sp, sp, #12
   0x000083b8 <+68>:    pop {pc}
End of assembler dump.
(gdb) quit

a. 程序的返回地址在調(diào)用的時(shí)候默認(rèn)存入lr寄存器,而在函數(shù)內(nèi)為了保證返回結(jié)果的正確性,將其push進(jìn)堆棧中。而后在函數(shù)結(jié)束的時(shí)候,將其pop至pc寄存器實(shí)現(xiàn)跳轉(zhuǎn)返回。
b. 從fibo函數(shù)中看出,r0為函數(shù)的第一個(gè)參數(shù)。而在參數(shù)少于4個(gè)的時(shí)候,使用寄存器r0~r4傳遞參數(shù)。
c. 本地變量的堆棧分配使用push操作將原本的寄存器值存在堆棧中,當(dāng)返回時(shí)再pop出來。而從 push {lr} === str lr, [sp, #-4]可以看出,堆棧是自頂向下伸展的。
d. 寄存器的值是collee保存。因?yàn)橥獠空{(diào)用者不知道內(nèi)部函數(shù)所需要的寄存器,保存也就無從談起。
而函數(shù)內(nèi)部用到的所有寄存器均會(huì)被保存,因?yàn)楹瘮?shù)內(nèi)部并不知道外部會(huì)使用哪些寄存器。

6. MLA 是帶累加的乘法,嘗試要如何寫 C 的表達(dá)式能編譯得到 MLA 指令。

root@Acadia:~/tmp/mla# cat mla.c 
#include <stdio.h>

int main(){
    int a, tot;
    scanf("%d %d", &a, &tot);
    tot += a * a;
    printf("%d\n", tot);
    return 0;
}
root@Acadia:~/tmp/mla# gcc mla.c -marm -O2 -o mla.out
...
root@Acadia:~/tmp/mla# echo disas main | gdb mla.out > mla_main.s
root@Acadia:~/tmp/mla# cat mla_main.s 
...
(gdb) Dump of assembler code for function main:
   0x00008374 <+0>: push    {lr}        ; (str lr, [sp, #-4]!)
   0x00008378 <+4>: sub sp, sp, #12
   0x0000837c <+8>: add r2, sp, #4
   0x00008380 <+12>:    movw    r0, #33932  ; 0x848c
   0x00008384 <+16>:    mov r1, sp
   0x00008388 <+20>:    movt    r0, #0
   0x0000838c <+24>:    bl  0x835c <__isoc99_scanf>
   0x00008390 <+28>:    ldr r2, [sp, #4]
   0x00008394 <+32>:    ldr r3, [sp]
   0x00008398 <+36>:    mov r0, #1
   0x0000839c <+40>:    movw    r1, #33940  ; 0x8494
   0x000083a0 <+44>:    movt    r1, #0
   0x000083a4 <+48>:    mla r3, r3, r3, r2 ; tot += a * a => r3 = r3 * r3 + r2
   0x000083a8 <+52>:    mov r2, r3
   0x000083ac <+56>:    str r3, [sp, #4]
   0x000083b0 <+60>:    bl  0x8350 <__printf_chk>
   0x000083b4 <+64>:    mov r0, #0
   0x000083b8 <+68>:    add sp, sp, #12
   0x000083bc <+72>:    pop {pc}
End of assembler dump.
(gdb) quit

值得一提的是,在不使用-O2選項(xiàng)的情況下,編譯的結(jié)果并不含mla。由此可見,代碼優(yōu)化的一種方式是將程序指令盡可能的貼合CPU,手動(dòng)編寫功能的運(yùn)行效率較低。

7. BIC是對(duì)某一個(gè)比特清零的指令,嘗試要如何寫 C 的表達(dá)式能編譯得到 BIC 指令。

root@Acadia:~/tmp/bic# cat bic.c 
#include <stdio.h>
#include <string.h>

int main(){
    int a, b;
    scanf("%d %d", &a, &b);
    a &= ~b;
    printf("%d\n", a);
    return 0;
}
root@Acadia:~/tmp/bic# gcc bic.c -marm -O2 -o bic.out
...
root@Acadia:~/tmp/bic# echo disas main | gdb bic.out > bic_main.s
root@Acadia:~/tmp/bic# cat bic_main.s 
...
(gdb) Dump of assembler code for function main:
   0x00008374 <+0>: push    {lr}        ; (str lr, [sp, #-4]!)
   0x00008378 <+4>: sub sp, sp, #12
   0x0000837c <+8>: add r2, sp, #4
   0x00008380 <+12>:    movw    r0, #33928  ; 0x8488
   0x00008384 <+16>:    mov r1, sp
   0x00008388 <+20>:    movt    r0, #0
   0x0000838c <+24>:    bl  0x835c <__isoc99_scanf>
   0x00008390 <+28>:    ldr r3, [sp]
   0x00008394 <+32>:    ldr r2, [sp, #4]
   0x00008398 <+36>:    mov r0, #1
   0x0000839c <+40>:    movw    r1, #33936  ; 0x8490
   0x000083a0 <+44>:    movt    r1, #0
   0x000083a4 <+48>:    bic r2, r3, r2 ; a &= ~b
   0x000083a8 <+52>:    str r2, [sp]
   0x000083ac <+56>:    bl  0x8350 <__printf_chk>
   0x000083b0 <+60>:    mov r0, #0
   0x000083b4 <+64>:    add sp, sp, #12
   0x000083b8 <+68>:    pop {pc}
End of assembler dump.
(gdb) quit

bic指令將某些由標(biāo)示數(shù)指定的bit清零,其原理是通過將標(biāo)示數(shù)進(jìn)行取反后取and,即所有原本在標(biāo)示數(shù)中為1的位,經(jīng)過取反之后為0。通過and操作,將所有的0位覆蓋至目標(biāo),同時(shí)卻又不覆蓋其他bit的值。

8. 編寫一個(gè)匯編函數(shù)。

編寫要求:接受一個(gè)整數(shù)和一個(gè)指針做為輸入,指針?biāo)笐?yīng)為一個(gè)字符串,該匯編函數(shù)調(diào)用C語言的 printf()函數(shù)輸出這個(gè)字符串的前n個(gè)字符,n即為那個(gè)整數(shù)。在C語言寫的main()函數(shù)中調(diào)用并傳遞參數(shù)給這個(gè)匯編函數(shù) 來得到輸出。

root@Acadia:~/tmp/asm# cat cutprint.S 
.global cutprint
cutprint:
    push {R5, R6, R7, lr}
    MOV R5, R0
    MOV R6, R1
    MOV R7, #0 ; 首先,將r0, r1保存起來,同時(shí)將計(jì)數(shù)器r7置零
    CMP R7, R6
    BGE exit  ; 如果r7大于r6的話,直接返回,因?yàn)閞6必定是負(fù)數(shù)
begin:
    LDR R0, =char ; 由于函數(shù)返回值在r0的位置,所以每次調(diào)用玩函數(shù),"%c"就會(huì)被覆蓋,需要再載入一次
    LDR R1, [R5, R7] ; 根據(jù)字符串以及r7計(jì)數(shù)器的下標(biāo),載入字符
    CMP R1, #0 ; 如果遇到了c風(fēng)格字符串結(jié)尾'\0',直接跳出循環(huán)
    BEQ exit
    bl printf
    ADD R7, R7, #1 ; 計(jì)數(shù)器+1
    CMP R7, R6 
    BLT begin ;判斷是否循環(huán)結(jié)束,未結(jié)束則跳到begin進(jìn)行下一輪循環(huán)
exit:
    LDR R0, =newline ; 輸出結(jié)束換行
    bl printf
    MOV R0, R7 ; 把輸出的計(jì)數(shù)器作為返回值
    pop {R5, R6, R7, pc}

.data
    char: .asciz "%c"
    newline: .asciz "\n"
root@Acadia:~/tmp/asm# cat cutprint.c 
#include <stdio.h>

extern int cutprint(char*, int);

int main(){
    int a;
    char s[100];
    scanf("%s %d", s, &a);
    a = cutprint(s, a);
    printf("Print %d character.\n", a);
    return 0;
}
root@Acadia:~/tmp/asm# gcc cutprint.c cutprint.S -o cutprint -g -marm
root@Acadia:~/tmp/asm# ./cutprint
123456789 -1

Print 0 character.
root@Acadia:~/tmp/asm# ./cutprint
123456789 5
12345
Print 5 character.
root@Acadia:~/tmp/asm# ./cutprint
123456789 19999
123456789
Print 9 character.

其中,cutpinrt.s的程序邏輯類似如下:

int cutprint(cahr *s, int a){
    int i = 0;
    for (i; i<a; i++)
        printf(“%c”, s[i]);
    return i;
}

9. 編寫測(cè)試程序,測(cè)試ARM指令和Thumb指令的執(zhí)行效率

root@Acadia:~/tmp/speed# cat speed.c 
#include <stdio.h>

int fibo(int n){
    if (n <= 1) return 1;
    return fibo(n - 2) + fibo(n - 1);
}

int main(){
    int a;
    scanf("%d", &a);
    printf("%d\n", fibo(a));
    return 0;
}
root@Acadia:~/tmp/speed# gcc speed.c -marm -o arm.out
root@Acadia:~/tmp/speed# gcc speed.c -mthumb -o thumb.out
root@Acadia:~/tmp/speed# echo 40 > speed.in
root@Acadia:~/tmp/speed# time ./arm.out < speed.in
165580141

real    0m5.951s
user    0m5.940s
sys 0m0.000s
root@Acadia:~/tmp/speed# time ./thumb.out < speed.in
165580141

real    0m6.425s
user    0m6.400s
sys 0m0.010s
root@Acadia:~/tmp/speed# gcc speed.c -marm -o arm.out -O2
...
root@Acadia:~/tmp/speed# gcc speed.c -mthumb -o thumb.out -O2
...
root@Acadia:~/tmp/speed# time ./arm.out < speed.in
165580141

real    0m2.190s
user    0m2.180s
sys 0m0.000s
root@Acadia:~/tmp/speed# time ./thumb.out < speed.in
165580141

real    0m3.988s
user    0m3.990s
sys 0m0.000s
root@Acadia:~/tmp/speed# gcc speed.c -marm -o arm.out -O3
...
root@Acadia:~/tmp/speed# gcc speed.c -mthumb -o thumb.out -O3
...
root@Acadia:~/tmp/speed# time ./arm.out < speed.in
165580141

real    0m2.520s
user    0m2.510s
sys 0m0.000s
root@Acadia:~/tmp/speed# time ./thumb.out < speed.in
165580141

real    0m2.723s
user    0m2.710s
sys 0m0.010s

程序中,主要消耗時(shí)間的地方在于fibo(40)的遞歸調(diào)用,可以看出,thumb整體而言還是偏慢的,但是如果開了編譯優(yōu)化之后,差別不會(huì)過大。

10. 編寫測(cè)試程序,測(cè)試使用帶條件的ARM指令和不使用時(shí)的執(zhí)行效率。

root@Acadia:~/tmp/ifspeed# cat noif.S
.global add
add:
    CMP R0, #0
    ADD R0, R0, R1
    MOV pc, lr
root@Acadia:~/tmp/ifspeed# cat useif.S
.global add
add:
    CMP R0, #0
    ADDNE R0, R0, R1
    MOV pc, lr
root@Acadia:~/tmp/ifspeed# cat test.c
#include <stdio.h>

extern int add(int, int);

int main(){
    int a, b, i, times;
    scanf("%d %d %d", &a, &b, &times);
    for (i=0; i<times; i++)
        a = add(a, b);
    printf("%d\n", a);
    return 0;
}
root@Acadia:~/tmp/ifspeed# cat testif.in
1 2 20000000
root@Acadia:~/tmp/ifspeed# gcc useif.S test.c -o useif -g -marm -O2
...
root@Acadia:~/tmp/ifspeed# gcc noif.S test.c -o noif -g -marm -O2
...
root@Acadia:~/tmp/ifspeed# time ./useif < testif.in 
40000001

real    0m0.254s
user    0m0.240s
sys 0m0.010s
root@Acadia:~/tmp/ifspeed# time ./noif < testif.in 
40000001

real    0m0.259s
user    0m0.240s
sys 0m0.010s

經(jīng)過多次比對(duì),雖然時(shí)間略有波動(dòng)。但是總體而言,兩程序執(zhí)行時(shí)間基本沒有區(qū)別,即ADDNE與ADD的執(zhí)行基本沒有時(shí)間差。

參考資料

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 前言 本文翻譯自iOS Assembly Tutorial: Understanding ARM 翻譯的不對(duì)的地...
    桃紅宿雨閱讀 18,423評(píng)論 7 198
  • 這個(gè)實(shí)驗(yàn)的目的是深入理解ARM和Thumb指令的特點(diǎn),了解編譯選項(xiàng)對(duì)代碼產(chǎn)生的影響。 配合課程 ARM處理器 實(shí)驗(yàn)...
    小浪明閱讀 682評(píng)論 0 2
  • 原文地址:C語言函數(shù)調(diào)用棧(一)C語言函數(shù)調(diào)用棧(二) 0 引言 程序的執(zhí)行過程可看作連續(xù)的函數(shù)調(diào)用。當(dāng)一個(gè)函數(shù)執(zhí)...
    小豬啊嗚閱讀 4,972評(píng)論 1 19
  • 了解更多精彩內(nèi)容:http://114.55.4.207/live.php/l/r/13038 很長一段時(shí)間內(nèi),許...
    三生三世十里拉面閱讀 246評(píng)論 0 1
  • 作為新員工,我們要及時(shí)的給自己定位,不能好高騖遠(yuǎn),等我們有足夠的能力時(shí),我才有資格去提建議。
    濰坊泰華DDM店劉云閱讀 179評(píng)論 0 0

友情鏈接更多精彩內(nèi)容