在C语言中,isupper的宏定义是什么?

5

我想了解在C/C++中如何定义“isupper”宏。您能否提供相应的信息或指向可用资源。我尝试查看ctype.h,但无法理解。

4个回答

13

它的实现是有定义的,每个供应商可以并且通常会有不同的方法。

最常见的方法通常涉及"traits"表 - 一个包含每个字符一个元素的数组,该元素的值是指示有关该字符详细信息的标志集合。例如:

 traits[(int) 'C'] = ALPHA | UPPER | PRINTABLE;
在什么情况下 isupper() 会变成类似这样的形式:
 #define isupper(c) ((traits[(int)(c)] & UPPER) == UPPER)

5
它是具体实现相关的。一个明显的实现方式是:
extern char *__isupper;
#define isupper(x) ((int)__isupper[(x)])

__isupper指向一个由区域设置确定的0和1数组。然而,这种技术已经不再流行,因为在共享库中访问全局变量相当低效,并且会创建永久的ABI要求,而且它与POSIX线程本地区域设置不兼容。

在仅限于ASCII或UTF-8的实现上实现它的另一种明显方法是:

#define isupper(x) ((unsigned)(x)-'A'<='Z'-'A')

1
@R,我觉得你在这里混淆了字节的概念。一个字节就是一个字符。在ISO C中没有多字节字符。如果底层字符集是Unicode(无论编码方式如何),isupper及其相关函数必须能处理其他语言 - 这是与地区设置相关的。 - paxdiablo
@paxdiablo,你错了。ISO C非常明确地定义了“多字节字符”以及在多字节字符和wchar_t之间转换的函数。任何多字节字符是否存在以及其编码方式的性质都是实现和区域设置特定的。但我特别指出了一个仅支持UTF-8的实现。如果编码是UTF-8,则仅有ASCII字符0-0x7f可以单独对应于(宽)字符。值0x80-0xbf和0xc2-0xf4用作适当的多字节序列中的组件,任何剩余的值都是纯粹无效的(EILSEQ)。 - R.. GitHub STOP HELPING ICE
好的,看起来我们在这里误解了。我的理解(正如你所指出的那样是一个误解)是Unicode(完整的UTF-32)是底层编码,因此其他字符需要转换为大写。谢谢你澄清这一点。 - paxdiablo
如果char编码为UTF-8,则wchar_t几乎肯定会表示为Unicode代码点(相当于UTF-32),并且isw*函数将需要处理所有额外的字符,但非宽字符的is*函数无法处理。 - R.. GitHub STOP HELPING ICE
@MSalters:你错了。请阅读标准。ISO C指定数字为0 1 2 3 4 5 6 7 8 9,没有别的。你可以不喜欢这样,但这是事实。 - R.. GitHub STOP HELPING ICE
显示剩余12条评论

4

这是一个函数,不是宏。 isupper() 函数的定义取决于区域设置和当前字符集等因素——这就是为什么有一个专门用于此目的的函数。

对于 ASCII 码,由于字母的分配方式,测试这一点实际上相当容易。如果字符的 ASCII 码在包括 0x410x5A 在内的区间内,则它是大写字母。


1

实际上,这在GCC中相当复杂。但是isupper的简单实现可以(尽管它有双重评估错误)最简单地定义为:

#define isupper(c) (c >= 'A') & (c <= 'Z')

http://ideone.com/GlN05

GCC会特别检查当前语言环境下字符的第0位是否为1:

(*__ctype_b_loc ())[(int) (c)] & (unsigned short int) (1 << (0))

其中,__ctype_b_loc()是一个函数,它返回指向当前语言环境下包含当前字符集中每个字符特征的字符数组的指针。


这个宏是有问题的,因为它在参数周围缺少括号,并且它会评估其参数两次(想想 isupper(*s++)...)。你需要强制转换为“unsigned”,并使用无符号溢出语义来测试范围,而不会评估参数超过一次。 - R.. GitHub STOP HELPING ICE
公平起见,我指出了双重评估漏洞。 :-) - Scott S. McCoy

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接