char如何存储两个数字?

时间:2018-12-05 22:59:06

标签: c char cyrillic

接下来的情况:我有西里尔文符号“б”。运行下一个代码:

int main() {
    char c;
    scanf("%c", &c);
    printf("%d\n", c);
    return 0;
}

显示-48。但是,当我调试此变量c时,下一个显示给我:-48 '\320' enter image description here

那么这如何工作?这是2长度数组的指针吗?或者它如何存储两个数字?

2 个答案:

答案 0 :(得分:5)

char变量既可以用于存储小的 1 整数,也可以用于存储一些定义不太明确的字符(更恰当的说是代码单元),通常是-基于ASCII的编码。在这里,调试器仅试图通过显示c内容的两个(有争议的)有意义的表示来提供帮助。


让我们想象一下,您实际上写了a而不是б;在这种情况下,调试器将编写类似

的内容
c = {char} 97 'a'

因为存储在c中的实际数字是97,并且被解码为ASCII,所以它对应于字母a

不幸的是,您可以将所有可能的字符放入单个8位char值的想法是完全错误的,因此,当今使用最广泛的编码(UTF-8)恰好是在您的计算机上使用,需要多个代码单位(≈字节)来表示单个代码点(≈逻辑字符)(更多详细信息in this question)。特别地,б表示为一个两个字节的字符串,即字节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中的一到三位表示具有相应 octal 值的字节; 0320实际上对于208是八进制的,对于0xD0是十进制的。

因此,这里没有什么神秘之处:c仍然包含一个值(仅是您字符的“一半”);您所看到的只是其内容的两种(相当不便)表示。


注释

  1. 在大多数平台上,[{-128,127]或[0,255],取决于char的签名(不幸的是,它是实现定义的)。
  2. 实际上,UTF-8 扩展 ASCII码,其多字节序列仅使用设置了高位(未使用ASCII)的字节;这样可以确保它们不会被误解为ASCII文本。

答案 1 :(得分:3)

[使用utf-8的西里尔字符为多字节 char。您的十六进制“字符”是字符串/数组:

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

以下是输出:

б
 D0 B1

更新:

  

那么,该char如何在内存中定位?西里尔字母C能否使char变为2字节?

首先,请参见:https://en.wikipedia.org/wiki/UTF-8

当您通过键盘输入西里尔字母时,它是键盘硬件,终端仿真器程序和文本编辑器的组合,可以将键盘序列转换为utf-8序列,并最终存储在您的文本文件中正在编辑。

您所说的西里尔字符是utf-8所称的“代码点”。

放置在文本文件中时,代码点将变为多字节序列,如上所述。

scanfprintf对此一无所知。例如,printf仅发送字符串:XXXXXXX\0,其中X可以是单个ASCII字符或多字符代码点的一部分。

这取决于终端仿真器并从utf-8字体集中输出正确的字符(该字体集包含西里尔字符,希腊字符,法语字符等)

strlenstrcpy之类的功能 only 关心尾随的0x00 EOS字符。因此,从技术上讲,它们可以正常工作,并且通常可以像通过EOS一样容易地通过utf-8字符串和ASCII一样通过。

但是,strlen将为您提供字符串中char的数量。例如,在上面的strlen中将返回2,因为它将D0B1作为char数组中单独的char值进行计数。

而且,strchr [可能]不起作用。您可能想在strstr的地方使用utf-8

当然,其中的西里尔字符只有一个 代码点,因此utf-8感知功能必须以不同的方式处理数组。例如,在计算代码点数时,他们需要查看D0B1是一个单个代码点,因此结果计数为一个

一般规则是ASCII(0x01-0x7F)作为单个utf-8直接映射到char。设置了高位(0x80)的任何内容都是utf-8多字节代码点的一部分。 0x40用于指示序列的 start [最左侧]字节。序列中所有其余字节的格式(以位为单位):10xxxxxx。序列中剩余字节的数量由起始字节中前缀1位的数量表示。在下表中,它显示了如何解码字节序列(x表示代码点值的一部分):

# of    Start       Remaining Bytes
bytes   Byte
1       0xxxxxxx
2       110xxxxx    10xxxxxx
3       1110xxxx    10xxxxxx    10xxxxxx
4       11110xxx    10xxxxxx    10xxxxxx    10xxxxxx

因此,utf-8感知功能可以在向前或向后扫描时检测并跳过代码点。并且,可以区分两个[或更多]相邻的多字节代码点。