在 UTF-16 等不同字符集编码中通过 ISO/IEC 混淆 sizeof(char)

Confusing sizeof(char) by ISO/IEC in different character set encoding like UTF-16

假设一个程序在具有UTF-16编码字符集的系统上运行。所以根据 The C++ Programming Language - 4th,第 150 页:

A char can hold a character of the machine’s character set.

→ 我认为 char 变量的大小为 2 个字节。

但是根据ISO/IEC 14882:2014:

sizeof(char), sizeof(signed char) and sizeof(unsigned char) are 1".

C++ 编程语言 - 第 4,第 149 页:

"[...], so by definition the size of a char is 1"

→固定大小为1

问题:以上这些说法是否有冲突或 sizeof(char) = 1 是否只是一个默认(定义)值,将根据每个系统由实现定义?

不,没有冲突。这两个语句引用了byte.

的不同定义

UTF-16 意味着 byteoctet 相同 - 一组 8 位。

在 C++ 语言中 byte 等同于 char。一个 C++ 字节可以包含多少位没有限制。 C++ 字节中的位数由 CHAR_BIT 宏常量定义。

如果您的 C++ 实现决定使用 16 位来表示每个字符,那么 CHAR_BIT 将为 16,并且每个 C++ 字节将占用两个 UTF-16 字节。 sizeof(char) 仍为 1,所有对象的大小将以 16 位字节为单位进行测量。

C++ 标准(和 C,就此而言)有效地将 byte 定义为 char 类型的大小, 而不是 作为八-位数1。根据 C++11 1.7/1(我的粗体):

The fundamental storage unit in the C++ memory model is the byte. A byte is at least large enough to contain any member of the basic execution character set and the eight-bit code units of the Unicode UTF-8 encoding form and is composed of a contiguous sequence of bits, the number of which is implementation defined.

因此表达式 sizeof(char) 总是 1,无论如何。

如果您想查看基线 char 变量(可能 unsigned 变体最好)是否真的可以保存 16 位值,您想要查看的项目是 CHAR_BIT 来自 <climits>。这包含 char 变量中的位数。


1 许多标准,尤其是与通信协议相关的标准,使用更准确的术语 octet 表示八位值。

一个char被定义为1个字节。字节是最小的可寻址单位。这在普通系统上是 8 位,但在某些系统上是 16 位、32 位或其他任何东西(但对于 C++ 必须至少为 8 位)。

这有点令人困惑,因为在流行的术语中,byte 用于技术上称为 octet(8 位)的内容。

所以,你的第二个和第三个引用是正确的。严格来说,第一句话是不正确的。

根据 C++ 标准中的 [intro.memory]/1 定义,char 只需要能够容纳 基本执行字符集 即可大约有 100 个字符(所有字符都出现在 ASCII 的 0 - 127 范围内),以及组成 UTF-8 编码的八位字节。也许这就是作者所说的机器字符集的意思。


在硬件可八位寻址但字符集为 Unicode 的系统上,char 可能会保持 8 位。但是,有类型 char16_tchar32_t(在 C++11 中添加)设计用于您的代码,而不是 char 用于具有 16 位或 32 位的系统字符集。

因此,如果系统使用 char16_t,那么您将使用 std::basic_string<char16_t> 而不是 std::string,依此类推。

UTF-16 的确切处理方式将取决于系统选择的实施细节。 Unicode是21位字符集,UTF-16是它的多字节编码;所以系统可以走 Windows-like 路线并使用 std::basic_string<char16_t> 和 UTF-16 编码字符串;或者它可以使用原始 Unicode 代码点作为字符 std::basic_string<char32_t>

Alf 的 post 详细介绍了一些可能出现的问题。

是的,char 的 C++ 角色合并存在许多严重的冲突和问题,而且 问题 也混淆了一些事情。所以一个简单直接的回答就像对“你停止打你妻子了吗?”这个问题回答“是”、“否”或“不知道”。唯一的直接答案是 the buddhist “mu”,没有提出问题。

因此,让我们从事实开始。


关于 char 类型的事实。

每个 char 的位数由 <limits.h> header 中定义的 CHAR_BIT 的实现给出。此数字保证为 8 或更大。对于 C++03 和更早版本,保证来自 C89 标准中该符号的规范,C++ 标准将其标记为(在 non-normative 部分,但仍然)为“合并”。对于 C++11 和更高版本的 C++ 标准,其自身明确地提供了≥8 的保证。在大多数平台上 CHAR_BIT 是 8,但在一些可能仍然存在的平台上 Texas Instruments digital signal processors 是 16,并且已经使用了其他值。

无论 CHAR_BIT 的值如何,sizeof(char) 根据定义为 1,即它不是实现定义的:

C++11 §5.3.3/1(在 [expr.sizeof] 中):

sizeof(char), sizeof(signed char) and sizeof(unsigned char) are 1.

