char如何存储两个数字?
下一个例子:我有西里尔字母“б”。运行下一个代码:char如何存储两个数字?,c,char,cyrillic,C,Char,Cyrillic,下一个例子:我有西里尔字母“б”。运行下一个代码: int main() { char c; scanf("%c", &c); printf("%d\n", c); return 0; } 显示-48。但是,当我调试这个变量c时,它接下来会显示:-48'\320' 那么这是如何工作的呢?这是指向2长度数组的指针吗?或者它如何能够存储两个数字?西里尔字符[使用utf-8]是多字节chars。十六进制的“字符”是字符串/数组: D0B1 因此,您不能使用%
int main() {
char c;
scanf("%c", &c);
printf("%d\n", c);
return 0;
}
显示-48
。但是,当我调试这个变量c
时,它接下来会显示:-48'\320'
那么这是如何工作的呢?这是指向2长度数组的指针吗?或者它如何能够存储两个数字?西里尔字符[使用
utf-8
]是多字节char
s。十六进制的“字符”是字符串/数组:
D0B1
因此,您不能使用%c
检索它。您需要使用%s
:
#include <stdio.h>
int
main(void)
{
char utf[1000];
char *cp;
scanf("%s", utf);
printf("%s\n", utf);
for (cp = utf; *cp != 0; ++cp)
printf(" %2.2X",*cp & 0xFF);
printf("\n");
return 0;
}
更新:
那么,这个字符是如何定位在内存中的呢?当涉及西里尔文时,C是否能够生成字符2字节 首先,请参见: 通过键盘输入西里尔字符时,键盘硬件、终端仿真器程序和文本编辑器的组合将键盘序列转换为
utf-8
序列,并最终生成正在编辑的文本文件
你所说的西里尔字符就是utf-8
所说的“代码点”
当放置在文本文件中时,代码点将变成如上所述的多字节序列
scanf
和printf
对此一无所知。例如,printf
只发送字符串:XXXXXXX\0
,其中X可以是单个ASCII字符或多字符代码点的一部分
由终端仿真器来理解这一点,并从utf-8字体集中输出正确的字符[其中包含西里尔字母、希腊字母、法语字母等]
诸如strlen
和strcpy
之类的函数只关心尾随的0x00 EOS字符。因此,从技术上讲,它们可以工作,并且通常可以像ASCII字符串一样轻松地通过utf-8
字符串,因为EOS是相同的
但是,strlen
将为您提供字符串中char
的编号。例如,在上面的strlen
中将返回2,因为它将D0
和B1
作为char
数组中的单独char
值进行计数
而且,strchr
[可能]不会起作用。您可能希望在utf-8
中使用strstr
当然,其中西里尔字符只有一个代码点,因此utf-8
aware函数必须以不同的方式处理数组。例如,在计算代码点的数量时,他们需要看到D0B1
是单个代码点,因此结果计数为一
一般规则是ASCII(0x01-0x7F
)直接映射到utf-8
,作为单个char
s。设置高位(0x80
)的任何内容都是utf-8
多字节代码点的一部分。0x40
用于指示序列的起始[最左边]字节。序列中所有剩余字节的格式(以位为单位):10xxxxxx
。序列中剩余字节的数量由起始字节中前缀1位的数量表示。下表显示了如何解码字节序列(x
表示作为代码点值一部分的位):
因此,
utf-8
aware功能可以在正向或反向扫描时检测并跳过代码点。并且,可以区分两个[或多个]相邻的多字节代码点。一个char
变量可以用于存储一个小整数,或者在一些定义不太明确、通常基于ASCII的编码中存储一个字符(更恰当地说,是代码单位)。这里,调试器只是试图通过显示c
内容的两个(有争议的)有意义的表示来提供帮助
让我们想象一下,您实际上编写了
a
,而不是б
;在这种情况下,调试器将编写如下内容
c = {char} 97 'a'
因为c
中存储的实际数字是97,并且被解码为ASCII,它对应于字母a
不幸的是,在一个8位char
值中可以容纳所有可能的字符的想法是完全有缺陷的,因此目前使用最广泛的编码(UTF-8)需要多个代码单元,而UTF-8恰好是您机器上使用的编码(≈字节)来表示单个代码点(≈逻辑字符(更多细节)。具体而言,б表示为两个字节的字符串,即字节0xD0和0xB1
C对UTF-8或代码点一无所知;如果将%c
指定为scanf
,则无论是否足以表示完整的UTF-8码点,它都以单个字节读取。因此,只有第一个字节被读取,c
只包含0xD0值;0xB1仍在缓冲区中,尚未读取
回到调试器显示的值,首先必须注意,在您的平台上(不幸的是,在许多平台上),char
是有符号的。因此,0xD0字节被解释为带符号值-48(实际上,0xD0=208,在127处“环绕”;208-256=-48)
至于'\320'
:这里的调试器希望显示该值的ASCII表示形式;但是,字节0xD0不在ASCII字符范围2内,因此在这里它将以转义序列显示。您可能熟悉用'\n'
表示换行符,或用\0
表示NUL字符;一般来说,C中一个\
后跟一到三位数字表示具有相应八进制值的字节0320实际上是208的八进制,这是0xD0的十进制
因此,这里没有什么神秘之处:c
仍然包含一个值(它只是角色的“一半”);您看到的只是其内容的两种(同样不方便)表示
注释
在大多数平台上
# of Start Remaining Bytes
bytes Byte
1 0xxxxxxx
2 110xxxxx 10xxxxxx
3 1110xxxx 10xxxxxx 10xxxxxx
4 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
c = {char} 97 'a'