[深入浅出C语言]隐式类型转换与例题

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第6天,点击查看活动详情

前言

        本文是讲解数据类型的第二篇,在学习了第一篇后来看看这一篇效果会更好。本篇主要讲解了隐式类型转换的分类和具体操作,佐以部分例题讲解,是以笔者学习经验和心得为基础的,新手上路,文章拙劣而纰漏难免,欢迎指正,希望于你有益。


隐式类型转换

        有些表达式的操作数在求值的过程中可能需要转换为其他类型,然而在进行这种类型转换的时候是没有用户命令而自动进行的,没有直接展现出来类型发生过变化,所以叫隐式类型转换

整型提升是什么

        C的整型算术运算总是至少以缺省整型类型的精度(至少是以int类型)来进行的。

        为了获得这个精度,表达式中的字符char和短整型short操作数在使用之前被转换为普通整型int,这种转换称为整型提升

整型提升的意义

        表达式的整型运算要在CPU的相应运算器件内执行,CPU内整型运算器(ALU)的操作数的字节长度一般就是int的字节长度,同时也是CPU的通用寄存器的长度。

        因此,即使两个char类型的数相加,在CPU执行时实际上也要先转换为CPU内整型操作数的标准长度int字节长度。通用CPU(general-purpose CPU)是难以直接实现两个8比特字节直接相加运算(虽然机器指令中可能有这种字节相加指令)。

        所以,表达式中各种长度可能小于int长度的整型值,都必须先转换为int或unsigned int,然后才能送入CPU去执行运算。

        换句话说就是char和short类型太小了,不能直接拿来执行运算,得先变长成int字节长度,运算后再截断多余位变回原来字节长度。

如何进行整型提升

        整型提升是按照变量的数据类型的符号位来提升的,有符号类型整型提升高位补符号位1,无符号类型整型提升高位补0。 

(1)负数的整型提升

char c1 = -1;

变量c1的二进制位(补码)中只有8个比特位:

1111111

因为 char 为有符号的 char

所以整型提升的时候,高位补充符号位,即为1

提升之后的结果是:

11111111111111111111111111111111

(2)正数的整型提升

char c2 = 1;

变量c2的二进制位(补码)中只有8个比特位:

00000001

因为 char 为有符号的 char

所以整型提升的时候,高位补充符号位,即为0

提升之后的结果是:

00000000000000000000000000000001

(3)实例1

int main()
{
 char a = 0xb6;
 short b = 0xb600;
 int c = 0xb6000000;

 if(a==0xb6)
     printf("a");
 if(b==0xb600)
     printf("b");
 if(c==0xb6000000)
     printf("c");
 return 0;
}
复制代码

在执行表达式a == 0xb6和b == 0xb600时,a和b要进行整型提升

a   0xb6   1011 0110   补码:1100 1010

整型提升后:

1111 1111 1111 1111 1111 1111 1100 1010

而0xb6的补码:0000 0000 0000 0000 0000 0000 1100 1010

这就不相等了,因为0xb6(182)在char的一个字节中值溢出了(char -128~127)变成了负数,在整型提升后补的又是符号位,所以就与0xb6补码不同。

b   0xb600    1011 0110 0000 0000  补码:1100 1010  0000 0000

整型提升后:

1111 1111 1111 1111 1100 1001  0000 0000

而0xb600的补码:0000 0000 0000 0000 1011 0110 0000 0000

同理:

        a,b整型提升之后,变成了负数,所以表达式 a==0xb6 , b==0xb600 的结果是假,但是c不发生整型提升,则表达式 c==0xb6000000 的结果是真。

(4)实例2

int main()
{
 char c = 1;
 printf("%u\n", sizeof(c));
 printf("%u\n", sizeof(+c));
 printf("%u\n", sizeof(-c));
 return 0;
}
复制代码

        实例2中的,c只要参与表达式运算,就会发生整形提升,而表达式 +c ,会发生提升由char变为int,所以 sizeof(+c) 是4个字节。

        表达式 -c 也会发生整形提升,所以 sizeof(-c) 是4个字节,但是 sizeof(c) ,就是1个字节.

算术转换

long long

double

float

unsigned long int

long int

unsigned int

int
复制代码

        如果某个操作数的类型在上面这个列表中排名较低,那么首先要转换为另外一个排名高的操作数的类型后执行运算。

        其实就是从短的向长的转换后再运算以确保精度,排位越高的实际上精度越高。

        如果让高精度的类型转换成低精度的类型会丢失精度,如

int a = 3.14159;
复制代码

        实际上a的值是3,在转换的过程中小数点后的数字被丢弃。

        而算术转换也是看不见地自动进行的,比如

