C和C++是否保证[af]和[AF]字符的ASCII?

jww*_*jww 24 c c++ ascii

我正在查看以下代码来测试十六进制数字并将其转换为整数.代码有点聪明,因为它利用大写字母和低位字母之间的差异是32,而那是第5位.所以代码执行一个额外的OR,但保存一个JMP和两个CMPs.

static const int BIT_FIVE = (1 << 5);
static const char str[] = "0123456789ABCDEFabcdef";

for (unsigned int i = 0; i < COUNTOF(str); i++)
{
    int digit, ch = str[i];

    if (ch >= '0' && ch <= '9')
        digit = ch - '0';
    else if ((ch |= BIT_FIVE) >= 'a' && ch <= 'f')
        digit = ch - 'a' + 10;
    ...
}
Run Code Online (Sandbox Code Playgroud)

C和C++是否保证[af]和[AF]字符的ASCII或值?这里,保证意味着上下字符集总是相差一个常数值,可以用一个位表示(对于上面的技巧).如果没有,标准对他们说了什么?

(对不起C和C++标签.我对两种语言在这个问题上的立场感兴趣).

Kei*_*son 37

不,不是的.

C标准保证了十进制数字和大写和小写字母存在,与一些其他字符一起.例如,它还保证十进制数字是连续的,'0' + 9 == '9'并且基本执行字符集的所有成员都具有非负值.它专门针对保证该信件是连续的.(有关所有血腥细节,请参阅C标准的N1570草案,第5.2.1节;基本字符为非负的保证在6.2.5p3中,在类型的讨论中char.)

假设'a'...... 'f''A'.. 'F'具有连续的代码几乎肯定是合理的.在ASCII和所有基于ASCII的字符集中,26个小写字母是连续的,26个大写字母也是连续的.即使在EBCDIC,唯一的显著对手ASCII,字母作为一个整体是不连续的,而是字母'a'.. 'f''A'.. 'F'是(EBCDIC具有之间的空隙'i''j'之间,'r''s'之间,'I''J'之间,以及'R''S').

然而,假设设置代表性的第5位将转换大写字母为小写字母是不是有效的EBCDIC.在ASCII中,小写和大写字母的代码相差32; 在EBCDIC中他们相差64.

在标准库的一部分代码或已知对性能至关重要的代码中,这种用于保存一两条指令的比特可能是合理的.基于ASCII的字符集的隐含假设至少应该通过注释明确表示恕我直言.256元素的静态查找表可能会以更少的额外存储为代价甚至更快.

  • 如果需要100%保证,可以使用`#if`来验证这些字母是连续的,如果不是则会触发错误.最好在编译时失败而不是运行时. (5认同)
  • @Boann :(更正我以前的评论)不,这不能保证工作.该标准明确指出`#if'z' - 'a'== 25`不一定与`if('z' - 'a'== 25)`的评价方式相同; 它可以在编译时和运行时使用不同的字符集.[N1570](http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf)6.10.1. (5认同)
  • 一个人必须检查所有字符.从理论上讲,可能存在一个狂热的字符集,其中a..z不是连续的,但是z与a意外地具有相同的距离. (2认同)

fre*_*low 23

为了获得最大的便携性,清晰度和速度,我建议一个简单的开关:

int hex_digit_value(char x)
{
    switch (x)
    {
    case '0': return 0;
    case '1': return 1;
    case '2': return 2;
    case '3': return 3;
    case '4': return 4;
    case '5': return 5;
    case '6': return 6;
    case '7': return 7;
    case '8': return 8;
    case '9': return 9;
    case 'A':
    case 'a': return 10;
    case 'B':
    case 'b': return 11;
    case 'C':
    case 'c': return 12;
    case 'D':
    case 'd': return 13;
    case 'E':
    case 'e': return 14;
    case 'F':
    case 'f': return 15;
    default: return -1;
    }
}
Run Code Online (Sandbox Code Playgroud)

clang -O1 -S 将其转换为简单的表查找:

    addl    $-48, %edi
    cmpl    $54, %edi
    ja  .LBB0_2

    movslq  %edi, %rax
    movl    .Lswitch.table(,%rax,4), %eax
    retq
.LBB0_2:
    movl    $-1, %eax
    retq
Run Code Online (Sandbox Code Playgroud)

为完整起见,这是生成的查找表:

