C 和 C++ 字符数据类型的历史

 

本文讨论各种字符数据类型、它们的用途、历史记录和使用场景。更多学习交流资料请加群Q833065897

介绍

本文的主题在大多数编程语言中被认为是非常基本的东西:字符和类型。但是,这不是一篇关于在 C 和 C++ 中使用字符数据的初学者级文章。我的目标是阐明各种字符数据类型、它们的用途、历史和使用场景。string

可能从阅读本文中受益的人包括:

  • 具有其他编程语言经验的程序员,有兴趣了解有关 C 和 C++ 的更多信息
  • C和C++不同经验水平的程序员,他们不能100%确定何时或如何使用不同的字符和类型string
  • 编程爱好者可能会发现了解 C 和 C++ 中字符和字符串数据类型开发背后的一些历史很有趣

原始字符数据类型

最初的类型是1971年在位于新泽西州的贝尔实验室发明的,当时Dennis Ritchie开始扩展B编程语言以添加类型。 是他添加到语言中的第一个类型,最初被称为(“新B”),后来更名为C。charcharNB

char是一种类型,表示可以包含基本字符集的计算机的最小可寻址单元。除了字符数据之外,它通常用作“”类型 - 例如,二进制数据通常声明为数组。bytechar

对于习惯于Java或C#等最新编程语言的开发人员来说,C版本看起来过于灵活且未被指定。例如,在 Java 中,该类型是原始整型,保证包含表示 UTF-16 代码单元的 16 位无符号整数。相比之下,C/C++ 类型:charcharchar

  • 大小为一个字节,但位数仅保证至少为 8。有,或者至少曾经有过,现实生活中的平台拥有超过 8 位 - 多达 32 位;char
  • 可以是签名的,也可以是未签名的。默认值通常取决于平台,通常可以通过编译器标志进行更改。存在有符号和无符号,它们保证分别是无符号和有符号的,但它们都是 不同的类型。charcharchar
  • 可以包含任何单字节字符集中的字符或编码的多字节字符集的字节。string

为了说明前面的几点 - 在 Java 中:

爪哇岛

 char a = (char)65;   // A

 char b = (char)174;  // ®

 char c = (char)1046; // Ж

我们知道它包含一个 16 位无符号整数值 65,代表拉丁大写; 是 16 位无符号整数值 174,代表注册商标符号; 是一个 16 位无符号整数值 1046,表示西里尔大写字母 zhe。aAbc

在 C 或 C++ 中,我们没有这样的保证。更多学习交流资料请加群Q833065897

C++

char a = (char)65;

a将至少有 8 位,因此我们可以确定它的值为 65,无论 的“符号”如何。该值将代表什么,可以解释。对于大多数 ASCII 派生的字符集,将表示拉丁大写,就像在 Java 中一样。但是,在使用 EBCDIC 编码的 IBM 大型机系统上,它不会表示任何内容。charaA

C++

char b = (char)174;

假设是 8 位宽,可以具有任一值,或者它会溢出到类似 ,具体取决于类型是有符号还是无符号。此外,即使对于各种 ASCII 派生字符集,它表示的实际字符也会有所不同。例如,在ISO-8859-1(拉丁语-1编码)中,它将代表注册商标标志(就像Java一样);使用 ISO 8859-2(拉丁语-2),它将代表“Ş”(S-cedilla);在ISO 8859-5(拉丁语/西里尔文)中,它将表示“Ў”(短U)等。charb174-82char

C++

char c = (char)1046;

c几乎可以保证溢出现代硬件架构,其价值将毫无意义。更多学习交流资料请加群Q833065897

实际上,我们很少将整数值分配给 s,而是使用字符文字。例如:char

C++

char a = 'A';

这将起作用并为 分配一个整数值。现在,你能猜出变量中存储的实际数值是多少吗?这取决于编译器、其选项和源文件的编码。对于当今使用的大多数平台,存储在中的值将是 。如果在 IBM 大型机上编译代码,则很有可能是 ,但这也取决于编译器设置。aa65193

怎么样:

C++

char b = '®';

