2016-12-06 28 views
5

在linux起作用在Linux Source代碼toupper和tolower的使用XOR位運算

tolower的和topupper的實現如下

static inline unsigned char __tolower(unsigned char c) 
{ 
     if (isupper(c)) 
      c -= 'A'-'a'; 
     return c; 
} 


static inline unsigned char __toupper(unsigned char c) 
{ 
    if (islower(c)) 
      c -= 'a'-'A'; 
    return c; 
} 

我可以使用XOR(^)按位操作,如下所示實現。

如果我使用異或操作,是否有任何潛在的Bug?

c -= 'A'-'a'; ----> c = c^0x20 ; //using xor to convert to lower case to upper case and vice versa 
+2

這會更快,因爲...? – Olaf

+2

你爲什麼認爲這會更快? – Art

+1

'c - ='A' - 'a';'而不是'c + ='a' - 'A'' ......只有在Linux中......(爲什麼不能讓事情變得不復雜,不容易?) – Lundin

回答

1

使用空格字符''而不是幻數0x20會更加正確。在這種情況下,這些功能對於EBCDIC表也是有效的。

這裏是一個示範項目

#include <stdio.h> 

char tolower(char c) 
{ 
    return c^' '; 
} 

char toupper(char c) 
{ 
    return c^' '; 
} 

int main(void) 
{ 
    char s[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; 

    puts(s); 

    for (char *p = s; *p; ++p) *p = tolower(*p); 

    puts(s); 

    for (char *p = s; *p; ++p) *p = toupper(*p); 

    puts(s); 
} 

程序輸出是

ABCDEFGHIJKLMNOPQRSTUVWXYZ 
abcdefghijklmnopqrstuvwxyz 
ABCDEFGHIJKLMNOPQRSTUVWXYZ 

當然喊你應該檢查參數是否在給定範圍內的字母字符的功能之前。

+0

@YassineHoussni我什麼都沒創造。我剛剛展示瞭如何使用^運算符來實現問題中的函數。 –

+0

空間無非是空間的ASCII值0x20 –

+1

@vinayhunachyal我想我指出爲什麼用空格字符爲什麼不用我? –

2

在ASCII平臺上,'a' - 'A'等於0x20,字母A-Z和a-z有連續的值,所有字母只在六個最低有效位中有所不同,所以你可以使用c = c^0x20。但C標準沒有指定字符編碼,使得這種方法不可移植。

稍微更便攜和自我記錄的變體是:

c ^= 'A'^'a'; 

(C標準也並不強制字母AZ和AZ已經連續值,因此Linux內核代碼是沒有嚴格的便攜式但它比XOR技巧的假設更少)

4

你很可能可以,但很難看出這一點。

異或:使用常量讀取字節值不會比添加(或減去)常量更快。由於代碼量非常小,它變成切換的好處(即toupper()tolower()可以是相同的代碼)非常小。

當拆卸,這兩個功能:

int my_tolower1(int c) 
{ 
    return c + 'a' - 'A'; 
} 

int my_tolower2(int c) 
{ 
    return c^('a' - 'A'); 
} 
當然

差不多編譯爲同樣的事情,模附加VS XOR:

my_tolower1(int): 
     pushq %rbp 
     movq %rsp, %rbp 
     movl %edi, -4(%rbp) 
     movl -4(%rbp), %eax 
     addl $32, %eax 
     popq %rbp 
     ret 
my_tolower2(int): 
     pushq %rbp 
     movq %rsp, %rbp 
     movl %edi, -4(%rbp) 
     movl -4(%rbp), %eax 
     xorl $32, %eax 
     popq %rbp 
     ret 

無論是addlxorl指令是三個字節,所以這裏沒有區別。我認爲他們現在對於最有趣的CPU都是單週期的。

請注意,正如我在我的評論中所說的那樣,通常情況下,您不應該出現並假定您的C程序運行在可以進行這些假設的環境中。然而,Linux內核就是這樣一個環境。

+0

謝謝解開這聽起來更好的解釋更快的實施案例... –

+0

一個小的區別:在某些平臺上,加/減改變處理器標誌(如carry或溢出),那麼按位操作不會,所以編譯器有更多的餘地來重新排序後者。 – nwellnhof

+0

@nwellnhof這在更大的背景下可能很重要,但在這裏我們只對單個角色進行操作,然後離開該功能,所以我認爲沒有太多機會。當然,如果編譯器可以證明它是正確的做法,編譯器可能會將添加切換到異或... – unwind