以下代码在Windows控制台正常输出会出现乱码
#include<stdio.h>
int main(){
char a=197;
printf("%c",a);
return 0;
}
为什么呢?
1. 我们先说明前提条件:C语言中char类型默认是有符号类型(范围是-128~127之间),当然也可以声明无符号类型的(unsigned char),这不在本文讨论范围之内。
在上列中,a = 197
对应的字符是在标准ASCII表
中是不存在的,标准ASCII表
只定义了0~127
的字符,而我们都知道一个字节可以表示0~255
,因此多余的字节ASCII并没有使用,而是作为了扩展编码。因此之所以会输出问号或则其它乱码符号,是因为我们的ASCII编码中没有这个位置的编码符号。
2. 接下来接着说,我们中文家庭版的Windows10电脑里面,CMD中默认的编码格式是936(ANSI/OEM - 简体中文GBK)
通过以下一个引用大家可以高明白GBK编码和ASCII编码的关系。
【1】ASCII: 每个字符占据1bytes,用二进制表示的话最高位必须为0(扩展的ASCII不在考虑范围内),因此ASCII只能表示128个字
【2】GB2312: 最早一版的中文编码,每个字占据2bytes。由于要和ASCII兼容,那这2bytes最高位不可以为0了(否则和ASCII会有冲突)。在GB2312中收录了6763个汉字以及682个特殊符号,已经囊括了生活中最常用的所有汉字。
【3】GBK: 由于GB2312只有6763个汉字,我汉语博大精深,只有6763个字怎么够?于是GBK中在保证不和GB2312、ASCII冲突(即兼容GB2312和ASCII)的前提下,也用每个字占据2bytes的方式又编码了许多汉字。经过GBK编码后,可以表示的汉字达到了20902个,另有984个汉语标点符号、部首等。值得注意的是这20902个汉字还包含了繁体字。
补充:
【4】GB18030: 然而,GBK的两万多字也已经无法满足我们的需求了,还有更多可能你自己从来没见过的汉字需要编码。这时候显然只用2bytes表示一个字已经不够用了(2bytes最多只有65536种组合,然而为了和ASCII兼容,最高位不能为0就已经直接淘汰了一半的组合,只剩下3万多种组合无法满足全部汉字要求)。因此GB18030多出来的汉字使用4bytes编码。当然,为了兼容GBK,这个四字节的前两位显然不能与GBK冲突(实操中发现后两位也并没有和GBK冲突)。我国在2000年和2005年分别颁布的两次GB18030编码,其中2005年的是在2000年基础上进一步补充。至此,GB18030编码的中文文件已经有七万多个汉字了,甚至包含了少数民族文字。
-
如果需要显示出来128~255的字符,我们就需要将控制台的编码格式改变
,比如改成美国的一种编码标准:437(OEM-美国),就可以正常显示显示SCII码中128-255的特殊字符。 -
最简单的方法就是在程序中加上以下的头文件和命令就可以修改代码页的默认显示方式:
#include<windows.h>
#include<stdio.h>
int main(){
SetConsoleOutputCP(437);
char a=197,b=198;
printf("%c %c",a,b);
}
- 通过以上修改,就可以正常显示197和198对应的字符,分别是 ┼ 和 ╞ ,当然这只是在一种编码格式下,如果再更换一种编码格式,可能又不一样。
3. 除此之外,C语言中,当char是有符号类型的时,即表示范围为-128~127。在有符号char类型当中0 ~ 127和-128~-1这两段在存储上分为两个不同的段。
十进制 | 对应的补码表示 |
---|---|
-128 | 1000 0000 |
-127 | 1000 0001 |
-126 | 1000 0010 |
-125 | 1000 0011 |
… | … |
-2 | 1111 1110 |
-1 | 1111 1111 |
0 | 0000 0000 |
1 | 0000 0001 |
… | … |
127 | 0111 1111 |
这就是计算机中采用的补码表示法,二进制最高位代表符号位,1为负数,0为整数。
综上所述,数据在计算机中的存储形式是以补码的形式存储的,并且在C语言中有符号的char型,其负数段与正数段在内存的存储形式不一样(最高位),但其任然满足溢出取模原理
。
如有不足,欢迎指出。