为什么没有“unsigned wchar_t”和“signed wchar_t”类型?

时间:2016-11-23 03:42:26

标签: c standards wchar-t widechar c-standard-library

char的签名不规范。因此,有signed charunsigned char类型。因此,使用单个字符的函数必须使用可以包含signed char和unsigned char的参数类型(this type被选为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 会发生什么)

没有明确的类型转换使其工作的事情是自动升级 从charint

此外,引入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_tsigned wchar_t类型(出于与unsigned charsigned char类型相同的原因)。

私人通信显示允许实施支持广泛 仅包含> = 0值的字符(与wchar_t的签名无关)。谁知道这意味着什么?瘦是指wchar_t为16位时 类型(例如),我们只能使用15位来存储宽字符的值? 换句话说,符号扩展wchar_t是否为有效值? 另请参阅this question

此外,私人通信显示标准要求wchar_t的任何有效值必须 可由wint_t表示。这是真的吗?

考虑这个例子:

#include <locale.h>
#include <ctype.h>
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 <locale.h>
#include <wchar.h>
#include <wctype.h>
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_tsigned wchar_t类型?

更新

标准是否保证在以下两个程序中投射到unsigned intint是正确的? (我刚刚将wint_twchar_t替换为glibc中的实际含义)

#include <locale.h>
#include <wchar.h>
int main(void)
{
  setlocale(LC_CTYPE, "en_US.UTF-8");
  unsigned int wc;
  wc = getwchar();
  putwchar((int) wc);
}

-

#include <locale.h>
#include <wchar.h>
#include <wctype.h>
int main(void)
{
  setlocale(LC_CTYPE, "en_US.UTF-8");
  int wc;
  wc = L'ÿ';
  if (iswlower((unsigned int) wc)) return 0;
  return 1;
}

1 个答案:

答案 0 :(得分:7)

TL; DR:

  

为什么没有未签名的wchar_t和签名的wchar_t类型?

因为C的宽字符处理设施被定义为不需要它们。

更详细,

  

char的签名没有标准化。

准确地说,&#34;实现应该定义char,使其具有与signed char或unsigned char相同的范围,表示和行为。&#34; (C2011,6.2.5 / 15)

  

因此有signed charunsigned char类型。

&#34;因此&#34;意味着因果关系,这很难说清楚,但当你想要处理数字而不是字符时,signed charunsigned char更合适。

  

因此,使用单个字符的函数必须使用可以包含signed char和unsigned char

的参数类型

不,一点也不。可以使用类型char轻松定义与单个字符一起使用的标准库函数,无论该类型是否已签名,因为库实现 知道其签名。如果这是一个问题,那么它也同样适用于字符串函数 - char将是无用的。

getchar()的例子不合适。它返回int而不是字符类型,因为它需要能够返回与任何字符不对应的错误指示符。此外,您提供的代码与随附的警告消息不对应:它包含从intunsigned char的转换,但不包含从charunsigned 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。这种实现过去比现在更常见。

你问几个问题:

  

私密通信表明,允许实现仅支持&gt; = 0值的宽字符(与wchar_t的签名无关)。谁知道这意味着什么?

我认为这意味着,与您沟通的人并不知道他们在谈论什么,或者他们所谈论的内容与C标准的要求不同。您会发现 in practice ,字符集仅使用非负字符代码定义,但这不是C标准所放置的约束。

  

瘦是否意味着当wchar_t是16位类型时(例如),我们只能使用15位来存储宽字符的值?

C标准没有说明或暗示。您可以将任何支持的字符的值存储在wchar_t中。特别是,如果实现支持包含超过32767的字符代码的字符集,那么您可以将它们存储在wchar_t中。

  

换句话说,符号扩展的wchar_t是否为有效值?

C标准没有说明或暗示。它甚至没有说wchar_t是否是签名类型(如果没有,那么符号扩展对它来说毫无意义)。如果它是带符号的类型,则无法保证在某些受支持的字符集中对表示字符的值进行符号扩展(该值原则上可以为负值)将生成一个值,该值也表示该字符中的字符设置,或任何其他支持的字符集。将{1}加到wchar_t值也是如此。

  

此外,私密通信显示标准要求wchar_t的任何有效值必须由wint_t表示。这是真的吗?

这取决于你的意思&#34;有效&#34;。标准说wint_t

  

是一个整数类型,默认情况下,参数提升不变,可以保存与扩展字符集成员对应的任何值,以及至少一个与扩展字符集的任何成员不对应的值。

(C2011,7.29.1 / 2)

wchar_t必须能够在任何受支持的语言环境中保存与扩展字符集成员对应的任何值。 wint_t也必须能够拥有所有这些价值观。但是,wchar_t可以表示与任何支持的字符集中的任何字符都不对应的值。这些值在类型可以表示它们的意义上是有效的。 wint_t不需要能够代表这些值。

例如,如果任何支持的语言环境的最大扩展字符集使用的字符代码最多但不超过32767,则实现可以自由地将wchar_t实现为无符号的16位整数,并{{ 1}}作为带符号的16位整数。 wint_t无法表示wchar_t可表示的与扩展字符不对应的值(但wint_t仍然有许多候选项,其所需的值与任何字符都不对应)。

关于字符和宽字符分类功能,唯一的答案是差异只是来自不同的规范。 wint_t分类函数被定义为使用char定义为返回的相同值 - -1或者必要时转换为getchar()的字符值。另一方面,宽字符分类函数接受unsigned char类型的参数,它可以表示所有宽字符的值不变,因此不需要转换。

你在这方面声称

  

我们需要在此处使用wint_t,但没有iswlower((unsigned wchar_t)wc)类型。

不,也许。您不需要将unsigned wchar_t参数转换为wchar_t到任何其他类型,特别是,您不需要将其转换为显式无符号类型。广泛的字符分类功能与这方面的常规字符分类功能不同,它的设计是为了后见之明。对于iswlower(),C不需要存在这样的类型,因此可移植代码不应该使用它,但它可能存在于某些实现中。

关于问题附加的更新:

  

标准是否保证在以下两个程序中转换为unsigned int和int是正确的? (我只是将wint_t和wchar_t替换为glibc中的实际含义)

标准没有提到一般的符合实施的那种。但是,我想假设您要具体询问unsigned wchar_twchar_tintwint_t的符合性实施。

在这样的实现中,您的第一个程序存在缺陷,因为它没有考虑unsigned int返回getwchar()的可能性。将WEOF转换为类型WEOF,如果这样做不会导致信号被引发,则无法保证生成与任何宽字符对应的值。因此,将此类转换的结果传递给wchar_t并未表现出已定义的行为。此外,如果putwchar()定义的值与WEOF相同(UINT_MAX无法表示),那么将该值转换为int会独立实现定义的行为int电话。

另一方面,我认为你正在努力的关键点是,如果putwchar()在第一个程序中返回的值不是getwchar(),那么它保证是一个转换为WEOF时保持不变。您的第一个程序将在该情况下执行,但不需要转换为wchar_t(或int)。

类似地,第二个程序是正确的,只要宽字符文字对应于适用的扩展字符集中的字符,但是转换是不必要的并且不做任何改变。这种文字的wchar_t值保证可以通过类型wchar_t表示,因此转换会更改其操作数的类型,但不会更改值。 (但如果文字与扩展字符集中的字符不对应,则行为是实现定义的。)

第三方面,如果您的目标是编写严格一致的代码,那么正确的事情,以及这些特定宽字符函数的预期使用模式,将是:

wint_t

和此:

#include <locale.h>
#include <wchar.h>
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);
  }
}