5

char 的符号没有标准化。因此有signed charunsigned 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 会发生什么

让它在没有显式类型转换的情况下工作的事情是从charto自动提升int

此外,wchar_t引入的 ISO C90 标准没有具体说明wchar_t.

glibc 参考中的一些引用:

定义wchar_tchar

ifwchar_t被定义为char类型wint_t必须被定义为int由于参数提升。

因此,wchar_t可以很好地定义为char,这意味着必须适用于宽字符类型的类似规则,即,可能存在 wchar_t肯定的实现,也可能存在wchar_t否定的实现。由此得出,必须存在unsigned wchar_t和类型(出于与存在和signed wchar_t类型相同的原因)。unsigned charsigned char

私人通信表明允许实现仅支持具有 >=0 值的宽字符(与 的符号无关wchar_t)。有人知道这是什么意思吗?薄是不是意味着当wchar_t是16位类型时(例如),我们只能使用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;
}

为了使其可移植,我们需要强制转换为 '(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 int和转换int是正确的?(我只是在glibc中替换wint_t了它们的实际含义)wchar_t

#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;
}
4

1 回答 1

8

TL;博士:

为什么没有无符号 wchar_t 和有符号 wchar_t 类型?

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


更详细地说,

char 的符号没有标准化。

准确地说,“实现应将 char 定义为与有符号字符或无符号字符具有相同的范围、表示和行为。” (C2011, 6.2.5/15)

因此有signed charunsigned char类型。

“因此”意味着因果关系,这很难说清楚,但当你想处理数字而不是字符时,肯定更合适signed charunsigned char

因此,使用单个字符的函数必须使用可以同时包含有符号字符和无符号字符的参数类型

一点都不。使用单个字符的标准库函数可以很容易地根据 type 定义char,无论该类型是否已签名,因为库实现确实知道它的签名。如果这是一个问题,那么它也同样适用于字符串函数 -char将无用。

你的例子getchar()是不恰当的。它返回int而不是字符类型,因为它需要能够返回不对应于任何字符的错误指示符。此外,您提供的代码与随附的警告消息不对应:它包含从intto的转换unsigned char,但没有从charto的转换unsigned char

一些其他字符处理函数接受int参数或返回类型int的值是为了与其他 stdio 函数兼容getchar(),以及出于历史原因。在过去的日子里,你实际上根本无法通过 a 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_t是16位类型时(例如),我们只能使用15位来存储宽字符的值?

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

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

C 标准没有说或暗示这一点。它甚至没有说是否wchar_t是有符号类型(如果不是,那么符号扩展对它来说毫无意义)。如果它是有符号类型,则无法保证对表示某些受支持字符集中的字符的值进行符号扩展(原则上该值可能为负)会产生一个也表示该字符中的字符的值集,或任何其他支持的字符集。将wchar_t值加 1 也是如此。

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

这取决于您所说的“有效”是什么意思。标准说wint_t

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

(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仍有许多候选值是其所需的不对应于任何字符的值)。

关于字符和宽字符分类功能,唯一的答案是差异只是来自不同的规范。char分类函数被定义为使用定义为返回的相同值——- getchar()1 或转换为的字符值(如有必要)unsigned char。另一方面,宽字符分类函数接受 type 的参数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_tisintwint_tis的符合实现unsigned int

在这样的实现中,您的第一个程序是有缺陷的,因为它没有考虑getwchar()返回的可能性WEOF。转换WEOF为 type wchar_t,如果这样做不会引发信号,则不能保证产生对应于任何宽字符的值。因此,将这种转换的结果传递给putwchar()不会表现出定义的行为。此外,如果定义为与(不能由 表示)WEOF相同的值,则将该值转换为具有独立于调用的实现定义的行为。UINT_MAXintintputwchar()

另一方面,我认为您正在努力解决的关键点是,如果getwchar()第一个程序中返回的值不是WEOF,那么它保证是通过转换为不变的wchar_t。在这种情况下,您的第一个程序将按预期执行,但强制转换为int(or wchar_t) 是不必要的。

类似地,第二个程序是正确的,前提是宽字符文字对应于适用的扩展字符集中的一个字符,但是强制转换是不必要的并且没有任何改变。这种wchar_t文字的值保证可以由 type 表示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);
  }
}

和这个:

#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;
}
于 2016-11-23T05:48:09.903 回答