0
点赞
收藏
分享

微信扫一扫

位运算的巧妙使用 -- 字母大小写转换

引言

C 标准库的 ctype.h 头文件提供了一些函数,可用于测试和映射字符,其中有两个函数,int tolower(int c)该函数把大写字母转换为小写字母,int toupper(int c),该函数把小写字母转换为大写字母。那么它们底层的原理是什么呢?能不能用一个方法就实现大写字母转小写,小写字母转大写呢?

字母大小写转换

ASCLL码

我们知道,​​ASCLL码​​使用7位数编码了128个字符,其中字母'a'-'z'是97到122,字母'A'到'Z'是65到90,想要判断一个字符的类型,或者将一个字符的大写转换为对应的小写都要运用这个ASCLL码表。

tolower和toupper的实现

我们参考这里的代码实现:

​​www.aospxref.com/android-12.…​​ 如下:

static inline int islower(unsigned ch) { return (ch - 'a') < 26; }
static inline int isupper(unsigned ch) { return (ch - 'A') < 26; }

18 int LLVM_LIBC_ENTRYPOINT(tolower)(int c) {
19 if (internal::isupper(c))
20 return c + 'a' - 'A';
21 return c;
22 }

18 int LLVM_LIBC_ENTRYPOINT(toupper)(int c) {
19 if (internal::islower(c))
20 return c + 'A' - 'a';
21 return c;
22 }

可以看到,上面的逻辑是很朴素的,判断是否是小写,就使用当前值减去'a'的值,判断是否小于26;判断大写的逻辑也相似。

在转换为小写时,先判断是大写才转换,小写直接返回。如果是大写,则需要加上'a'与'A'的差(很明显,这里加了一个正数);转换大写的逻辑类似。

能否使用一个函数来完成这个判断加转换的工作?

首先,我们注意到,'a'与'A'的差值是97-65=32,是一个比较特殊的数2^5;

其次,我们来看看,这两个字符的二进制表示:

字符

int值

二进制表示

'a'

97

0110 0001

'A'

65

0100 0001

'a'-'A'

32

0010 0000

'z'

122

0111 1010

'Z'

90

0101 1010

这里,可以很明显地看出来,中间的32和上下两个值的关系:异或关系

即:97 = 65 ^ 32,同时65 = 97 ^ 32;

这样我们就可以改写一下转变大小写的函数,只是我们的函数将大写转为小写,小写转为大写,总是反过来,如下:

int reverseChar(int c) {
return c ^ (1 << 5);
}

将原来的加减法加判断,转换为了位运算操作。你体会到位运算的奥妙了吗?

实际上,我们要理解,异或运算相当于不进位的加法,这样一想,对ASCLL编码的操作其实都是可以用异或操作来代替加法的

举报

相关推荐

0 条评论