C和C ++是否保证和字符的ASCII?

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

 static const int BIT_FIVE = (1 << 5); static const char str[] = "0123456789ABCDEFabcdef"; for (unsigned int i = 0; i = '0' && ch = 'a' && ch <= 'f') digit = ch - 'a' + 10; ... } 

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

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

没有关于特定值的保证,但您不应该在意,因为您的软件可能永远不会遇到以这种方式与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,假设您的所有数据指针都是不同的大小。 在一天结束时,这并没有让我们更接近我们在实际现代系统上编写工作软件的目标( 偶尔例外)。

不,不是的。

C标准保证存在十进制数字和大写和小写字母,以及许多其他字符。 它还保证十进制数字是连续的,例如'0' + 9 == '9' ,并且基本执行字符集的所有成员都具有非负值。 它特别不保证字母是连续的。 (有关所有血腥细节,请参阅C标准的N1570草案,第5.2.1节;基本字符为非负的保证在6.2.5p3中,在char类型的讨论中。)

'a''f''A' …… 'F'具有连续代码的假设几乎肯定是合理的。 在ASCII和所有基于ASCII的字符集中,26个小写字母是连续的,26个大写字母也是连续的。 即使在作为ASCII的唯一重要竞争对手EBCDIC中 ,整个字母表也不是连续的,但字母'a' …… 'f''A' …… 'F'是(EBCDIC在'i''j''r''s'之间, 'I''J'之间, 'R''S' )。

但是,假设将表示的第5位设置为将大写字母转换为小写字母对EBCDIC无效。 在ASCII中,小写和大写字母的代码相差32; 在EBCDIC中他们相差64。

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

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

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

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 

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

 .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