7.7 大小写转换
toupper()和tolower()函数有着类似的历史。他们最初都被实现为宏:
#define toupper(c) ((c) + 'A' - 'a')
#define tolower(c) ((c) + 'A' - 'a')
当给定一个小写字母作为输入时,toupper()将产生相应的大写字母。tolower()反之。这两个宏都依赖于实现的字符集,它们需要所有的大写字母和对应的小写字母之间的差别都是常数的。这个假设对于ASCII和EBCDIC字符集来说都是有效的,可能不是很危险,因为这些不可移植的宏定义可以被封装到一个单独的文件中并包含它们。
这些宏确实有一个缺陷,即:当给定的东西不是一个恰当的字符,它会返回垃圾。因此,下面这个通过使用这些宏来将一个文件转为小写的程序是无法工作的:
int c;
while((c = getchar()) != EOF)
putchar(tolower(c));
我们必须写:
int c;
while((c = getchar()) != EOF)
putchar(isupper(c) ? tolower(c) : c);
就这一点,AT&T中的UNIX开发组织提醒我们,toupper()和tolower()都是事先经过一些适当的参数进行测试的。考虑这样重写这些宏:
#define toupper(c) ((c) >= 'a' && (c) <='z' ? (c) + 'A' - 'a' : (c))
#define tolower(c) ((c) >= 'A' && (c) <='Z' ? (c) + 'a' - 'A' : (c))
但要知道,这里c的三次出现都要被求值,这会破坏如toupper(*p++)这样的表达式。因此,可以考虑将toupper()和tolower()重写为函数。toupper()看起来可能像这样:
int toupper(int c) {
if(c >= 'a'&& c <= 'z')
return c +'A' - 'a';
return c;
}
tolower()类似。
这个改变带来更多的问题,每次使用这些函数的时候都会引入函数调用开销。我们的英雄认为一些人可能不愿意支付这些开销,因此他们将这个宏重命名为:
#define _toupper(c) ((c) + 'A' - 'a')
#define _tolower(c) ((c) + 'a' - 'A')
这就允许用户选择方便或速度。
这里面其实只有一个问题:伯克利的人们和其他的C实现者并没有跟着这么做。这意味着一个在AT&T系统上编写的使用了toupper()或tolower()的程序,如果没有为其传递正确大小写字母参数,在其他C实现中可能不会正常工作。
如果不知道这些历史,可能很难对这类错误进行跟踪。
免责声明:以上内容源自网络,版权归原作者所有,如有侵犯您的原创版权请告知,我们将尽快删除相关内容。