根据编译器和源文件编码,它最终可能会成功编译和存储类似 into 的内容,或导致编译器错误。例如,我在Linux上使用的clang需要UTF-8源文件,它报告:174b14.0

错误:字符太大,无法将字符文本类型括起来

像这样:

C++

char c = 'Ж';

如果将源文件另存为 ISO-8859-5 代码页*并且*编译器设置为使用该编码,理论上可以工作。Clang再次失败,并出现相同的错误和相同的原因。更多学习交流资料请加群Q833065897

文字的另一个有趣的特征是,在 C 中,它的类型是 - 不是 。例如, 可能会返回类似 .在C++中,文字的类型是 ,并且保证为 。charintcharsizeof('a')4charsizeof('a')1

C 样式字符串

显然,字符数据最常用作字符的 s 而不是单个字符。在 C 中,a 是一个字符数组,以带有值的字符结尾。有一些 C 库实现了所谓的“Pascal 风格”,其中字符数组以其长度为前缀,但它们很少见,因为语言本身倾向于以 null 结尾。例如,文字 “” 的类型将是(在 C 中,而不是 C++) - 它将包括尾随零的空间。stringstring0stringstringstringabcchar[4]

在最简单的情况下,C 样式将使用单字节字符集进行编码,在这种情况下,每个字符集对应于可以在屏幕上显示的“字母”。显然,数组不携带有关字符集的信息,因此必须单独提供它才能正确显示文本。stringcharchar

C 样式 s 可以很好地与多字节编码配合使用,只要它们不需要嵌入零。在这种情况下,单个包含的字节可以对应于单个字符或多字节编码字符的一部分。stringchar

C 标准库假定 是零端接的。例如,函数的朴素实现可能如下所示:stringstrlen()

C++

size_t strlen(const char *str)

{

    const char *c = str;

    while (*c != 0)

        ++c;

    return (c - str);

}

让我们看看上述类型的特征如何影响数据的 s。如果我们看一下这个例子,我们可以看到:charstringstrlen

  • 它不受 大小的影响。无论位数如何,它都会正确计数,直到它击中char0;
  • 它不受 符号的影响。无论有符号还是无符号,它都将返回相同的值;charchar
  • 根据字符集,返回的值可能是也可能不是调用方预期的值。也就是说,该函数将始终返回数组中的 s 数,如果字符集是单字节,则通常是字符数。对于多字节字符集,返回的字符数通常与用户感知的字符数不同。strlencharchar

C++ 字符串类

C++ 标准库提供了可以为各种字符类型实例化的模板类。它在标头中声明,与字符类型实例化的 s 一起声明。对于类型为 。std::basic_string<><string>typedeftypedefcharstd::basic_string<char>std::string

从历史上看,该类早于 C++ 中的模板和命名空间,并且在 1998 年采用 C++ 标准之前,它只是使用的众多类之一。在早期,类通常使用写入时复制语义实现,这导致了各种问题,尤其是在多线程环境中,最终被 C++11 标准禁止。stringstringstring

如今,被广泛采用和使用。它的实现通常包含“小字符串优化” - 一种将基于堆栈的缓冲区用于小 s 的技术。随着移动语义的采用,s 可以很好地与C++容器配合使用,而不会引入不必要的副本。在现代C++中使用 for s 很少有意义,除非在 API 级别。std::basic_stringstringstringchar*string

wchar_t

在 1980 年代后期,开始了一项倡议,以引入通用字符集,以取代所有基于 8 位代码单元的传统字符编码。这个想法是将流行的ASCII字符集从7位扩展到16位,这被认为足以涵盖所有世界语言的字符。新的编码标准被称为Unicode,第一个版本于1991年底发布。更多学习交流资料请加群Q833065897

