亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 編程 > C > 正文

C指針原理教程之C內嵌匯編

2020-01-26 13:32:36
字體:
來源:轉載
供稿:網友

內聯匯編的重要性體現在它能夠靈活操作,而且可以使其輸出通過 C 變量顯示出來。因為它具有這種能力,所以 "asm" 可以用作匯編指令和包含它的 C 程序之間的接口。簡單得說,內聯匯編,就是可以讓程序員在C語言中直接嵌入匯編代碼,并與匯編代碼交互C程序中的C表達式,享受匯編的高運行效率。

內聯匯編的格式是直接在C代碼中插入以下格式:

asm(........)

其中的"..."為匯編代碼,比如下面例子中,在 result=a*b和printf("%d/n",result)之間插入一段匯編,

     下面的這段匯編什么都不做,每個nop指令占用一個指令的執行時間

 result=a*b; asm("nop/n/t" "nop/n/t" "nop/n/t" "nop");//4個nop指令,/n/t表示換行,然后加上TAB行首空,因為每個匯編指令必須在單獨一行,需要換行,加上制表符是為了適應某些編譯器的要求。  printf("%d/n",result);

可以很明顯地看到:

    匯編代碼之間用“/n/t”間隔,并且每條匯編代碼單獨占用一行,共有4個nop指令,每個指令后的“/n/t”表示換行,然后加上TAB行首空,因為每個匯編指令必須在單獨一行,需要換行,加上制表符是為了適應某些編譯器的要求。

 下面是一個完整的例子,內嵌的匯編完成對2個C程序定義的全局變量c和d的相加,并將相加結果存入全局變量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個nop指令,/n/t表示換行,然后加上TAB行首空,因為每個匯編指令必須在單獨一行,需要換行,加上制表符是為了適應某些編譯器的要求。  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$ ./test1230

在匯編代碼中可以直接使用變量名稱操作C程序定義的全局變量,比如c、d和addresult就是全局變量:

 "movl c,%eax/n/t" "movl d,%ebx/n/t" "movl %eax, addresult/n/t"

   內聯匯編部分如果不需要編譯器優化( 優化可能破壞匯編代碼的內部結構,因為匯編代碼直接操作寄存器,而寄存器使用優化是編譯器提供的功能), 可以在 "asm" 后使用關鍵字 "volatile"。

asm volatile(........)

如果程序必須與 ANSI C 兼容,則應該使用 asm 和 volatile。

 __asm__ __volatile__( ......... ......... )

    下面的代碼和剛才代碼功能一樣,唯一的區別是不需要優化

 #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標準的asm有其它用,所以用__asm__,__volatile__表示內聯匯編部分不用優化(可以用volatile,但是ansi c不行),以防優化破壞內聯代碼組織結構 __asm__ __volatile__("nop/n/t" "nop/n/t" "nop/n/t" "nop");//4個nop指令,/n/t表示換行,然后加上TAB行首空,因為每個匯編指令必須在單獨一行,需要換行,加上制表符是為了適應某些編譯器的要求。  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;}

如何在內聯匯編中訪問C程序的局部變量呢,請看下面這段代碼。

#include <stdio.h>int main(void){  //不使用全局變量,必須使用擴展GNU的asm  //格式為:asm("匯編代碼":輸出位置:輸入位置:改動的寄存器列表)  //a為eax,ax,al;b為ebx等;c為ecx等;d為edx等;S為esi或si;D為edi或di  //+讀和寫;=寫;%如果必要,操作數可以和下一個操作數切換;&在內聯函數完成之前,可以刪除或重新使用操作數  int xa=6;  int xb=2;  int result;  //ansi c標準的asm有其它用,所以用__asm__,__volatile__表示內聯匯編部分不用優化(可以用volatile,但是ansi c不行),以防優化破壞內聯代碼組織結構  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");//注意擴展方式使用2個%表示    printf("%d/n",result);  return 0;}

這個例子完成這個計算:(xa+xb)2=(6+2)2=16

     不使用全局變量與匯編代碼交互,我們必須使用擴展GNU的asm ,格式為:

      asm("匯編代碼":輸出位置:輸入位置:改動的寄存器列表)  

       匯編代碼中涉及寄存器部分的使用2個“%”,如:使用%%eax表示eax寄存器

       輸出位置、輸入位置的特殊命名規則為:

      a為eax,ax,al;b為ebx等;c為ecx等;d為edx等;S為esi或si;D為edi或di  

      +讀和寫

      =寫

      %如果必要,操作數可以和下一個操作數切換

      &在內聯函數完成之前,可以刪除或重新使用操作數

    上述代碼中,匯編代碼部分為

    輸出位置、輸入位置、改動的寄存器列表部分為:
     :"=d"(result):"a"(xa),"b"(xb):"%ecx"

     先來看匯編代碼部分,使用雙%號表示寄存器,比如:

     "add %%ebx,%%eax/n/t"

      關于輸出位置、輸入位置部分,可以這么理解:將變量與寄存器綁定,綁定后,對寄存器的操作就是對變量的操作。

       :"=d"(result):"a"(xa),"b"(xb)

       將result與寄存器edx綁定,xa與寄存器eax綁定,xb與寄存器ebx綁定。
       %ecx屬于需要改動的寄存器

