在Linux Source代码中
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(^)按位操作,如下所示。
如果我使用xor操作,是否有任何潜在的错误?
c -= 'A'-'a'; ----> c = c ^ 0x20 ; //using xor to convert to lower case to upper case and vice versa
答案 0 :(得分:4)
你很可能,但很难看出这一点。
XOR:使用常量输入字节值并不比添加(或减去)常量快。并且它成为切换的好处(即toupper()
和tolower()
可以是相同的代码)非常小,因为代码量非常小。
拆解时,这两个功能:
int my_tolower1(int c)
{
return c + 'a' - 'A';
}
int my_tolower2(int c)
{
return c ^ ('a' - 'A');
}
几乎编译成相同的东西,模数当然是add 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
addl
和xorl
指令都是三个字节,所以没有区别。我认为这些天它们在大多数有趣的CPU上都是单周期的。
请注意,正如我在评论中所说,一般情况下,您不应该绕过并假设您的C程序在可以进行这些假设的环境中运行。然而,Linux内核就是这样一种环境。
答案 1 :(得分:2)
在ASCII平台上,'a' - 'A'
等于0x20
,字母A-Z和a-z具有连续值,并且所有字母仅在六个最低有效位中不同,因此您可以使用c = c ^ 0x20
。但是C标准没有指定字符编码,这使得这种方法不可移植。
稍微便携且自我记录的变体是:
c ^= 'A' ^ 'a';
(C标准也没有强制要求字母A-Z和a-z具有连续值,因此Linux内核代码也不是严格可移植的。但它比XOR技巧做的假设更少。)
答案 2 :(得分: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
当然在调用函数之前,你应该检查参数是否是给定范围内的字母字符。