为了支持新的“宽”字符,在 C90 标准中添加了一个新类型 - 。它被定义为“一种整型类型,其值范围可以表示在支持的区域设置中指定的最大扩展字符集的所有成员的不同代码”。“” 的意思是 “” 来强调这通常是(但不一定!宽于 .“” 部分来自这样一个事实,即在 C 中不是一个不同的编译器类型,而是另一个整数类型,例如 。 在 wchar.h 中声明,以及各种函数以处理宽 s,例如 、 等。wchar_twwchar_twidewchar_tchar_twchar_ttypedefunsigned shortwchar_tstringwcslen()wprintf()

在预标准C++中,也以 开始,但很快就决定它必须是一个独特的编译器类型。即使在标准化之后,它仍然与“底层类型”绑定,这是其他整体类型之一。wchar_ttypedef

在实践中(但不是通过任何标准的字母),总是无符号的,有两种尺寸:wchar_t

  • 在Microsoft Windows和IBM AIX上,它是16位
  • 在几乎所有其他平台上,它都是 32 位

这种大小差异是一个不幸的历史事件 - Unicode 的早期采用者采用了与 Unicode 16.1 标准兼容的 0 位大小。后来的Unicode标准版本引入了补充平面,用于Windows和AIX上的UTF-16编码形式,以及后来采用Unicode的其他平台上的UTF-32编码形式。wchar_t

与此同时,引入了宽字符的新文字:L'' 表示宽字符,L“” 表示宽字符。wchar_tstring

C++ 标准将类定义为用作字符类型的类模板的实例化。std::wstringbasic_stringwchar_t

C11 / C++11 字符类型

在 C11 中,引入了两种新的字符类型:和 ;两者都在标头中声明,并且是无符号整数类型的 S。前者用于存储 16 位字符,并且必须至少为 16 位宽。后者用于 32 位字符,并且必须至少为 32 位宽。char16_tchar32_t<uchar.h>typedef

就像 一样,引入了新的文字:u'' for 和 U'' for 。与 不同,没有与适用于新类型的函数等效的新函数;没有 .wchar_tchar16_tchar32_twchar_tstringcharstrlen()char16_t

引入的第三种新文字类型是 u8''。它适用于旧类型,并用于 UTF-8 编码形式。char

使用 C11,基本上变得无用(尽管没有正式弃用)。字符类型适用于以下方案:wchar_t

  • char用于 UTF-8 Unicode 编码形式、各种单字节和多字节传统编码,以及作为字节类型
  • char16_t用于 UTF-16 Unicode 编码形式
  • char32_t用于 UTF-32 Unicode 编码形式

不出所料,C++ 11引入了两种同名的字符类型。与 C 不同,它们是不同的内置类型,而不是 s 和新关键字。typedef

C++11 中引入了两个用于两种新类型的实例化的新 s:typedefstd::basic_string

  • std::u16string- 一种类型定义std::basic_string<char16_t>
  • std::u32string- 一种类型定义std::basic_string<char32_t>

C++20 char8_t

C++20 引入了专门针对 UTF-8 编码字符数据的新字符类型:。它具有与它相同的大小和符号,但与它不同。u8'' 字符文本和 u8“” 文本已更改为返回新类型。引入了新的 for。char8_tunsigned charstringtypedefstd::u8stringstd::basic_string<char8_t>

即将推出的 C 标准(可能是 C23)包括一个 的提案,即 to 。char8_ttypedefunsigned char

结论

C 和 C++ 字符和字符串类型反映了语言的悠久历史。更多学习交流资料请加群Q833065897

原始类型仍在最广泛使用。在新代码中,它应该用于旧的单字节和多字节编码,以及非字符二进制数据。它适用于 UTF-8 编码的字符串,可用于它们,尤其是对于不支持类型的编译器。charchar8_t

wchar_t原来是更改 Unicode 规范的受害者。今天没有充分的理由在新代码中使用它,即使使用古老的编译器也是如此。

char16_t应该用于过去使用过各种“”的地方的 UTF-16 编码。stringwidechartypedef

char32_t应该用于 UTF-32 编码。尽管由于内存效率低下,很少看到 s 编码为 UTF-32,但单个代码点经常采用 UTF-32 编码,并且是用于此目的的理想类型。stringstringchar32_t

char8_t最近才被引入C++,并且只被提议用于 C。目前尚不清楚它在编码UTF-8方面相对于普通旧版本的优势是否足以看到广泛使用。charstring

更多学习交流资料请加群Q833065897

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值