携手创作,共同成长!这是我参与「掘金日新计划 · 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类型。
然而我们确实没看到转换的过程,能看到结果,但有时结果或许会和我们想的不太一样。
一些例题
先看张图理解一下整型的存入与读取
第一题
//输出什么?
#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之间。
也就是说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的条件,也就一直循环下去即死循环。
以上就是本文全部内容,感谢观看,你的支持就是对我最大的鼓励~