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

Igo*_*nko 4 c standards wchar-t widechar c-standard-library

char的签名不是标准化的.因此有signed charunsigned 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
Run Code Online (Sandbox Code Playgroud)

(这里我们考虑如果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_t16位类型(例如)时,我们只能使用15位来存储宽字符的值吗?换句话说,符号扩展wchar_t是有效值吗?另见这个问题.

此外,私人通信显示标准要求任何有效值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;
}
Run Code Online (Sandbox Code Playgroud)

为了使它可移植,我们需要转换为'(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;
}
Run Code Online (Sandbox Code Playgroud)

我们需要在iswlower((unsigned wchar_t)wc)这里使用,但没有unsigned wchar_t类型.

为什么没有unsigned wchar_tsigned wchar_t类型?

UPDATE

是标准说,铸造unsigned int,并int在以下两个方案是保证是正确的?(我刚刚更换wint_t,并wchar_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);
}
Run Code Online (Sandbox Code Playgroud)

-

#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;
}
Run Code Online (Sandbox Code Playgroud)

Joh*_*ger 7

TL; DR:

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

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


更详细的,

char的签名不是标准化的.

确切地说,"实现应该将char定义为具有与signed char或unsigned char相同的范围,表示和行为." (C2011,6.2.5/15)

因此有signed charunsigned char类型.

"因此,"意味着因果关系,这将是很难清楚地争论,但肯定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_tchar.这种实现过去比现在更常见.

你问几个问题:

私有通信显示允许实现仅支持> = 0值的宽字符(与签名无关wchar_t).谁知道这意味着什么?

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

瘦是指wchar_t16位类型(例如)时,我们只能使用15位来存储宽字符的值吗?

C标准没有说或暗示.您可以将任何支持的字符的值存储在a中wchar_t.特别是,如果实现支持包含超过32767的字符代码的字符集,那么您可以将它们存储在a中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位整数和带符号的16位整数自由实现wint_t.因此,可表示的值wchar_t与扩展字符不对应的值不能表示wint_t(但wint_t仍有许多候选者表示其所需的值与任何字符都不对应).

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

你在这方面声称

我们需要在iswlower((unsigned wchar_t)wc)这里使用,但没有unsigned wchar_t类型.

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


关于问题的附加更新:

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

该标准没有提到一般的符合实现的那种.但是我想,那你的意思是问具体的实现一致的,其wchar_tintwint_tunsigned 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);
  }
}
Run Code Online (Sandbox Code Playgroud)

还有这个:

#include <locale.h>
#include <wchar.h>
#include <wctype.h>
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;
}
Run Code Online (Sandbox Code Playgroud)