in Linux源代码
实施的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
您很可能可以,但是很难看到这一点。
XOR:具有常数的字节值并不比添加(或减去)常数更快。并且它变为切换的好处(即toupper()
和tolower()
可以是相同的代码)非常小,因为代码的量很小。
拆卸时,这两个功能:
int my_tolower1(int c)
{
return c + 'a' - 'A';
}
int my_tolower2(int c)
{
return c ^ ('a' - 'A');
}
几乎编译到同一件事,Modulo当然是添加与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内核就是这样的环境。
在ASCII平台上,'a' - 'A'
等于0x20
,字母A-Z和A-Z具有连续的值,所有字母仅在六个最不重要的位置上有所不同,因此您可以使用c = c ^ 0x20
。但是C标准并未指定字符编码,使此方法无法说明。
一个更便携式和自我日志的变体是:
c ^= 'A' ^ 'a';
(C标准也不要求字母A-Z和A-Z具有连续的值,因此Linux内核代码也不是严格可移植的。但是它的假设比XOR技巧更少。)
<) <。/div>使用空间字符''而不是魔术编号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
当然,在调用功能之前,您应该检查参数是否是给定范围内的alpha字符。