【问题标题】:What assumption is safe for a C++ implementation's character set?对于 C++ 实现的字符集,什么假设是安全的?
【发布时间】:2014-01-26 14:35:11
【问题描述】:

在 C++ 编程语言 6.2.3 中,它说:

可以安全地假设实现字符集包括 十进制数字、英文的 26 个字母字符和一些 的基本标点符号。假设是不安全的 那:

  • 8 位字符集中的字符数不超过 127 个(例如,某些字符集提供 255 个字符)。

  • 没有比英语提供更多的字母字符(大多数欧洲 语言提供更多,例如æ、þ和ß)。

  • 字母字符是连续的(EBCDIC 在“i”和“j”之间留有间隙)。

  • 用于编写 C++ 的每个字符都可用(例如, 一些国家字符集不提供 {、}、[、]、| 和 \)。

  • 一个字符适合 1 个字节。有嵌入式处理器 没有字节访问 char 为 4 个字节的硬件。还有,一个 可以合理地为基本字符使用 16 位 Unicode 编码。

我不确定我是否理解最后两个陈述。

在标准的第 2.3 节中,它说:

基本源字符集由 96 个字符组成:空格 字符,代表水平制表符的控制字符, 垂直制表符、换页和换行,以及以下 91 个图形 字符:

a b c d e f g h i j k l m n o p q r s t u v w x y z
A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
0 1 2 3 4 5 6 7 8 9
_ { } [ ] # ( ) % : ; . ? * + - / ^ & | ! = , \ " '
...

基本执行字符集和基本执行宽字符集应分别包含基本执行字符集的所有成员 源字符集,加上代表警报的控制字符, 退格和回车,加上一个空字符(分别, null 宽字符),其表示全为零。

我们可以看到,标准规定像 { } [ ] | 这样的字符。 \ 是基本执行字符集的一部分。那么为什么 TC++PL 说假设这些字符在实现的字符集中可用是不安全的呢?

对于字符的大小,在标准的第 5.3.3 节中:

sizeof 运算符产生对象中的字节数 其操作数的表示。 ... ... sizeof(char)sizeof(signed char)sizeof(unsigned char) 是 1。

我们可以看到标准规定一个 char 是 1 个字节。 TC++PL 在这里试图说明什么?

【问题讨论】:

  • 关于char的大小,它总是1,但这并不一定意味着它是一个字节
  • 并且假设字母表中的数字或字符序列是连续的是安全的。正如您所说,EBCDIC 在编码中留下了一个空白,但它仍然是有效的源编码。 (如果我没记错的话,IBM 大型机仍然使用 EBCDIC。)
  • 感谢您的回复。该标准说“sizeof 运算符产生其操作数的对象表示中的字节数。”那为什么 sizeof(char)==1 并不意味着它是一个字节? @joachim-pileborg
  • char 类型是一种特殊情况。您可能想阅读例如this old SO question.
  • 关于{}[] 等不可用,例如:您正在为带 LCD 显示屏的数字温度计编写软件,您编写代码并编译它使用具有这些字符的PC。这并不意味着运行此代码的设备(温度计)将支持显示这些字符。要点是:开发系统!=目标系统。

标签: c++ character


【解决方案1】:
  • “字节”这个词似乎在第一个引用中被草率地使用了。就 C++ 而言,一个字节始终是一个字符,但它所包含的位数取决于平台(在 CHAR_BITS 中可用)。有时您想说“一个字节是八位”,在这种情况下您会得到不同的含义,这可能是“一个字符有四个字节”这句话的预期含义。

  • 执行字符集很可能比环境提供的输入字符集大或不兼容。存在三元组和替代标记以允许在此类受限平台上以较少的输入字符表示执行集字符(例如,not 在所有目的上都与! 相同,而后者并非在所有字符集或键盘布局中都可用)。

【讨论】:

    【解决方案2】:

    过去,某些国家/地区的 ASCII 变体(例如斯堪的纳维亚语言)使用重音字母字符作为美国 ASCII 标点符号的代码点,例如 []{、@ 987654325@。这就是 C89 包含三元组的原因——它们允许将代码编写在 ISO 646 的“不变子集”中。请参阅维基百科页面上的国家变体中使用的字符图表。

    例如,斯堪的纳维亚的某人可能需要阅读:

    #include <stdio.h>
    
    int main(int argc, char **argv)
    Å
        for (int i = 1; i < argc; i++)
            printf("%s\n", argvÆiØ);
        return 0;
    ø
    

    代替:

    #include <stdio.h>
    
    int main(int argc, char **argv)
    {
        for (int i = 1; i < argc; i++)
            printf("%s\n", argv[i]);
        return 0;
    }
    

    使用三元组,你可以这样写:

    ??=include <stdio.h>
    
    int main(int argc, char **argv)
    ??<
        for (int i = 1; i < argc; i++)
            printf("%s??/n", argv??(i??));
        return 0;
    ??>
    

    这在任何语言中都同样可怕。

    我不确定这仍然是一个多大的问题,但这就是 cmets 存在的原因。

    【讨论】:

    • 非常感谢您的回答。这一点我还不是很清楚。标准对包含所有这些字符的基本源字符集和基本执行字符集(char 类型的对象保证能够容纳)的要求是什么意思?
    • 我认为这是一个“理论遇见实践”的案例;实践,理论”。该标准规定了现实世界中并不总能满足的要求。
    • 在标准中基本源字符集的 91 个字符列表之后的脚注中,它说“基本源字符集成员的字形旨在识别来自ISO/IEC 10646 对应 ASCII 字符集。但是,由于从源文件字符到源字符集的映射(在翻译阶段 1 中描述)被指定为实现定义,因此需要一个实现来记录基本源字符在源文件中表示。”
    • 对于翻译阶段 1,标准规定:“物理源文件字符以实现定义的方式映射到基本源字符集(引入换行符以行指示符)(如有必要)。接受的物理源文件字符集是实现定义的。三字符序列 (2.4) 被相应的单字符内部表示替换。"
    • 结合这些双引号和您的示例,可以安全地猜测 TC++PL 在我的原始帖子中表达的关于 {}[] 不可用的担忧主要是关于物理源文件字符?当涉及到基本源字符集和基本执行字符集时,这些字符总是存在的。这是正确的吗?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-08-13
    • 1970-01-01
    • 2010-10-16
    相关资源
    最近更新 更多