#include <stdio.h>int main(void){  int xa=6;  int xb=2;  int result;  //使用占位符,由r表示,編譯器自主選擇使用哪些寄存器,%0,%1。。。表示第1、2。。。個變量  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"是占位符,表示由編譯器自主選擇使用哪些寄存器,不指定哪個變量綁定在哪個寄存器上,

:"=r"(result):"r"(xa),"r"(xb)

那我們如何知道這些變量綁定在哪些寄存器上呢,不知道綁定的寄存器,如何對變量進行操作呢,可以使用

%0,%1這樣的符號來代替要操作的寄存器,%后的數字表示第幾個變量,如:%0,%1。。。表示第1、2。。。個變量。

:"=r"(result):"r"(xa),"r"(xb)

上面這個輸出和輸入列表已經指定了變量的順序,

result是第0個,xa是第1個,xb是第2個   

   下面的例子完成   xb=xb-xa的計算,問題出現了,可能會導致xb被分配了2個寄存器:

    :"=r"(xb):"r"(xa),"r"(xb)); 

    使用引用占位符能有效地使用可用寄存器,在這里我們指定xb使用第0個變量綁定的寄存器

    :"=r"(xb):"r"(xa),"0"(xb));

    第0個變量就是xb,即xb綁定的寄存器被修改后,結果仍寫回原寄存器

    下面是完整例子

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

  我們編譯運行一下

  $ gcc -o test test.c  $ ./test  4 

   用數字來表示變量的順序也許很麻煩,我們可以使用更簡單的方法,使用“[標識]”的格式標記綁定后的變量。  下面的代碼完成xb=xb+xa的計算

#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標記可以直接在內存中對數進行操作,前面的例子對變量進行操作時都需要將變量值存儲在要修改的寄存器中,然后將它寫回內存位置中.

#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的內存地址中將xa取出,而不需要再將xa先存儲在一個寄存器。

首先,我們看一下AT&T匯編各段的意義

節 含義
.text 已編譯程序的機器代碼
.rodata 只讀數據,如pintf和switch語句中的字符串和常量值
.data 已初始化的全局變量
.bss 未初始化的全局變量
.symtab 符號表,存放在程序中被定義和引用的函數和全局變量的信息
.rel.text 當鏈接器吧這個目標文件和其他文件結合時,.text節中的信息需修改
.rel.data 被模塊定義和引用的任何全局變量的信息
.debug 一個調試符號表。
.line 原始C程序的行號和.text節中機器指令之間的映射
.strtab 一個字符串表,其內容包含.systab和.debug節中的符號表

上面列表也許比較抽象,我們從一個C程序生成的中間匯編代碼分析:

#include <stdio.h>void main(){  char *x="xxxx";  char y[]="yy";//y的16進制ASCII碼是97,9797的十進制為31097  printf("%s-----%s",x,y);  exit(0);}

 我們使用gcc -S testcr.c,查看編譯生成的匯編代碼(為便于理解,將生成的匯編代碼進行了注釋)

.file  "testcr.c"  .section  .rodata.LC0:  .string "xxxx"#使用char *分配.LC1:  .string "%s-----%s"  .text.globl main  .type  main, @functionmain:  pushl  %ebp  movl  %esp, %ebp  andl  $-16, %esp  subl  $32, %esp#分配32字節??臻g,根據變量情況分配  movl  $.LC0, 24(%esp)#x變量使用指針(4個字節大小),放入棧中,可以看到,變量分配靠近??臻g的尾部  movw  $31097, 29(%esp)#字符'yy'移到main程序的棧中,直接將y變量的值放入棧中  movb  $0, 31(%esp)#加上NULL標志,表示字符結束   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函數中char *分配在只讀數據段中,實際使用時,只在程序棧中分配一個指針的空間。char[] 在程序棧中分配空間,然后直接使用movl、movw之類的匯編直接把值放入棧中空間。那么在其它函數中聲明的呢,可以從以下程序中看出,仍然如此。

#include <stdio.h>void myprinf(){  char *x="xxxx";  char y[]="yy";//y的16進制ASCII碼是97,9797的十進制為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, @functionmyprinf:  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, @functionmain:  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

內存的常用分配方式有:

第一,靜態分配,所有名字在編譯時綁定某個存儲位置。不能在運行時改變
第二,棧分配,活動時壓入系統棧。
第三,堆分配,以任意次序分配

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表

圖片精選

亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
亚洲最大av网站| 美女性感视频久久久| 色妞色视频一区二区三区四区| 欧美日本高清一区| 色妞色视频一区二区三区四区| 91在线免费视频| 少妇高潮 亚洲精品| 久久久久久这里只有精品| 黑人精品xxx一区| 最好看的2019的中文字幕视频| 久久精品国产成人精品| 日韩av色综合| 亚洲欧美另类国产| 久久久国产一区二区三区| 日韩精品在线视频| 欧美日韩精品二区| 亚洲精品成人av| 亚洲乱码av中文一区二区| 欧美丰满老妇厨房牲生活| 国产成人精品一区二区| 成人国内精品久久久久一区| 日韩av在线网址| 一个人看的www欧美| 国产一区二区在线免费视频| 久久久精品一区二区| 欧美成年人在线观看| 中文精品99久久国产香蕉| 日韩在线免费av| 青青草原成人在线视频| 国产日韩欧美在线视频观看| 国产激情999| 欧美电影免费观看网站| 69国产精品成人在线播放| 国产精品精品久久久| 日韩三级影视基地| 亚洲精品久久久久中文字幕二区| 亚洲美女免费精品视频在线观看| 国产有码一区二区| 欧美老女人在线视频| 狠狠久久五月精品中文字幕| 热久久免费视频精品| www.日韩免费| 国产一区私人高清影院| 亚洲伦理中文字幕| 中文字幕一精品亚洲无线一区| 日韩欧美国产高清91| 欧美精品九九久久| 久久久久久久久久av| 亚洲女同性videos| 精品视频偷偷看在线观看| 在线精品国产成人综合| 久久久伊人日本| 日韩国产欧美精品一区二区三区| 亚洲无av在线中文字幕| 久久偷看各类女兵18女厕嘘嘘| 国产精品成人av性教育| 国产视频999| 欧美专区日韩视频| 精品视频在线观看日韩| 成人精品视频久久久久| 日韩av成人在线| 久久激情视频久久| 国产精品入口免费视频一| 深夜福利一区二区| 欧美日韩成人在线观看| 亚洲综合中文字幕68页| 成人黄色片网站| 亚洲第一精品电影| 亚洲精品一区中文| 亚洲欧洲国产一区| 欧美大成色www永久网站婷| 欧美日韩国产精品| 欧美视频在线视频| 国产精品678| 亚洲一级片在线看| 综合久久五月天| 久久久在线观看| 日韩免费视频在线观看| 美日韩精品免费视频| 成人久久久久爱| 高清欧美一区二区三区| 久久精品国产视频| 欧美成年人视频网站| 久久全球大尺度高清视频| 俺去啦;欧美日韩| 欧美激情视频给我| 亚洲iv一区二区三区| 久久人人爽人人爽人人片av高请| 91九色国产在线| 一区二区三区 在线观看视| 国产精品久久9| 中文字幕欧美日韩va免费视频| 亚洲第一福利在线观看| 午夜精品福利在线观看| 亚洲经典中文字幕| 欧美色视频日本版| 激情懂色av一区av二区av| 在线播放国产一区中文字幕剧情欧美| 欧美性受xxxx黑人猛交| 国产精品高潮呻吟久久av无限| 日韩在线免费高清视频| 国产精品久久久久久久久久小说| 色悠悠久久88| 欧美肥老太性生活视频| 91精品久久久久久久久久| 亚洲欧美另类自拍| 日韩中文字在线| 日本精品视频在线观看| 欧美一区二粉嫩精品国产一线天| 国产亚洲精品一区二555| 91在线免费看网站| 在线亚洲国产精品网| 日韩精品在线视频美女| 黑人巨大精品欧美一区二区| 永久免费看mv网站入口亚洲| 超碰日本道色综合久久综合| 亚洲综合在线播放| 久久精品人人做人人爽| 在线观看国产精品91| 亚洲欧美日韩在线一区| xxx一区二区| 欧美高清在线播放| 国产日韩在线亚洲字幕中文| 久久久久久高潮国产精品视| 久久综合免费视频影院| 国内精品久久久| 亚洲视频在线观看免费| 国产91|九色| 国产精品偷伦视频免费观看国产| 国产区精品视频| 国产日韩欧美夫妻视频在线观看| 欧美在线一区二区三区四| 中国日韩欧美久久久久久久久| 欧美日韩中文字幕在线视频| 日韩av电影手机在线观看| 欲色天天网综合久久| 日韩免费不卡av| 色综合久久中文字幕综合网小说| 欧洲精品久久久| 亚洲最大成人在线| 日韩在线观看电影| 国产精品成人在线| 97avcom| 欧美肥婆姓交大片| 91在线国产电影| 成人乱色短篇合集| 国产精品18久久久久久麻辣| 欧美一区亚洲一区| 欧美性一区二区三区| 亚洲欧洲国产伦综合| 亚洲天堂免费视频| 性欧美在线看片a免费观看| 在线观看日韩欧美| 日本亚洲欧洲色α| 日韩av综合网站| 国产成人综合精品在线| 久久精品视频中文字幕| 日韩有码在线电影| 97涩涩爰在线观看亚洲| 国产精品看片资源| 日韩福利视频在线观看| 久久亚洲成人精品| 国产精品va在线播放我和闺蜜| 亚洲第一网站免费视频|