訂閱
糾錯(cuò)
加入自媒體

如何通過asm關(guān)鍵字嵌入?yún)R編語言代碼?

4. test5.c 聲明改動(dòng)的寄存器

在 test4.c 中,我們沒有聲明改動(dòng)的寄存器,所以編譯器可以任意選擇使用哪些寄存器。從生成的匯編代碼 test4.s 中可以看到,gcc 使用了  %edx 寄存器。

那么我們來測試一下:告訴 gcc 不要使用 %edx 寄存器。

#include <stdio.h>
int main()

   int data1 = 1;
   int data2 = 2;
   int data3;
   asm("movl %%ebx, %%eax "
       "addl %%ecx, %%eax"
       : "=a"(data3)
       : "b"(data1),"c"(data2)
       : "%edx");
   printf("data3 = %d ", data3);
   return 0;

代碼中,asm 指令最后部分 "%edx" ,就是用來告訴 gcc 編譯器:在內(nèi)聯(lián)匯編代碼中,我們會(huì)使用到 %edx 寄存器,你就不要用它了。

生成匯編代碼指令:

gcc -m32 -S -o test5.s test5.c

來看一下生成的匯編代碼 test5.s:

movl $1, -20(%ebp)
movl $2, -16(%ebp)
movl -20(%ebp), %eax
movl -16(%ebp), %ecx
movl %eax, %ebx
#APP
# 10 "test5.c" 1
movl %ebx, %eax
addl %ecx, %eax
# 0 "" 2
#NO_APP
movl %eax, -12(%ebp)

可以看到,在內(nèi)聯(lián)匯編代碼之前,gcc 沒有選擇使用寄存器 %edx。

三、使用占位符來代替寄存器名稱

在上面的示例中,只使用了 2 個(gè)寄存器來操作 2 個(gè)局部變量,如果操作數(shù)有很多,那么在內(nèi)聯(lián)匯編代碼中去寫每個(gè)寄存器的名稱,就顯得很不方便。

因此,擴(kuò)展 asm 格式為我們提供了另一種偷懶的方法,來使用輸出和輸入操作數(shù)列表中的寄存器:占位符!

占位符有點(diǎn)類似于批處理腳本中,利用 2...來引用輸入?yún)?shù)一樣,內(nèi)聯(lián)匯編代碼中的占位符,從輸出操作數(shù)列表中的寄存器開始從 0 編號(hào),一直編號(hào)到輸入操作數(shù)列表中的所有寄存器。

還是看例子比較直接!

1. test6.c 使用占位符代替寄存器#include <stdio.h>
int main()

   int data1 = 1;
   int data2 = 2;
   int data3;
   asm("addl %1, %2 "
       "movl %2, %0"
       : "=r"(data3)
       : "r"(data1),"r"(data2));
   printf("data3 = %d ", data3);
   return 0;

代碼說明:

輸出操作數(shù)列表"=r"(data3):約束使用字符 r, 也就是說不指定寄存器,由編譯器來選擇使用哪個(gè)寄存器來存儲(chǔ)結(jié)果,最后復(fù)制到局部變量 data3中;

輸入操作數(shù)列表"r"(data1),"r"(data2):約束字符r, 不指定寄存器,由編譯器來選擇使用哪 2 個(gè)寄存器來接收局部變量 data1 和 data2;

輸出操作數(shù)列表中只需要一個(gè)寄存器,因此在內(nèi)聯(lián)匯編代碼中的 %0 就代表這個(gè)寄存器(即:從 0 開始計(jì)數(shù));

輸入操作數(shù)列表中有 2 個(gè)寄存器,因此在內(nèi)聯(lián)匯編代碼中的 %1 和 %2 就代表這 2 個(gè)寄存器(即:從輸出操作數(shù)列表的最后一個(gè)寄存器開始順序計(jì)數(shù));

生成匯編代碼指令:

gcc -m32 -S -o test6.s test6.c

匯編代碼如下 test6.s:

movl $1, -20(%ebp)
movl $2, -16(%ebp)
movl -20(%ebp), %eax
movl -16(%ebp), %edx
#APP
# 10 "test6.c" 1
addl %eax, %edx
movl %edx, %eax
# 0 "" 2
#NO_APP
movl %eax, -12(%ebp)

可以看到,gcc 編譯器選擇了 %eax 來存儲(chǔ)局部變量 data1,%edx 來存儲(chǔ)局部變量 data2 ,然后操作結(jié)果也存儲(chǔ)在 %eax 寄存器中。

是不是感覺這樣操作就方便多了?不用我們來指定使用哪些寄存器,直接交給編譯器來選擇。

在內(nèi)聯(lián)匯編代碼中,使用 %0、%1 、%2 這樣的占位符來使用寄存器。

別急,如果您覺得使用編號(hào)還是麻煩,容易出錯(cuò),還有另一個(gè)更方便的操作:擴(kuò)展 asm 格式還允許給這些占位符重命名,也就是給每一個(gè)寄存器起一個(gè)別名,然后在內(nèi)聯(lián)匯編代碼中使用別名來操作寄存器。

