2014-11-05 65 views
0

我已經寫了下面的彙編代碼來將字符串從小寫字母轉換爲大寫字母,它並不完全工作,因爲我無法訪問字符串的地址, m轉換。這段代碼不工作爲什麼?在gcc中的內聯彙編中訪問字符串的地址

#include<stdio.h> 
    int convert(char *str) 
    { 
     char *ptr; 
    __asm__ __volatile__ ("movl (%1),%%ebx;" 
        "subl $1,%%ebx;" 
        "movl %%ebx,%0;" 
      "REPEAT: addl $1,%%ebx;" 
        "testl %%ebx,%%ebx;" 
        "je END;" 
        "movzbl 0(%%ebx),%%ecx;" 
        "cmpl $97, %%ecx;" 
        "jb END;" 
        "cmpl $122,%%ecx;" 
        "ja END;" 
        "subb $32,0(%%ebx);" 
        "jmp REPEAT;" 
       "END: movl %%ebx,(%0);" 
        :"=r" (ptr) 
        :"r" (str) 
       ); 
    printf("converted string =%s\n", str); 
} 

    int main() 
    { 
    int i; 
    char str[] = "convert"; 

    i = convert(str); 
    return 0; 

    } 
+0

你的問題是什麼?請問一個問題。 – fuz 2014-11-05 16:47:07

+0

@FUZxxi:我無法訪問字符串的地址,並且上面的代碼不起作用! – goal4321 2014-11-05 16:55:40

+0

你在哪一點指定'ptr'或'str'? – fuz 2014-11-05 16:56:50

回答

0

這是我的解決方案與上面略有不同,感謝FUZxxi指出。我應該說,檢索裝配很大程度上有助於解決問題,但它可能很難理解,但它會給你帶來實際問題。如果有人想了解我想達到的目標,我寫了足夠的評論。

/* code to convert from lower case to upper case */ 
int convert(char *str) 
{ 
    __asm__ __volatile__ ("movl %1,%%ebx;" // Get the address of str 
       "subl $1,%%ebx;"  
     "REPEAT: addl $1,%%ebx;"  
       "movl 0(%%ebx),%%edx" // Move the contents to edx 
       "movzbl %%dl,%%ecx;" // moving last character to ecx 
       "testl %%ecx,%%ecx;" // compare if it's null 
       "je END;"    
       "cmpl $97, %%ecx;"  
       "jb END;" 
       "cmpl $122,%%ecx;" 
       "ja END;" 
       "subb $32,(%%ebx);" // if its lower case, subtract 32 
       "jmp REPEAT;" 
      "END:;" 
       :   // No output specified 
       :"r" (str) //input 
       :"ecx","edx" //clobbers 
      ); 
    printf("converted string =%s\n", str); 
} 

上面的代碼,如果你編譯使用「GCC -m32」選項應該工作,如果你是編譯的AMD64。我遇到過這樣做
問題「致命錯誤:SYS/cdefs.h:沒有這樣的文件或目錄」

解決方案:安裝該軟件包:libc6的-DEV-I386

+0

這個解決方案如何不是高效的解決方案,如果你想快速做到這一點(主要是我們想要的),請使用矢量指令。 – goal4321 2014-11-28 17:21:53

0

在接受答案的代碼似乎有幾個問題:

  • 正如所寫,此代碼不會編譯(只有1個參數時它引用%1),並且它在第4個asm行缺少結束符。
  • 此代碼沒有正確處理「aBc」之類的字符串。
  • 此代碼不使用「內存」clobber,即使它修改內存。
  • 此代碼(仍然)修改一個未被破壞的寄存器(ebx)。
  • 不適用於x64。

怎麼樣更多的東西是這樣的:

char *convert(char *str) 
{ 
    char *res = str; 
    char temp; 

    __asm__ __volatile__ (
     "dec %[str]\n" 
     "REPEAT:\n\t"  
     "inc %[str]\n\t" 
     "movb (%[str]), %[temp]\n\t" /* Read the next char */ 
     "testb %[temp], %[temp]\n\t" 
     "jz END\n\t"     /* Is the char null */ 
     "cmpb $97, %[temp]\n\t"  /* >= 'a'? */ 
     "jb REPEAT\n\t" 
     "cmpb $122, %[temp]\n\t"  /* <= 'z'? */ 
     "ja REPEAT\n\t" 
     "subb $32, %[temp]\n\t"  /* Perform lowercase */ 
     "mov %[temp], (%[str])\n\t" /* Write back to memory */ 
     "jmp REPEAT\n" 
     "END:\n" 
     : [str] "+r" (str), [temp] "=r" (temp) 
     : /* no inputs */ 
     : "memory" 
    ); 

    /* Note that at this point, str points to the null. 
     str - res is the length. */ 

    return res; 
} 

此代碼:

  • 使用更少的寄存器(2比4)。
  • 通過使用「= r」(temp),我們讓編譯器選擇最好的寄存器來使用,而不是強制一個特定的寄存器。
  • 只讀取一次內存(而不是兩次)。
  • 返回一個指向字符串的指針(而不是什麼都不返回?)。
  • IMO使用%[temp]和%[src]比%1稍微容易閱讀。
  • 使用\n\t(而不是;)使得從gcc -S的輸出更易於閱讀。
  • 此代碼修改str(這就是爲什麼它被列爲「+ r」)。

或者,如果您確實想要看中,請在'c'中輸入代碼,然後使用gcc -O2 -S查看輸出。