.Lswitch.table:
.long   0                       # 0x0
.long   1                       # 0x1
.long   2                       # 0x2
.long   3                       # 0x3
.long   4                       # 0x4
.long   5                       # 0x5
.long   6                       # 0x6
.long   7                       # 0x7
.long   8                       # 0x8
.long   9                       # 0x9
.long   4294967295              # 0xffffffff
.long   4294967295              # 0xffffffff
.long   4294967295              # 0xffffffff
.long   4294967295              # 0xffffffff
.long   4294967295              # 0xffffffff
.long   4294967295              # 0xffffffff
.long   4294967295              # 0xffffffff
.long   10                      # 0xa
.long   11                      # 0xb
.long   12                      # 0xc
.long   13                      # 0xd
.long   14                      # 0xe
.long   15                      # 0xf
.long   4294967295              # 0xffffffff
.long   4294967295              # 0xffffffff
.long   4294967295              # 0xffffffff
.long   4294967295              # 0xffffffff
.long   4294967295              # 0xffffffff
.long   4294967295              # 0xffffffff
.long   4294967295              # 0xffffffff
.long   4294967295              # 0xffffffff
.long   4294967295              # 0xffffffff
.long   4294967295              # 0xffffffff
.long   4294967295              # 0xffffffff
.long   4294967295              # 0xffffffff
.long   4294967295              # 0xffffffff
.long   4294967295              # 0xffffffff
.long   4294967295              # 0xffffffff
.long   4294967295              # 0xffffffff
.long   4294967295              # 0xffffffff
.long   4294967295              # 0xffffffff
.long   4294967295              # 0xffffffff
.long   4294967295              # 0xffffffff
.long   4294967295              # 0xffffffff
.long   4294967295              # 0xffffffff
.long   4294967295              # 0xffffffff
.long   4294967295              # 0xffffffff
.long   4294967295              # 0xffffffff
.long   4294967295              # 0xffffffff
.long   10                      # 0xa
.long   11                      # 0xb
.long   12                      # 0xc
.long   13                      # 0xd
.long   14                      # 0xe
.long   15                      # 0xf
Run Code Online (Sandbox Code Playgroud)

  • @Zaibis:那不是Duff的装置. (4认同)

Die*_*Epp 18

对特定值没有任何保证,但您不应该关心,因为您的软件可能永远不会遇到以这种方式与ASCII不兼容的系统.假设空间总是32并且A总是65,这在现代世界中工作得很好.

C标准仅保证字母AZ和az存在,并且它们适合单个字节.

它确保0-9是顺序的.

在源和执行基本字符集中,上述十进制数字列表中0之后的每个字符的值应比前一个值大1.

理由

世界上有很多字符编码.如果您关心可移植性,可以将程序移植到不同的字符集,也可以选择一个字符集在任何地方使用(例如Unicode).我将继续并为您分类大多数现有的字符编码:

  1. 单字节字符编码与ISO/IEC 646兼容.数字0-9和字母AZ和az总是占据相同的位置.

  2. 多字节字符编码(Big5,Shift JIS,基于ISO 2022).在这些编码中,您的程序可能已经损坏,如果您愿意,您需要花时间修复它.但是,解析数字仍将按预期工作.

  3. Unicode编码.数字0-9和字母AZ,az总是占据相同的位置.您可以自由使用代码点或代码单元,如果您使用的代码点低于128(您就是这样),您将获得相同的结果.(您使用的是UTF-7吗?不,您应该只将其用于电子邮件.

  4. EBCDIC.数字和字母的分配值与ASCII中的值不同,但0-9和AF,af仍然是连续的.即便如此,您的代码在EBCDIC系统上运行的可能性基本上为零.

所以这里的问题是:你是否认为将来会发明一个假设的第五个选项,以某种方式与Unicode不兼容/更难以使用?

你关心EBCDIC吗?

我们可以整天梦想奇怪的系统......假设CHAR_BIT是11,或者sizeof(long) = 100,或者假设我们使用一个补码算术,或者malloc()总是返回NULL,或者假设显示器上的像素排列成六边形网格.假设您的浮点数不是IEEE 754,假设您的所有数据指针都是不同的大小.在一天结束时,这并没有让我们更接近我们在实际现代系统上编写工作软件的目标(偶尔例外).

  • 哇,教导别人不要抽象思考的方法.这是一个_awful_答案,无论事实多么冷酷. (10认同)
  • @jww:"未定义的行为"意味着`ch> ='a'&& ch <='f'`理论上可以使程序崩溃,重新格式化你的硬盘,并喷涂你的猫粉红色."实施定义"意味着它取决于实施.实现具有字符编码,字符编码的选择决定了这些问题的答案.请注意,没有人实际使用AF不连续的编码,所以没人关心. (9认同)
  • @DietrichEpp:请改变*"但你不应该关心"*到*"在实践中,在今天遇到的最常见的平台上,"*.如果您了解它(并且理想情况下,static_assert),平台依赖性就可以了.你的答案是好的,但是第一句让我的脚趾甲卷起来,这根本不是一个好看的. (6认同)
  • @LightningRacisinObrit:我很难理解你怎么能认为编写可移植代码与编写非可移植代码相比没有成本......通常,你看起来很精明. (4认同)
  • @LightningRacisinObrit:我不认为这里的目标是教人们在不需要抽象时进行抽象思考。编写可移植代码是有成本的,而编写可移植到既不兼容 ASCII 也不兼容 EBCDIC 但某些*假设的第三个选项* 的字符集的代码的好处是零,除非有人可以另外证明。EBCDIC 可移植性的好处通常也是零。 (2认同)
  • "编写可移植代码没有任何成本"显然是不正确的.可移植性是一种约束,其他约束会增加项目成本.你没有理由为这个陈述辩护,我想知道你的理由是什么.也许你不是一般性地发言而是谈论某些具体的案例,但话说回来,声明不合格,我没有看到任何明显的隐含资格. (2认同)