ASCII码表字母大小写相差32的原因分析

思考起因

直入主题,谈一下思考的起因,从实际应用探寻原因。

下面是一道经典的“十六进制转八进制”题目(摘选自蓝桥杯题库):

 

经典例题,题目不难,引用一份c语言实现的代码(代码出处https://www.cnblogs.com/panweiwei/p/6478705.html,博客园,作者AllSight):

#include<stdio.h>
#include<string.h>

char h[100002],b[400002],e[400002];
int main(){
    int n;
    scanf("%d",&n);
    while(n--){
        scanf("%s",h);
        int i,len=0;
        /*先把16进制化成二进制——从后往前展开 */
        for(i=strlen(h)-1;i>=0;i--){
            int v;
            if(h[i]>='0' && h[i]<='9')
                v=h[i]-'0';
            else v=h[i]-'A'+10;
            for(int j=0;j<4;j++){
                b[len++]=v%2+'0';
                v/=2;
            }
        }
        b[len]='\0';
        int x=0,cnt=1;
        int l=0;
        for(i=0;i<len;i++){
            /*每三位二进制转成一位8进制,最后不足三位补0*/
            if(cnt==4||i==len-1){
                x=cnt*(b[i]-'0')+x;
                cnt=1;
                e[l++]=x+'0';
                x=0;
            } else{
                x=cnt*(b[i]-'0')+x;
                cnt*=2;
               }
        }
        i=l-1;
        while(i>=0 && e[i]=='0')
            /*去掉前导0*/
            i--;
        if(i<0)
            printf("0");
        for (;i>=0;i--){
            printf("%c",e[i]);
        }
        printf("\n");
    }
    return 0;
}

 

这里只讨论与主题有关的前半段,即十六进制→二进制的部分,9~20行。这部分代码读一个键盘敲入的十六进制数,以字符形式储存在字符数组h中,再将该字符/字符串转换为二进制表示。为了便于观察,我们把9~20行单独提取出来,如下:

#include<stdio.h>
#include<string.h>

int main()
{
    char h[100002],b[400002];
    scanf("%s",h);
    int i,len=0;
    /*先把16进制化成二进制——从后往前展开 */
    for(i=strlen(h)-1;i>=0;i--){
        int v;
        if(h[i]>='0' && h[i]<='9')
            v=h[i]-'0';
        else v=h[i]-'A'+10;
        for(int j=0;j<4;j++){
            b[len++]=v%2+'0';
            v/=2;
        }
    }
    return 0;
}

 

我们来看一下if判断部分:

博主最初的思路是:

1.判断是否为数字0~9,如果是,执行v=h[i]-'0';如果否,继续判断

2.判断是否为大写,如果是,执行v=h[i]-'A';如果否,继续判断

3.判断是否为小写,如果是,执行v=h[i]-'a';

但发现这份代码并没有判断区分大写/小写,大小写字母统统执行v=h[i]-'A';

这里引起了博主的兴趣,设输入字符为'F'与'f',断点设在第14行处,进行数值监测:

输入'F'

只关注v的取值,15

输入'f'

只关注v的取值,47

 

v取值完毕,继续执行15~18行代码:

for (int j = 0; j<4; j++) {
    b[len++] = v % 2 + '0';
    v /= 2;
}

这段代码会把十进制v转化成四位二进制储存在数组b中。重要的一点,这里转化得到的四位二进制,是v二进制形式的低四位

15对应二进制:0000 1111

47对应二进制:0010 1111

在本次输入'F'与'f'举例中,这就是并没有判断区分大写/小写的原因,15和47二进制形式的低四位相同。而四位二进制,恰好可以表示一个十六进制。

一份ASCII码表字母大小写对照,大写与小写二进制形式的低四位相同,验证普遍性(表格出处http://ascii.911cha.com/911查询)

至此,可以体会ASCII码表字母大写小写相差32的便利之处了。

 

思考分析

为什么ASCII码表字母大小写相差32,会产生“巧合”呢?

32对应二进制:0010 0000

它的低四位是0000,这意味着,与大写/小写字母二进制形式做加减法时,大写/小写字母二进制形式的低四位不会改变。

例如,'A'+32,二进制加法:

同理,可推广至A~Z,a~z。

其实使用+16/-16:即+0001 0000/-0001 0000,也可以实现大写/小写字母二进制形式的低四位不被改变。不过,16个字符不够26个英文字母使用,因此采用+32/-32,即+0010 0000/-0010 0000,作为ASCII码表中字母大小写间的差值。


版权声明:本文为weixin_43735348原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。