C指針原理教程之C內(nèi)嵌匯編
內(nèi)聯(lián)匯編的重要性體現(xiàn)在它能夠靈活操作,而且可以使其輸出通過(guò) C 變量顯示出來(lái)。因?yàn)樗哂羞@種能力,所以 "asm" 可以用作匯編指令和包含它的 C 程序之間的接口。簡(jiǎn)單得說(shuō),內(nèi)聯(lián)匯編,就是可以讓程序員在C語(yǔ)言中直接嵌入?yún)R編代碼,并與匯編代碼交互C程序中的C表達(dá)式,享受匯編的高運(yùn)行效率。
內(nèi)聯(lián)匯編的格式是直接在C代碼中插入以下格式:
asm( .... .... )
其中的"..."為匯編代碼,比如下面例子中,在 result=a*b和printf("%d\n",result)之間插入一段匯編,
下面的這段匯編什么都不做,每個(gè)nop指令占用一個(gè)指令的執(zhí)行時(shí)間
result=a*b; asm("nop\n\t" "nop\n\t" "nop\n\t" "nop");//4個(gè)nop指令,\n\t表示換行,然后加上TAB行首空,因?yàn)槊總€(gè)匯編指令必須在單獨(dú)一行,需要換行,加上制表符是為了適應(yīng)某些編譯器的要求。 printf("%d\n",result);
可以很明顯地看到:
匯編代碼之間用“\n\t”間隔,并且每條匯編代碼單獨(dú)占用一行,共有4個(gè)nop指令,每個(gè)指令后的“\n\t”表示換行,然后加上TAB行首空,因?yàn)槊總€(gè)匯編指令必須在單獨(dú)一行,需要換行,加上制表符是為了適應(yīng)某些編譯器的要求。
下面是一個(gè)完整的例子,內(nèi)嵌的匯編完成對(duì)2個(gè)C程序定義的全局變量c和d的相加,并將相加結(jié)果存入全局變量addresult中:
#include <stdio.h> int c=10; int d=20; int addresult; int main(void){ int a=6; int b=2; int result; result=a*b; asm("nop\n\t" "nop\n\t" "nop\n\t" "nop");//4個(gè)nop指令,\n\t表示換行,然后加上TAB行首空,因?yàn)槊總€(gè)匯編指令必須在單獨(dú)一行,需要換行,加上制表符是為了適應(yīng)某些編譯器的要求。 printf("%d\n",result); asm("pusha\n\t" "movl c,%eax\n\t" "movl d,%ebx\n\t" "add %ebx,%eax\n\t" "movl %eax, addresult\n\t" "popa");//使用全局C變量c和d printf("%d\n",addresult); return 0; }
編譯上述代碼
$ gcc -o test test.c $ ./test 12 30
在匯編代碼中可以直接使用變量名稱操作C程序定義的全局變量,比如c、d和addresult就是全局變量:
"movl c,%eax\n\t" "movl d,%ebx\n\t" "movl %eax, addresult\n\t"
內(nèi)聯(lián)匯編部分如果不需要編譯器優(yōu)化( 優(yōu)化可能破壞匯編代碼的內(nèi)部結(jié)構(gòu),因?yàn)閰R編代碼直接操作寄存器,而寄存器使用優(yōu)化是編譯器提供的功能), 可以在 "asm" 后使用關(guān)鍵字 "volatile"。
asm volatile( .... .... )
如果程序必須與 ANSI C 兼容,則應(yīng)該使用 asm 和 volatile。
__asm__ __volatile__( ......... ......... )
下面的代碼和剛才代碼功能一樣,唯一的區(qū)別是不需要優(yōu)化
#include <stdio.h> int c=10; int d=20; int addresult; int main(void){ int a=6; int b=2; int result; result=a*b; //ansi c標(biāo)準(zhǔn)的asm有其它用,所以用__asm__,__volatile__表示內(nèi)聯(lián)匯編部分不用優(yōu)化(可以用volatile,但是ansi c不行),以防優(yōu)化破壞內(nèi)聯(lián)代碼組織結(jié)構(gòu) __asm__ __volatile__("nop\n\t" "nop\n\t" "nop\n\t" "nop");//4個(gè)nop指令,\n\t表示換行,然后加上TAB行首空,因?yàn)槊總€(gè)匯編指令必須在單獨(dú)一行,需要換行,加上制表符是為了適應(yīng)某些編譯器的要求。 printf("%d\n",result); __asm__ __volatile__("pusha\n\t" "movl c,%eax\n\t" "movl d,%ebx\n\t" "add %ebx,%eax\n\t" "movl %eax, addresult\n\t" "popa");//使用全局C變量c和d printf("%d\n",addresult); return 0; }
如何在內(nèi)聯(lián)匯編中訪問(wèn)C程序的局部變量呢,請(qǐng)看下面這段代碼。
#include <stdio.h> int main(void){ //不使用全局變量,必須使用擴(kuò)展GNU的asm //格式為:asm("匯編代碼":輸出位置:輸入位置:改動(dòng)的寄存器列表) //a為eax,ax,al;b為ebx等;c為ecx等;d為edx等;S為esi或si;D為edi或di //+讀和寫;=寫;%如果必要,操作數(shù)可以和下一個(gè)操作數(shù)切換;&在內(nèi)聯(lián)函數(shù)完成之前,可以刪除或重新使用操作數(shù) int xa=6; int xb=2; int result; //ansi c標(biāo)準(zhǔn)的asm有其它用,所以用__asm__,__volatile__表示內(nèi)聯(lián)匯編部分不用優(yōu)化(可以用volatile,但是ansi c不行),以防優(yōu)化破壞內(nèi)聯(lián)代碼組織結(jié)構(gòu) asm volatile( "add %%ebx,%%eax\n\t" "movl $2,%%ecx\n\t" "mul %%ecx\n\t" "movl %%eax,%%edx" :"=d"(result):"a"(xa),"b"(xb):"%ecx");//注意擴(kuò)展方式使用2個(gè)%表示 printf("%d\n",result); return 0; }
這個(gè)例子完成這個(gè)計(jì)算:(xa+xb)2=(6+2)2=16
不使用全局變量與匯編代碼交互,我們必須使用擴(kuò)展GNU的asm ,格式為:
asm("匯編代碼":輸出位置:輸入位置:改動(dòng)的寄存器列表)
匯編代碼中涉及寄存器部分的使用2個(gè)“%”,如:使用%%eax表示eax寄存器
輸出位置、輸入位置的特殊命名規(guī)則為:
a為eax,ax,al;b為ebx等;c為ecx等;d為edx等;S為esi或si;D為edi或di
+讀和寫
=寫
%如果必要,操作數(shù)可以和下一個(gè)操作數(shù)切換
&在內(nèi)聯(lián)函數(shù)完成之前,可以刪除或重新使用操作數(shù)
上述代碼中,匯編代碼部分為
輸出位置、輸入位置、改動(dòng)的寄存器列表部分為:
:"=d"(result):"a"(xa),"b"(xb):"%ecx"
先來(lái)看匯編代碼部分,使用雙%號(hào)表示寄存器,比如:
"add %%ebx,%%eax\n\t"
關(guān)于輸出位置、輸入位置部分,可以這么理解:將變量與寄存器綁定,綁定后,對(duì)寄存器的操作就是對(duì)變量的操作。
:"=d"(result):"a"(xa),"b"(xb)
將result與寄存器edx綁定,xa與寄存器eax綁定,xb與寄存器ebx綁定。
%ecx屬于需要改動(dòng)的寄存器
#include <stdio.h> int main(void){ int xa=6; int xb=2; int result; //使用占位符,由r表示,編譯器自主選擇使用哪些寄存器,%0,%1。。。表示第1、2。。。個(gè)變量 asm volatile( "add %1,%2\n\t" "movl %2,%0" :"=r"(result):"r"(xa),"r"(xb)); printf("%d\n",result); return 0; }
result、xa、xb綁定的寄存器由編譯器決定,前面的例子中我們采用直接指定的方式,在這里我們改成由編譯器
自主選擇,"r"是占位符,表示由編譯器自主選擇使用哪些寄存器,不指定哪個(gè)變量綁定在哪個(gè)寄存器上,
:"=r"(result):"r"(xa),"r"(xb)
那我們?nèi)绾沃肋@些變量綁定在哪些寄存器上呢,不知道綁定的寄存器,如何對(duì)變量進(jìn)行操作呢,可以使用
%0,%1這樣的符號(hào)來(lái)代替要操作的寄存器,%后的數(shù)字表示第幾個(gè)變量,如:%0,%1。。。表示第1、2。。。個(gè)變量。
:"=r"(result):"r"(xa),"r"(xb)
上面這個(gè)輸出和輸入列表已經(jīng)指定了變量的順序,
result是第0個(gè),xa是第1個(gè),xb是第2個(gè)
下面的例子完成 xb=xb-xa的計(jì)算,問(wèn)題出現(xiàn)了,可能會(huì)導(dǎo)致xb被分配了2個(gè)寄存器:
:"=r"(xb):"r"(xa),"r"(xb));
使用引用占位符能有效地使用可用寄存器,在這里我們指定xb使用第0個(gè)變量綁定的寄存器
:"=r"(xb):"r"(xa),"0"(xb));
第0個(gè)變量就是xb,即xb綁定的寄存器被修改后,結(jié)果仍寫回原寄存器
下面是完整例子
#include <stdio.h> int main(void){ int xa=2; int xb=6; asm volatile( "subl %1,%0\n\t" :"=r"(xb):"r"(xa),"0"(xb)); printf("%d\n",xb); return 0; }
我們編譯運(yùn)行一下
$ gcc -o test test.c $ ./test 4
用數(shù)字來(lái)表示變量的順序也許很麻煩,我們可以使用更簡(jiǎn)單的方法,使用“[標(biāo)識(shí)]”的格式標(biāo)記綁定后的變量。 下面的代碼完成xb=xb+xa的計(jì)算
#include <stdio.h> int main(void){ int xa=6; int xb=2; asm volatile( "add %[mya],%[myb]\n\t" :[myb]"=r"(xb):[mya]"r"(xa),"0"(xb)); printf("%d\n",xb); return 0; }
我們使用m標(biāo)記可以直接在內(nèi)存中對(duì)數(shù)進(jìn)行操作,前面的例子對(duì)變量進(jìn)行操作時(shí)都需要將變量值存儲(chǔ)在要修改的寄存器中,然后將它寫回內(nèi)存位置中.
#include <stdio.h> int main(void){ int xa=2; int xb=6; asm volatile( "subl %1,%0\n\t" :"=r"(xb):"m"(xa),"0"(xb)); printf("%d\n",xb); return 0; }
我們直接從xa的內(nèi)存地址中將xa取出,而不需要再將xa先存儲(chǔ)在一個(gè)寄存器。
首先,我們看一下AT&T匯編各段的意義
節(jié) 含義
.text 已編譯程序的機(jī)器代碼
.rodata 只讀數(shù)據(jù),如pintf和switch語(yǔ)句中的字符串和常量值
.data 已初始化的全局變量
.bss 未初始化的全局變量
.symtab 符號(hào)表,存放在程序中被定義和引用的函數(shù)和全局變量的信息
.rel.text 當(dāng)鏈接器吧這個(gè)目標(biāo)文件和其他文件結(jié)合時(shí),.text節(jié)中的信息需修改
.rel.data 被模塊定義和引用的任何全局變量的信息
.debug 一個(gè)調(diào)試符號(hào)表。
.line 原始C程序的行號(hào)和.text節(jié)中機(jī)器指令之間的映射
.strtab 一個(gè)字符串表,其內(nèi)容包含.systab和.debug節(jié)中的符號(hào)表
上面列表也許比較抽象,我們從一個(gè)C程序生成的中間匯編代碼分析:
#include <stdio.h> void main(){ char *x="xxxx"; char y[]="yy";//y的16進(jìn)制ASCII碼是97,9797的十進(jìn)制為31097 printf("%s-----%s",x,y); exit(0); }
我們使用gcc -S testcr.c,查看編譯生成的匯編代碼(為便于理解,將生成的匯編代碼進(jìn)行了注釋)
.file "testcr.c" .section .rodata .LC0: .string "xxxx"#使用char *分配 .LC1: .string "%s-----%s" .text .globl main .type main, @function main: pushl %ebp movl %esp, %ebp andl $-16, %esp subl $32, %esp#分配32字節(jié)??臻g,根據(jù)變量情況分配 movl $.LC0, 24(%esp)#x變量使用指針(4個(gè)字節(jié)大小),放入棧中,可以看到,變量分配靠近棧空間的尾部 movw $31097, 29(%esp)#字符'yy'移到main程序的棧中,直接將y變量的值放入棧中 movb $0, 31(%esp)#加上NULL標(biāo)志,表示字符結(jié)束 movl $.LC1, %eax leal 29(%esp), %edx movl %edx, 8(%esp) movl 24(%esp), %edx movl %edx, 4(%esp) movl %eax, (%esp) call printf movl $0, (%esp) call exit .size main, .-main .ident "GCC: (Ubuntu 4.4.3-4ubuntu5) 4.4.3" .section .note.GNU-stack,"",@progbits
在MAIN函數(shù)中char *分配在只讀數(shù)據(jù)段中,實(shí)際使用時(shí),只在程序棧中分配一個(gè)指針的空間。char[] 在程序棧中分配空間,然后直接使用movl、movw之類的匯編直接把值放入棧中空間。那么在其它函數(shù)中聲明的呢,可以從以下程序中看出,仍然如此。
#include <stdio.h> void myprinf(){ char *x="xxxx"; char y[]="yy";//y的16進(jìn)制ASCII碼是97,9797的十進(jìn)制為31097 printf("%s-----%s",x,y); } void main(){ int num=1; myprint(); exit(0); }
生成的中間匯編代碼為:
.file "testcr.c" .section .rodata .LC0: .string "xxxx" .LC1: .string "%s-----%s" .text .globl myprinf .type myprinf, @function myprinf: pushl %ebp movl %esp, %ebp subl $40, %esp movl $.LC0, -16(%ebp) movw $31097, -11(%ebp) movb $0, -9(%ebp) movl $.LC1, %eax leal -11(%ebp), %edx movl %edx, 8(%esp) movl -16(%ebp), %edx movl %edx, 4(%esp) movl %eax, (%esp) call printf leave ret .size myprinf, .-myprinf .globl main .type main, @function main: pushl %ebp movl %esp, %ebp andl $-16, %esp subl $32, %esp movl $1, 28(%esp) call myprint movl $0, (%esp) call exit .size main, .-main .ident "GCC: (Ubuntu 4.4.3-4ubuntu5) 4.4.3" .section .note.GNU-stack,"",@progbits
內(nèi)存的常用分配方式有:
第一,靜態(tài)分配,所有名字在編譯時(shí)綁定某個(gè)存儲(chǔ)位置。不能在運(yùn)行時(shí)改變
第二,棧分配,活動(dòng)時(shí)壓入系統(tǒng)棧。
第三,堆分配,以任意次序分配
上一篇:劍指offer之C++語(yǔ)言實(shí)現(xiàn)鏈表(兩種刪除節(jié)點(diǎn)方式)
欄 目:C語(yǔ)言
下一篇:C++中各種可調(diào)用對(duì)象深入講解
本文標(biāo)題:C指針原理教程之C內(nèi)嵌匯編
本文地址:http://mengdiqiu.com.cn/a1/Cyuyan/457.html
您可能感興趣的文章
- 01-10深入理解數(shù)組指針與指針數(shù)組的區(qū)別
- 01-10基于C++輸出指針自增(++)運(yùn)算的示例分析
- 01-10解析sizeof, strlen, 指針以及數(shù)組作為函數(shù)參數(shù)的應(yīng)用
- 01-10探討C++中數(shù)組名與指針的用法比較分析
- 01-10深入理解雙指針的兩種用法
- 01-10libxml教程(圖文詳解)
- 01-10C語(yǔ)言數(shù)組指針的小例子
- 01-10基于SVN源碼服務(wù)器搭建(詳細(xì)教程分析)
- 01-10解析如何用指針實(shí)現(xiàn)整型數(shù)據(jù)的加法
- 01-10深入const int *p與int * const p的區(qū)別詳解(常量指針與指向常量的指


閱讀排行
- 1C語(yǔ)言 while語(yǔ)句的用法詳解
- 2java 實(shí)現(xiàn)簡(jiǎn)單圣誕樹的示例代碼(圣誕
- 3利用C語(yǔ)言實(shí)現(xiàn)“百馬百擔(dān)”問(wèn)題方法
- 4C語(yǔ)言中計(jì)算正弦的相關(guān)函數(shù)總結(jié)
- 5c語(yǔ)言計(jì)算三角形面積代碼
- 6什么是 WSH(腳本宿主)的詳細(xì)解釋
- 7C++ 中隨機(jī)函數(shù)random函數(shù)的使用方法
- 8正則表達(dá)式匹配各種特殊字符
- 9C語(yǔ)言十進(jìn)制轉(zhuǎn)二進(jìn)制代碼實(shí)例
- 10C語(yǔ)言查找數(shù)組里數(shù)字重復(fù)次數(shù)的方法
本欄相關(guān)
- 04-02c語(yǔ)言函數(shù)調(diào)用后清空內(nèi)存 c語(yǔ)言調(diào)用
- 04-02func函數(shù)+在C語(yǔ)言 func函數(shù)在c語(yǔ)言中
- 04-02c語(yǔ)言的正則匹配函數(shù) c語(yǔ)言正則表達(dá)
- 04-02c語(yǔ)言用函數(shù)寫分段 用c語(yǔ)言表示分段
- 04-02c語(yǔ)言中對(duì)數(shù)函數(shù)的表達(dá)式 c語(yǔ)言中對(duì)
- 04-02c語(yǔ)言編寫函數(shù)冒泡排序 c語(yǔ)言冒泡排
- 04-02c語(yǔ)言沒(méi)有round函數(shù) round c語(yǔ)言
- 04-02c語(yǔ)言分段函數(shù)怎么求 用c語(yǔ)言求分段
- 04-02C語(yǔ)言中怎么打出三角函數(shù) c語(yǔ)言中怎
- 04-02c語(yǔ)言調(diào)用函數(shù)求fibo C語(yǔ)言調(diào)用函數(shù)求
隨機(jī)閱讀
- 08-05DEDE織夢(mèng)data目錄下的sessions文件夾有什
- 01-10SublimeText編譯C開(kāi)發(fā)環(huán)境設(shè)置
- 01-11Mac OSX 打開(kāi)原生自帶讀寫NTFS功能(圖文
- 08-05織夢(mèng)dedecms什么時(shí)候用欄目交叉功能?
- 01-10使用C語(yǔ)言求解撲克牌的順子及n個(gè)骰子
- 01-10delphi制作wav文件的方法
- 01-11ajax實(shí)現(xiàn)頁(yè)面的局部加載
- 08-05dedecms(織夢(mèng))副欄目數(shù)量限制代碼修改
- 04-02jquery與jsp,用jquery
- 01-10C#中split用法實(shí)例總結(jié)