为什么没有“unsigned wchar_t”和“signed wchar_t”类型?
char的签名不是标准化的。 因此,有signed char
和unsigned char
类型。 因此,使用单个字符的函数必须使用可以包含signed char和unsigned char的参数类型(此类型被选为int
),因为如果参数类型为char
,我们将从编译器获取类型转换警告(如果在这样的代码中使用-Wconversion):
char c = 'ÿ'; if (islower((unsigned char) c)) ... warning: conversion to 'char' from 'unsigned char' may change the sign of the result
( 这里我们考虑如果islower()的参数类型为char会发生什么 )
而没有明确类型转换使其工作的事情是从char
到int
自动升级。
此外,引入了wchar_t
的ISO C90标准没有说明wchar_t
的表示。
来自glibc引用的一些引用:
将
wchar_t
定义为char
是合理的
如果将
wchar_t
定义为char
则由于参数提升,必须将类型wint_t
定义为int
。
因此, wchar_t
可以很好地定义为char
,这意味着必须应用类似于宽字符类型的规则,即,可能存在wchar_t
为正的实现,并且可能存在wchar_t
为负的实现。 unsigned wchar_t
,必须存在unsigned wchar_t
和signed wchar_t
类型(出于与unsigned char
和signed char
类型相同的原因)。
私有通信显示允许实现仅支持> = 0值的宽字符(与wchar_t
的签名无关)。 谁知道这意味着什么? 瘦是否意味着当wchar_t
是16位类型(例如)时,我们只能使用15位来存储宽字符的值? 换句话说,符号扩展的wchar_t
是否为有效值? 另见这个问题 。
此外,私人通信显示标准要求wchar_t
任何有效值必须由wint_t
表示。 这是真的吗?
考虑这个例子:
#include #include int main (void) { setlocale(LC_CTYPE, "fr_FR.ISO-8859-1"); /* 11111111 */ char c = 'ÿ'; if (islower(c)) return 0; return 1; }
为了使它可移植,我们需要强制转换为’(unsigned char)’。 这是必要的,因为char
可能是等效的signed char
,在这种情况下,设置顶部位的字节在转换为int
时将被符号扩展,从而产生超出unsigned char
范围的值。
现在,为什么这种情况与宽字符的以下示例不同?
#include #include #include int main(void) { setlocale(LC_CTYPE, ""); wchar_t wc = L'ÿ'; if (iswlower(wc)) return 0; return 1; }
我们需要在这里使用iswlower((unsigned wchar_t)wc)
,但是没有unsigned wchar_t
类型。
为什么没有unsigned wchar_t
和signed wchar_t
类型?
UPDATE
标准是否保证在以下两个程序中转换为unsigned int
和int
是正确的? (我只是将wint_t
和wchar_t
替换为glibc中的实际含义)
#include #include int main(void) { setlocale(LC_CTYPE, "en_US.UTF-8"); unsigned int wc; wc = getwchar(); putwchar((int) wc); }
–
#include #include #include int main(void) { setlocale(LC_CTYPE, "en_US.UTF-8"); int wc; wc = L'ÿ'; if (iswlower((unsigned int) wc)) return 0; return 1; }
TL; DR:
为什么没有未签名的wchar_t和签名的wchar_t类型?
因为C的宽字符处理设施被定义为不需要它们。
更详细的,
char的签名不是标准化的。
确切地说,“实现应该将char定义为具有与signed char或unsigned char相同的范围,表示和行为。” (C2011,6.2.5 / 15)
因此,有
signed char
和unsigned char
类型。
“因此”意味着因果关系,这很难说清楚,但当你想要处理数字而不是字符时,肯定signed char
和unsigned char
更合适。
因此,使用单个字符的函数必须使用可以包含signed char和unsigned char的参数类型
一点都不。 使用单个字符的标准库函数可以根据char
类型轻松定义,无论该类型是否已签名,因为库实现确实知道其签名。 如果这是一个问题,那么它也同样适用于字符串函数 – char
将是无用的。
你的getchar()
示例是不合适的。 它返回int
而不是字符类型,因为它需要能够返回与任何字符都不对应的错误指示符。 此外,您提供的代码与随附的警告消息不对应:它包含从int
到unsigned char
转换,但没有从char
转换为unsigned char
。
其他一些字符处理函数接受int
参数或返回int
类型的值,以便与getchar()
和其他stdio函数兼容,并且由于历史原因。 在昔日的日子里,你实际上根本无法传递一个char
– 它总是被提升为int
,这就是函数将(并且必须)接受的东西。 以后不能改变论证类型,语言的演变。
此外,引入了
wchar_t
的ISO C90标准没有说明wchar_t
的表示。
C90不再具有真正的相关性,但毫无疑问它与C2011(7.19 / 2)非常类似,它描述了wchar_t
为
一个整数类型,其值范围可以表示支持的语言环境[…]中指定的最大扩展字符集的所有成员的不同代码。
glibc引用中的引用是非权威的,除了glibc之外。 它们在任何情况下都是评论,而不是规范,并且不清楚为什么你提出它们。 当然,至少第一个是正确的。 参考标准,如果给定实现支持的语言环境中指定的最大扩展字符集的所有成员都可以放入char
那么该实现可以将wchar_t
定义为char
。 这种实现过去比现在更常见。
你问几个问题:
私有通信显示允许实现仅支持> = 0值的宽字符(与
wchar_t
的签名无关)。 谁知道这意味着什么?
我认为这意味着,与您沟通的人不知道他们在谈论什么,或者他们所谈论的内容与C标准的要求不同。 您会发现, 在实践中 ,字符集仅使用非负字符代码定义,但这不是C标准所放置的约束。
瘦是否意味着当
wchar_t
是16位类型(例如)时,我们只能使用15位来存储宽字符的值?
C标准没有说或暗示。 您可以将任何支持的字符的值存储在wchar_t
。 特别是,如果实现支持包含超过32767的字符代码的字符集,则可以将它们存储在wchar_t
。
换句话说,符号扩展的wchar_t是否为有效值?
C标准没有说或暗示。 它甚至没有说wchar_t
是否是带符号的类型(如果没有,那么符号扩展对它来说毫无意义)。 如果它是带符号的类型,则无法保证在某些受支持的字符集中对表示字符的值进行符号扩展(该值原则上可以为负值)将生成一个值,该值也表示该字符中的字符设置,或任何其他支持的字符集。 将1添加到wchar_t
值也是如此。
此外,私人通信显示标准要求
wchar_t
任何有效值必须由wint_t
表示。 这是真的吗?
这取决于你的意思是“有效”。 标准说wint_t
是默认参数提升未更改的整数类型,可以保存与扩展字符集成员对应的任何值,以及至少一个与扩展字符集的任何成员不对应的值。
(C2011,7.29.1 / 2)
wchar_t
必须能够在任何受支持的语言环境中保存与扩展字符集的成员相对应的任何值。 wint_t
必须能够保存所有这些值。 但是, wchar_t
可能表示与任何支持的字符集中的任何字符都不对应的值。 这些值在类型可以表示它们的意义上是有效的。 wint_t
不需要能够表示这样的值。
例如,如果任何支持的语言环境的最大扩展字符集使用的字符代码最多但不超过32767,那么实现可以自由地将wchar_t
实现为无符号的16位整数,并将wint_t
为带符号的16位整数。 wchar_t
表示的与扩展字符不对应的值因此不能由wint_t
表示(但是wint_t
仍然有许多候选者,其所需的值与任何字符都不对应)。
关于字符和宽字符分类function,唯一的答案是差异仅仅源于不同的规范。 char
分类函数被定义为使用与定义返回的getchar()
相同的值 – -1或者必要时转换为unsigned char
的字符值。 另一方面,宽字符分类函数接受wint_t
类型的参数,它可以表示所有宽字符的值不变,因此不需要转换。
你在这方面声称
我们需要在这里使用
iswlower((unsigned wchar_t)wc)
,但是没有unsigned wchar_t
类型。
不,也许吧。 您不需要将wchar_t
参数转换为iswlower()
到任何其他类型,特别是,您不需要将其转换为显式无符号类型。 广泛的字符分类function与这方面的常规字符分类function不同,它们的设计是为了后见之明。 对于unsigned wchar_t
,C不需要存在这样的类型,因此可移植代码不应该使用它,但它可能存在于某些实现中。
关于问题的附加更新:
标准是否保证在以下两个程序中转换为unsigned int和int是正确的? (我只是将wint_t和wchar_t替换为glibc中的实际含义)
该标准没有提到一般的符合实现的那种。 但是,我想你的意思是要具体询问符合wchar_t
为int
且wint_t
为unsigned int
。
在这样的实现中,你的第一个程序是有缺陷的,因为它没有考虑getwchar()
返回getwchar()
的可能性。 将WEOF
转换为类型wchar_t
,如果这样做不会导致信号被引发,则不能保证产生对应于任何宽字符的值。 因此,将这种转换的结果传递给putwchar()
不会表现出已定义的行为。 此外,如果UINT_MAX
定义的值与UINT_MAX
(无法用int
表示)相同,那么将该值转换为int
具有独立于putwchar()
调用的实现定义行为。
另一方面,我认为你正在努力的关键点是,如果getwchar()
在第一个程序中返回的值不是WEOF
,那么它保证是通过转换为wchar_t
而保持不变的值。 您的第一个程序将在该情况下执行,但是转换为int
(或wchar_t
)是不必要的。
类似地,第二个程序是正确的,只要宽字符文字对应于适用的扩展字符集中的字符,但是转换是不必要的并且不做任何改变。 这种文字的wchar_t
值保证可以通过类型wint_t
表示,因此转换会更改其操作数的类型,但不会更改值。 (但是如果文字与扩展字符集中的字符不对应,则行为是实现定义的。)
第三方面,如果您的目标是编写严格一致的代码,那么正确的事情,以及这些特定宽字符函数的预期使用模式,将是这样的:
#include #include int main(void) { setlocale(LC_CTYPE, "en_US.UTF-8"); wint_t wc = getwchar(); if (wc != WEOF) { // No cast is necessary or desirable putwchar(wc); } }
还有这个:
#include #include #include int main(void) { setlocale(LC_CTYPE, "en_US.UTF-8"); wchar_t wc = L'ÿ'; // No cast is necessary or desirable if (iswlower(wc)) return 0; return 1; }