库函数toupper和tolower有一段不为人知的历史,具体的实现有如下几个阶段:
阶段一:
#include <stdio.h>
#define toupper(c) ((c) + 'A' - 'a')
#define tolower(c) ((c) + 'a' - 'A')
int main()
{
printf("A = %d, a toupper result is: %d\n", 'A', toupper('a'));
printf("a = %d, A tolower result is: %d\n", 'a', tolower('A'));
printf("a to lower: %d\n", tolower('a'));
return 0;
}
运行结果:
cheny.le@cheny-ThinkPad-T420:~$ ./a.out
A = 65, a toupper result is: 65
a = 97, A tolower result is: 97
a to lower: 129
可以看到大写字母转小写字母是ok的,小写字母转大写也是ok的,但是如果误用了小写字母转小写,会出现问题,这个阶段没有考虑到用户的误操作。
阶段二:
#include <stdio.h>
#define toupper(c) ((c) >= 'a' && (c) <= 'z' ? (c) + 'A' - 'a' : (c))
#define tolower(c) ((c) >= 'A' && (c) <= 'Z' ? (c) + 'a' - 'A' : (c))
int main()
{
printf("A = %d, a toupper result is: %d\n", 'A', toupper('a'));
printf("a = %d, A tolower result is: %d\n", 'a', tolower('A'));
printf("a to lower: %d\n", tolower('a'));
return 0;
}
这个可以看到小写字母转小写字母的问题避免了,但是每次宏调用会使c被求值1-3次,如果遇到tolower(*p++)类似的表达式,会产生不良的后果。
阶段三:
#include <stdio.h>
int toupper(int c)
{
if (c >= 'a' && c <= 'z') {
return c + 'A' - 'a';
}
return c;
}
int tolower(int c)
{
if (c >= 'A' && c <= 'Z') {
return c + 'a' - 'A';
}
return c;
}
int main()
{
printf("A = %d, a toupper result is: %d\n", 'A', toupper('a'));
printf("a = %d, A tolower result is: %d\n", 'a', tolower('A'));
printf("a to lower: %d\n", tolower('a'));
return 0;
}
这样改动之后程序的健壮性显著增强,看起来没有什么缺点了,但是每次使用这些函数的时候又引入了函数调用的开销。
阶段四:
#include <stdio.h>
#define _toupper(c) ((c) + 'A' - 'a')
#define _tolower(c) ((c) + 'a' - 'A')
int toupper(int c)
{
if (c >= 'a' && c <= 'z') {
return c + 'A' - 'a';
}
return c;
}
int tolower(int c)
{
if (c >= 'A' && c <= 'Z') {
return c + 'a' - 'A';
}
return c;
}
int main()
{
printf("A = %d, a toupper result is: %d\n", 'A', toupper('a'));
printf("A = %d, a toupper result is: %d\n", 'A', _toupper('a'));
printf("a = %d, A tolower result is: %d\n", 'a', tolower('A'));
printf("a = %d, A tolower result is: %d\n", 'a', _tolower('A'));
printf("a to lower: %d\n", tolower('a'));
printf("a to lower: %d\n", _tolower('a'));
return 0;
}
这样子改动给了用户更多的选择,如果不需要考虑速度,就可以使用函数调用,如果考虑效率就必须自己注意使用方式,这样子速度和效率有了一个平衡,这个也就是最终版本了