int a =10;
float  b = 9.34;
double c = a + b;
复制代码

        在a和b运算前,a先要转换成float类型,相加之后的值还要再转换成double类型。

        然而我们确实没看到转换的过程,能看到结果,但有时结果或许会和我们想的不太一样。

一些例题

先看张图理解一下整型的存入与读取

image.png

第一题

//输出什么?
#include <stdio.h>
int main()
{
    char a= -1;
    signed char b=-1;
    unsigned char c=-1;
    printf("a=%d,b=%d,c=%d",a,b,c);
    return 0;
}

复制代码

        在赋值的时候,赋值符右边的常量是自动分配类型的,比如上面代码中的-1被认为是signed int型,那么有:

原码:10000000000000000000000000000001

补码:1111111111111111111111111111111111111

由于char只能存储一个字节的值,所以从低位向高位发生截断

a的补码:11111111

        在printf打印的时候使用的转换说明是%d,这时候需要发生整型提升,在这里char默认是signed char,则有:

补码:1111111111111111111111111111111111111

原码:10000000000000000000000000000001

        打印结果为-1

        对于b而言过程与结果同理,打印结果为-1。

对于c

原码:10000000000000000000000000000001

补码:1111111111111111111111111111111111111

由于char只能存储一个字节的值,所以从低位向高位发生截断

c的补码:11111111

        在printf打印的时候使用的转换说明是%d,这时候需要发生整型提升,又是unsigned char,高位补0,则有:

补码:00000000000000000000000011111111

        被认为是正数,则原码与补码相同,所以打印结果为255。

第二题

#include <stdio.h>
int main()
{
    char a = -128;
    printf("%u\n",a);
    return 0;
}
复制代码

对于-128

原码:10000000000000000000010000000

补码:11111111111111111111111110000000

由于char只能存储一个字节的值,所以从低位向高位发生截断

a的补码:10000000

        在printf打印的时候使用的转换说明是%u,这时候需要发生整型提升,是char,则有:

补码:11111111111111111111111110000000

        打印的时候被认为是无符号数也就是正数,所以原码与补码相同,则打印结果为4294967168。

        要是把%u改成%d得到结果又会是什么呢?

        改成%d则认为是有符号数,符号位是1则为负数,对应原码

10000000000000000000000010000000

        打印结果为-128

第三题

#include <stdio.h>
int main()
{
    char a = 128;
    printf("%u\n",a);
    return 0;
}
复制代码

对于128

原码:00000000000000000000010000000

正数补码与原码相同

由于char只能存储一个字节的值,所以从低位向高位发生截断

a的补码:10000000

        在printf打印的时候使用的转换说明是%u,这时候需要发生整型提升,是char,则有:

补码:11111111111111111111111110000000

打印的时候被认为是无符号数也就是正数,所以原码与补码相同,则打印结果为4294967168。

第四题

int main()
{   
    unsigned int i;
    for(i = 9; i >= 0; i--)
    {
        printf("%u\n",i);
    }
    return 0;
}
复制代码

打印什么?

答案是死循环。

        最大的坑就是i是无符号整型,并且for的循环条件有=号,当i自减为0时,照样可以进入循环,打印后i--,得到的不是负数,而是一个很大的正数!也就是说i不可能为负数,则永远满足i>=0的条件,一直循环下去。

比如:0-1得

111111111111111111111111

而i是无符号整型,原反补码相同,对应得到的数为4294967295。

第五题

int main()
{
    char a[1000];
    int i;
    for(i=0; i<1000; i++)
   {
        a[i] = -1-i;
   }
    printf("%d",strlen(a));
    return 0;
}
复制代码

        先看看要打印什么,strlen()求的是字符串长度,所关注的是字符串中'\0'(ASCII码为0)前面有多少个字符。

        可能有人就会觉得那不就是1000了嘛,多简单啊。

        错啦!一定要注意数组元素是char类型的,范围只在-128~127之间。

image.png

        也就是说a[i]的取值:-1,-2,......-127,-128,127,126,........1,0,-1,-2......一直循环在-128~127范围内。在第一个0之前有255个数,所以答案就是255。

第六题

#include <stdio.h>
unsigned char i = 0;
int main()
{
    for(i = 0;i<=255;i++)
   {
        printf("hello world\n");
   }
    return 0;
}
复制代码

        归根到底还是无符号整数溢出问题,i始终是在0~255之间循环,不可能大于255,也就一直满足i<=255的条件,也就一直循环下去即死循环。


以上就是本文全部内容,感谢观看,你的支持就是对我最大的鼓励~

u=1537268702,2652616864&fm=253&fmt=auto&app=138&f=JPEG.webp

猜你喜欢

转载自juejin.im/post/7130444808740405261