char及其变体是内存的基本寻址单位,也就是byte[=143]的基本含义=],在普通话和正式的 C++ 中:

C++11 §1.7/1(在 [intro.memory] 中):

The fundamental storage unit in the C ++ memory model is the byte.

这意味着在上述 TI DSP 上,没有 C++ 方法获取指向单个 octets(8 位部分)的指针。这反过来意味着需要处理字节顺序或以其他方式需要将 char 值视为八位字节序列的代码,特别是对于网络通信,需要使用 char 值来处理在 CHAR_BIT 为 8 的系统上没有意义。这也意味着普通的 C++ 窄字符串文字,如果它们符合标准,并且如果平台的标准软件使用 8 位字符编码,则会浪费内存。

Pascal 语言曾(或现在)直接解决了浪费问题,它区分了压缩字符串(每个字节多个八位字节)和未压缩字符串(每字节一个八位字节),其中前者用于被动文本存储,后者用于高效处理。

这说明了单个 C++ 类型中三个方面的基本合并 char:

  • 内存寻址单位,a.k.a。字节,

  • 最小的基本类型(如果是 octet 类型就好了),

  • 字符编码值单位。

是的,这是一个冲突。


关于 UTF-16 编码的事实。

Unicode 是一大组 21 位 代码点 ,其中大部分单独构成字符,但其中一些与其他字符组合形成字符。例如。通过组合“e”和“´”-as-accent 的代码点可以形成带有重音符号的字符,例如“é”。由于这是一种通用机制,这意味着一个 Unicode 字符可以是任意数量的代码点,尽管通常只有 1 个。

UTF-16 编码最初是基于原始 Unicode 的每个代码点 16 位的代码兼容方案,当 Unicode 扩展到每个代码点 21 位时。基本方案是原始 Unicode 定义范围内的代码点表示为它们自己,而每个新的 Unicode 代码点表示为 16 位值的代理对。一小部分原始 Unicode 用于代理对值。

当时,基于每个代码点 16 位的软件示例包括 32 位 Windows 和 Java 语言。

在具有 8 位字节 UTF-16 的系统上是 宽文本 编码的示例,即编码单元比基本可寻址单元宽。面向字节的文本编码被称为 narrow text。在这样的系统上,C++ char 适合后者,但不适合前者。

在 C++03 中,唯一适合宽文本编码单元的内置类型是 wchar_t.

但是,C++ 标准实际上要求 wchar_t 适合 code-point,对于现代的 21-bits-per-code-point Unicode的意思就是需要32位。因此,没有符合 UTF-16 编码值要求的 C++03 专用类型,每个值 16 位。由于历史原因,最流行的基于UTF-16作为宽文本编码的系统,即微软Windows,将wchar_t定义为16位,在Unicode扩展后已经与标准公然矛盾,但是,该标准在这个问题上是不切实际的。一些平台s 定义 wchar_t 为 32 位。

C++11引入了新类型char16_tchar32_t,前者是(设计是)适用于 UTF-16 编码值。


关于问题。

关于问题的陈述假设

” a system with UTF-16 encoding character set

这可能意味着以下两种情况之一:

  • 以UTF-16为标准窄编码的系统,或
  • 以 UTF-16 作为标准宽编码的系统。

以 UTF-16 作为标准窄编码 CHAR_BIT ≥ 16,并且(根据定义)sizeof(char) = 1。我不知道任何系统,即它似乎是假设的。然而,这似乎是当前其他答案中默认的含义。

以UTF-16为标准宽编码,如Windows,情况更复杂,因为C++标准不能胜任。但是,以 Windows 为例,一种实际可能性是 sizeof(wchar_t)= 2。并且应该注意的是,该标准与该问题的现有实践和实际考虑相冲突,当理想情况下该标准反而使现有的做法标准化,如果有的话。

现在终于可以解决这个问题了,

Is there a conflict between these statements above or is the sizeof(char) = 1 just a default (definition) value and will be implementation-defined depends on each system?

这是错误的二分法。这两种可能性并不是对立的。我们有

  • char作为字符编码单位和作为内存寻址单位(字节)确实存在冲突。如前所述,Pascal 语言有关键字 packed 来处理该冲突的一个方面,即存储与处理要求。 wchar_t 的形式要求与其在使用 UTF-16 编码的最广泛使用的系统中对 UTF-16 编码的使用之间存在进一步的冲突,即 Windows.

  • sizeof(char) = 1 根据定义:它不是 system-dependent.

  • CHAR_BIT 是实现定义的,保证≥ 8。

不引用标准很容易给出简单的答案,因为:

字节定义不是8位。字节是任意大小但最小的可寻址内存单元。最常见的是 8 位,但没有理由没有 16 位字节。

C++标准给出了更多的限制,因为它必须至少是8位。

所以不管怎样,sizeof(char) 总是 1 是没有问题的。有时它会以 8 位表示,有时是 16 位等等。