Eri*_*hil 5 c char literals language-lawyer
C 2018 标准对十六进制转义序列(例如 )的值指定了什么'\\xFF'?
考虑一个 C 实现,其中char是有符号的和八位。
第 6.4.4.4 节告诉我们有关字符常量的信息。在第 6 段中,它讨论了十六进制转义序列:
\n\n\n十六进制转义序列中反斜杠和字母x后面的十六进制数字被视为整数字符常量的单个字符或宽字符常量的单个宽字符的构造的一部分。如此形成的十六进制整数的数值指定所需字符或宽字符的值。
\n
十六进制整数为 \xe2\x80\x9cFF\xe2\x80\x9d。根据十六进制表示法的通常规则,其值1为 255。请注意,到目前为止,我们还没有特定的类型: A \xe2\x80\x9ccharacter\xe2\x80\x9d 是 \xe2\x80\x9c 的成员用于组织、控制或表示数据\xe2\x80\x9d (3.7) 或适合字节\xe2\x80\x9d (3.7.1) 的\xe2\x80\x9cbit 表示的一组元素。当\\xFF被用在 中时'\\xFF',它是语法(6.4.4.4 1)中的c-char'\\xFF' ,并且是整型字符常量。根据 6.4.4.4 2, \xe2\x80\x9c 整数字符常量是用单引号括起来的一个或多个多字节字符的序列,如\xe2\x80\x99x\xe2\x80\x99 .\xe2\x80\ x9d
6.4.4.4 9 指定对字符常量的约束:
\n\n\n八进制或十六进制转义序列的值应在相应类型的可表示值范围内:
\n
接下来是一个表,对于没有前缀的字符常量,显示相应的类型是unsigned char。
到目前为止,一切都很好。我们的十六进制转义序列的值为 255,该值在unsigned char.
然后 6.4.4.4 10 旨在告诉我们字符常量的值。我在这里引用它,将其句子分开并标记以供参考:
\n\n\n(i) 整型字符常量的类型为int。
\n(ii) 包含映射到单字节执行字符的单个字符的整型字符常量的值是解释为整数的映射字符表示形式的数值。
\n(iii) 包含多个字符的整型字符常量的值(例如,\xe2\x80\x99ab\xe2\x80\x99),或者包含不映射到单字节执行字符的字符或转义序列,是实现定义的。
\n(iv) 如果整型字符常量包含单个字符或转义序列,则其值是当char类型的对象(其值为单个字符或转义序列的值)转换为int类型时得到的值。
\n
如果 255 映射到执行字符,则 (ii) 适用,并且 的值'\\xFF'是该字符的值。这是标准中第一次使用\xe2\x80\x9cmaps\xe2\x80\x9d;它没有在其他地方定义。它是否应该意味着除了从迄今为止导出的值(255)到具有相同值的执行字符的映射之外的任何内容?如果是这样,要应用 (ii),必须有一个值为 255 的执行字符。那么 的值为'\\xFF'255。
否则 (iii) 适用,并且 的值'\\xFF'是实现定义的。
无论(ii)或(iii)是否适用,(iv)也适用。它表示 的值是值为 255 的对象'\\xFF'的值,随后转换为。但是,由于是有符号的且是 8 位的,因此不存在值为 255 的对象。因此第四句话说明了不可能。charintcharchar
1 3.19 将 \xe2\x80\x9cvalue\xe2\x80\x9d 定义为 \xe2\x80\x9c 当解释为具有特定类型时对象内容的精确含义,\xe2\x80\x9d 但我不相信技术上的这里使用这个词。\xe2\x80\x9c十六进制整数的数值\xe2\x80\x9d还没有讨论的对象。这似乎是在普通意义上使用 \xe2\x80\x9cvalue\xe2\x80\x9d 一词。
\n您的演示得出了一个有趣的结论:
没有可移植的方法来写入值超出范围的字符常量
0......CHAR_MAX对于单个字符来说这不一定是问题,因为可以使用整数代替字符常量,但对于字符串常量则没有这样的替代方案。
char为了与许多标准 C 库函数保持一致,默认情况下类型应该始终是无符号的:
fgetc()如果失败,则返回int负值;如果成功读取字节,则EOF返回 an 值。unsigned char因此,其含义和效果fgetc() == '\xFF'是由实现定义的。
函数<ctype.h>接受int与返回值相同的参数fgetc()。传递负值char具有未定义的行为。
strcmp()并根据转换为 的字符值比较字符串unsigned char。
'\xFF'可能具有完全不直观的值-1,并且可能与 的值相同EOF。
默认情况下进行签名或保持char签名的唯一原因是与旧版编译器兼容,以支持依赖此行为的历史代码,并且是在signed char大约 30 年前的 出现之前编写的!
我强烈建议程序员默认使用 unsigned,如果需要带符号的 8 位变量和结构成员,则使用-funsigned-char或更好。charsigned charint8_t
正如海德评论的那样,为了避免可移植性问题,char值应该被转换为可能引起问题(unsigned char)的符号char:例如:
char str[] = "Hello world\n";
for (int i = 0; str[i]; i++)
str[i] = tolower((unsigned char)str[i]);
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
475 次 |
| 最近记录: |