還是看代碼!

2. test7.c 給寄存器起別名#include <stdio.h>
int main()

   int data1 = 1;
   int data2 = 2;
   int data3;
   asm("addl %[v1], %[v2] "
       "movl %[v2], %[v3]"
       : [v3]"=r"(data3)
       : [v1]"r"(data1),[v2]"r"(data2));
   printf("data3 = %d ", data3);
   return 0;

代碼說明:

輸出操作數(shù)列表:給寄存器(gcc 編譯器選擇的)取了一個(gè)別名 v3;

輸入操作數(shù)列表:給寄存器(gcc 編譯器選擇的)取了一個(gè)別名 v1 和 v2;

起立別名之后,在內(nèi)聯(lián)匯編代碼中就可以直接使用這些別名( %[v1], %[v2],  %[v3])來操作數(shù)據(jù)了。

生成匯編代碼指令:

gcc -m32 -S -o test7.s test7.c

再來看一下生成的匯編代碼 test7.s:

movl $1, -20(%ebp)
movl $2, -16(%ebp)
movl -20(%ebp), %eax
movl -16(%ebp), %edx
#APP
# 10 "test7.c" 1
addl %eax, %edx
movl %edx, %eax
# 0 "" 2
#NO_APP
movl %eax, -12(%ebp)

這部分的匯編代碼與 test6.s 中完全一樣!

四、使用內(nèi)存位置

在以上的示例中,輸出操作數(shù)列表和輸入操作數(shù)列表部分,使用的都是寄存器(約束字符:a, b, c, d, r等等)。

我們可以指定使用哪個(gè)寄存器,也可以交給編譯器來選擇使用哪些寄存器,通過寄存器來操作數(shù)據(jù),速度會(huì)更快一些。

如果我們愿意的話,也可以直接使用變量的內(nèi)存地址來操作變量,此時(shí)就需要使用約束字符 m。

1. test8.c 使用內(nèi)存地址來操作數(shù)據(jù)#include <stdio.h>
int main()

   int data1 = 1;
   int data2 = 2;
   int data3;
   asm("movl %1, %%eax "
       "addl %2, %%eax "
       "movl %%eax, %0"
       : "=m"(data3)
       : "m"(data1),"m"(data2));
   printf("data3 = %d ", data3);
   return 0;

代碼說明:

輸出操作數(shù)列表 "=m"(data3):直接使用變量 data3 的內(nèi)存地址;

輸入操作數(shù)列表 "m"(data1),"m"(data2):直接使用變量 data1, data2 的內(nèi)存地址;

在內(nèi)聯(lián)匯編代碼中,因?yàn)樾枰M(jìn)行相加計(jì)算,因此需要使用一個(gè)寄存器(%eax),計(jì)算這個(gè)環(huán)節(jié)是肯定需要寄存器的。

在操作那些內(nèi)存地址中的數(shù)據(jù)時(shí),使用的仍然是按順序編號(hào)的占位符。

生成匯編代碼指令:

gcc -m32 -S -o test8.s test8.c

生成的匯編代碼如下 test8.s:

movl $1, -24(%ebp)
movl $2, -20(%ebp)
#APP
# 10 "test8.c" 1
movl -24(%ebp), %eax
addl -20(%ebp), %eax
movl %eax, -16(%ebp)
# 0 "" 2
#NO_APP
movl -16(%ebp), %eax

可以看到:在進(jìn)入內(nèi)聯(lián)匯編代碼之前,把 data1 和 data2 的值放在了棧中,然后直接把棧中的數(shù)據(jù)與寄存器 %eax 進(jìn)行操作,最后再把操作結(jié)果(%eax),復(fù)制到棧中 data3 的位置(-16(%ebp))。

五、總結(jié)

通過以上 8 個(gè)示例,我們把內(nèi)聯(lián)匯編代碼中的關(guān)鍵語法規(guī)則進(jìn)行了講解,有了這個(gè)基礎(chǔ),就可以在內(nèi)聯(lián)匯編代碼中編寫更加復(fù)雜的指令了。

希望以上內(nèi)容對您能有所幫助!謝謝!

<上一頁  1  2  3  
聲明: 本文由入駐維科號(hào)的作者撰寫,觀點(diǎn)僅代表作者本人,不代表OFweek立場。如有侵權(quán)或其他問題,請聯(lián)系舉報(bào)。

發(fā)表評論

0條評論,0人參與

請輸入評論內(nèi)容...

請輸入評論/評論長度6~500個(gè)字

您提交的評論過于頻繁,請輸入驗(yàn)證碼繼續(xù)

  • 看不清,點(diǎn)擊換一張  刷新

暫無評論

暫無評論

人工智能 獵頭職位 更多
掃碼關(guān)注公眾號(hào)
OFweek人工智能網(wǎng)
獲取更多精彩內(nèi)容
文章糾錯(cuò)
x
*文字標(biāo)題:
*糾錯(cuò)內(nèi)容:
聯(lián)系郵箱:
*驗(yàn) 證 碼:

粵公網(wǎng)安備 44030502002758號(hào)