类型的归总

21 篇文章 2 订阅
8 篇文章 0 订阅

类型

sizeof 计算变量或数据类型所占的字节个数
在32 位的系统上
short 占据的内存大小是2 个byte;
int 占据的内存大小是4 个byte;
long 占据的内存大小是4 个byte;
float 占据的内存大小是4 个byte;
double 占据的内存大小是8 个byte;
char 占据的内存大小是1 个byte。
bool 占据的内存大小是1 个byte。

void 不能定义变量,但可以定义指针

常见的ASICII值

'a'		// 97
'A'		// 65
'\0'	// 0
'0'		// 48

1 对于补码的类型(计算机存储数据的数据都是补码)

1 为什么需要补码?

因为计算机开始仅有加法器(设计到门电路设计,受限于CPU设计压力 ),取反,和移位操作,而没有乘法器,而为了仅用加法就能完成四则运算,就需要使得-法变成加法,即A-B 变为 A + (-B),要完成该能力,就引入了补码(负数的补码是起原码除符号位取反+1),将减法可以利用加法完成,而有了加法和减法,计算机就可以使用乘法和除法!
而根据补码又可以进行位运算。(移位,与,或,非)

5+-50000 0101
1111 1011
——————————
0000 0000  0  //实现了减法变加法,加法无敌

所以计算机存储数据采用了补码方式

	char b = 200;// 1 1100 1000  //当进行运算时扩充  以补码形式存放,不要惊讶
2 类型对补码的影响 *

不同类型的数据所占位数不同而类型在定义时就确定了,即补码位数也确定了
char 8位
short 16位
int 32位

3 补码根据类型又分为有符号和无符号

有符号和无符号类型又影响了第一位补码位的意义。(当然浮点数没有有符号和无符号之分,浮点数利用区别于整形的存储方式和寄存器,所以浮点数天然就有符号)
有符号,第一位补码是符号位。如char (-128,-1) ,(0,127) 127+1=-128
无符号,第一位补码还是数据位,即无符号比有符号能多一个bit位,即比有符号能多储存2倍。 unsigned char (0,255),即2^n-1 即(2*Tmin-1)

char a= -5; //对,计算机存放是由补码存放,其补码为11111011。所以不要觉得奇怪。
//打印结果会是神马呢?
int main()
{
char i=0;
for(i=0;i<128,++i)//成为了死循环,不会退出,哇哦
{
	cout<<i;
}
return 0;
}

由于char类型有符号整数0-127,127+1不等于128,而是等于-128 !!!
ps:魔鬼数字Tmin -Tmin=Tmin 就像上面-(-128)=-128 当我们输入128时,其也是-128

	for (int i = 7; i - sizeof(char)>=0; i--)//sizeof 返回size_t 无符号,所以需要注意判断条件运算时是否会变为无符号类型,使得成为死循环

ps:对于有符号和无符号之间的运算,将运算后的表达式转换为了无符号类型,另外操作数先进行扩展再转变为无符号类型

2 类型对输出的影响(根据不同类型输出不同的信息

char a=0;
char b='0'//48
char c='\0';//0

printf("%c",b);// 输出字符0
printf("%d",b);//输出值48

即根据不同类型输出不同的信息,程序员成为了操作者

在这里插入图片描述
而unsigned char 0-256 ,其8位都算是数据位

3 根据类型选择不同位数的寄存器(扩充和截取)

//这里只是a,b
%rax //64位
%eax //32位
%ax //16位 又分为ah和al高8位和低8位
%al //8位

ps:

  1. 寄存器扩充又跟变量符号类型有关系(扩充后的数需要与原来的数保持一致)
  2. 如下,还是扩充时与其变量本身有关系,才会进行unsigned,还是为了保证扩充后的数需要与原来的数保持一致
us = (unsigned short)c + uc;//0  虽然这里被转换为了(unsigned short),但也要先扩充
//先扩充为short类型 c->1111 1111 1000 0000 此时它才是unsigned short
//因为寄存器在扩充时,查看的是c是否为有符号。而扩充后才进行unsigned
//若在扩充为int,其为0000 0000 0000 0000 1111 1111 1000 0000
1 扩充

无符号扩充,扩充数据都为0
有符号扩充,则需要区分正负(即符号位,因为扩充后的数需要与原来的数保持一致),即扩充后根据符号位来进行填充寄存器的高位值。
ps:
1. 扩充放进寄存器的值只是一个拷贝值,其变量本身是不变的,这也是C语言是强类型语言的体现
2. 扩展符号位,保证扩充后的数需要与原来的数保持一致
在这里插入图片描述
在这里插入图片描述

2 截取

在这里插入图片描述
小端存放,高地址存高位数,低地址存低位数,而指针指向的首地址是低地址,所以寄存器截取解取到的是低位

一些有趣的题
	int a; //0x0000 0000 - 0x7fff ffff  16进制的好处
	char c = 128;
	unsigned char uc = 128;
	unsigned short us = 0;
	us = c + uc;//0  //c和uc被ebx和ecx拷贝,进行加操作 然后赋值给us eax  ,所以进行了扩展,而有符号根据符号位扩展 
	printf( "x\n",us);//char 运算时会转换为int,运算后结果在赋值给char或short提高了精度
	//对于有符号和无符号之间的运算,是将运算后的表达式转换为了无符号类型,而不是将操作数先变为无符号类型
	us = (unsigned char)c + uc;//100  
	printf("%x\n", us);
	
	us = c + (char)uc;//ff00 
	printf("%x\n", us);//uc -> 1111 1111 10000 (由于转换为了有符号)

	us = (unsigned short)c + uc;//0  虽然这里被转换为了(unsigned short),但也要先扩充
//先扩充为short类型 c->1111 1111 1000 0000 此时它才是unsigned short
//因为寄存器在扩充时,查看的是c是否为有符号。而扩充后才进行unsigned
//若在扩充为int,其为0000 0000 0000 0000 1111 1111 1000 0000

## 4.运算时进行类型转换
### 算术类型转换和赋值类型转换
#### 1. **所谓类型相容,指的就是类型不同但系统可以自动进行转换。**
a.只针对基本数据类型,char,short,int ,long int, long long ,unsigned char,unsigned short,unsigned long int; unsigned long long , float; double类型。
b.**指针,数组,结构体类型,联合体类型,枚举类型,不具此性质,不能直接进行隐式转换**。

```c
int *q=NULL;
char *p=q;//错误的,需要进行强转
	char a = 100;// 0 0110 0100
	char b = 200;// 1 1100 1000  //当进行运算时扩充  以补码形式存放,不要惊讶
	char c = a+b;// 0 0010 1100                44 截断

	printf("%d %d\n", c,a+b);//44 , 44  a+b直接输出eax里的值
	
	
	unsigned char x = 100;//    0 0110 0100
	unsigned char y = 200;//    0 1100 1000
	unsigned char z = x + y;//  1 0010 1100    44 截断

	printf("%d %d\n", z, x + y);//44 256+44=300
2.不同类型数据的混合运算(具有隐式类型转换和显示类型转换)又分为运算过程和赋值过程

在计算机内,进行的数据运算处理都是由二进制补码来进行的。而不同的类型所占的位数不同,即有了相应的截断和扩展。 进行+法会两个数据不会进行转换吗,别脑子空空咯,为什么呢?加法为什么不进行转换呢,两个操作数在进行运算时,寄存器根据不同类型的数据进行选择不同位数的寄存器,所以是需要进行转换的,而你都是8位则不需要进行转换,(不管你的符号位,我只知道这个数字的补码),且传进寄存器的值和本身的值之间的关系只是一种拷贝,只是根据类型选择了不同位数的寄存器来存放该拷贝数值,但对于扩容,其寄存器前面的数值会变成符号位,因为需要保证该数值在全新的位表示里值是不变的。所以你说两个数相加它会进行类型转换吗?当然会,运算都会,因为你要尽可能的保存你计算的值不会丢失。而你又要将计算的临时量,此时该临时量存放在寄存器里,要赋给一个变量,又会根据该变量来进行扩展和截断(相当于需要寄存器还需要传递给另一个合适的寄存器来保存这个值)
ps:寄存器还是要看类型是否为有符号和无符号进行扩充

返回值类型即函数返回时赋值,作为右值时,需要运算符两边类型相同,返回右值类型为const*时,所以需要左值也是const * ,而返回过程eax(针对整形的寄存器)保存的只是其值,不关心其类型(char int long),其类型,且该值成为了右值 eax 就是这个临时量 a=fun () mov a的内存,eax
如char int 比较 先写入寄存器,而写入寄存器之前,其类型就被转换了,即这里发生了隐式转换。而这种转换是在编译阶段,编译器器就会检测出来,然后进行改写,相当于添加了强制转换了,然后在运行时,才进行写入寄存器,使得能正常的比较。所以返回值类型是在编译时就会确定下来的,即eax只管传值,而编译器早就每个地方都写入了类型。

ps:

  1. 编译器对于"求一个表达式的值,但不使用该值"的语句只是将该值简单的丢掉,(可以用到判断语句里,做值)
  2. 对于有符号和无符号之间的运算,是将运算后的表达式转换为了无符号类型,是将操作数先扩展再变为无符号类型(保证其值)
3.运算时进来类型转换

运行时,运算符两边的类型是一致的(无论是默认转还是强转)。
在这里插入图片描述
编译器在处理需要默认类型转换时,将<-的类型自动转为它对应左边的类型进行计算(),如
float 转为double 进行运算,而上箭头不会自动转,而是当运算符左右两边类型还不相同时才会进行转换
横向转换(无条件转换),如两个float运算时自动转为double,提高运算的精度
纵向转换,运算(比大小)时小的转为大的(往上),赋值也是扩展或截断
其他转换没有优先级,有多种转换方式

	char a = -1;
	unsigned char b = a;
	if (a == b)//char 进行运算都会先转换为int ,选用eax等寄存器进行处理,然后当比较时,其类型为unsigned,则需要转换  即255 ,运算结束后转换后unsigned char
	{//这也是先扩展的,才进行unsign的
		printf( "%d=%d\n",a,b);//但a的类型没变 还是char 所以打印 -5,255
	}
	else
		printf("%d!=%d\n", a, b);
	int a; //0x0000 0000 - 0x7fff ffff  16进制的好处
	char c = 128;
	unsigned char uc = 128;
	unsigned short us = 0;
	us = c + uc;//0  //c和uc被ebx和ecx拷贝,进行加操作 然后赋值给us eax  ,所以进行了扩展,而有符号根据符号位扩展 
	printf( "x\n",us);//char 运算时会转换为int,运算后结果在赋值给char或short提高了精度
	//对于有符号和无符号之间的运算,是将运算后的表达式转换为了无符号类型,而不是将操作数先变为无符号类型
	us = (unsigned char)c + uc;//100  
	printf("%x\n", us);
	
	us = c + (char)uc;//ff00 
	printf("%x\n", us);//uc -> 1111 1111 10000 (由于转换为了有符号)

	us = (unsigned short)c + uc;//0  虽然这里被转换为了(unsigned short),但也要先扩充
//先扩充为short类型 c->1111 1111 1000 0000 此时它才是unsigned short
//因为寄存器在扩充时,查看的是c是否为有符号。而扩充后才进行unsigned
//若在扩充为int,其为0000 0000 0000 0000 1111 1111 1000 0000
	printf("%x\n", us);

5. 指针的类型影响(指针指向相当于指向了一个黑箱,而其根据类型具有相应的解析能力)

指针根据类型的不同,指针的+1能力不同,且指针对内存的解析能力不同。指针可以强转是因为改变了指针的类型只是改变了指针的解析能力,它不管指向的是谁。它只负责解析。而不同结构体变量之间不能强转,因为变量的类型在定义后就确定下来了,且该类型。。。。

指针通过申请堆内存后可以类似数组一样用下标来进行内存的访问,因为指针与数组名都是对连续空间的首地址,而其内存又是连续的,当然可以相互转换。

变量的类型在定义后就确定下来了,进行运算什么的都只是该值的拷贝,强转改变的也不是其本身,而是一个拷贝。

6 浮点数类型(浮点类型与整形类型在内存中存储是不同的)

	int x = 0x41440000;
	float *fp = (float*)&x;//相同地址,但解析的值却不同
	printf("&x=%08x,x=%d		\n", &x, x);
	printf("&fp=%08x,*fp=%f		\n", fp, *fp);

	*fp = 10.25;
	printf("&x=%08x,x=%d		\n", &x, x);
	printf("&fp=%08x,*fp=%f		\n", fp, *fp);

在这里插入图片描述

	int x = 10.25;
	float f = 10.25;
	printf("%d %d\n", x, f);// 10  0
	printf("%d %d\n", x, (int)f)//10 10

在这里插入图片描述
ps:格式控制符没有隐式转换 ,那么格式控制符的根本意义是什么呢?

整形储存方式(小端存放):

在这里插入图片描述
ps:当指针指向其整形类型时,根据整形类型解析其存储方式,符号位,数值

浮点数储存方式:
(-1)^S*M*2^E
1. ^s表示符号位,当s=0,×为正数;当s=1,×为负数
2. M表示有效数字,大于等于1,小于2。
3. 2^E表示指数位。小数的转换为*2取个位值,值是0,取0,值是1,取1,直到乘位О位置。

在这里插入图片描述
ps:浮点数只能近似表示,而无法精确表示,其末尾小数为5
在这里插入图片描述
**ps:IEEE754标准做了这样的规定:当尾数(小数)不为0时,尾数域的最高有效位为1,这称为浮点数的规格化.
**例如:(165.25)10----》(10100101.01)2—》(1.010010101*2^7);
规格化后的二进制小数,有了统一的规格,可以发现这样规格化之后,我们只需要存储一个尾数(即小数部分,整数部分恒为1)和指数部分。
在这里插入图片描述
在这里插入图片描述

ps:当指针指向其浮点型类型时,根据浮点型类型解析其存储方式,符号位,指数位,有效数字

对浮点数进行运算时需要有调整因子(FEPS 1.0E-6 DEPS 1.0E-15)

int main()
{

	float ft = 0.0;
	int n = 100;
	for (int i = 0; i < n; i++)
	{
		ft = ft + 0.01;
	}
	ft =ft+ FEPS;
	printf("%f\n", ft);
	//cout << ft << endl;
	for (int i = 0; i < n; i++)
	{
		ft = ft - 0.01;
	}
	ft -= FEPS;

	printf("%f\n", ft);
	//cout << ft << endl;


	return 0;
}

6 类型转换的本质

为什么取地址得到的类型是int *类型,而不是int类型?当数据传输时,你获取这个数据(二进制流),这个数据如何识别,根据类型!!!

在这里插入图片描述

格式控制符的根本意义

	int x = 10.25;
	float f = 10.25;
	printf("%d %d\n", x, f);// 10  0

在这里插入图片描述

当进行输出时,根据printf根据格式控制符进行输出,首先进行4字节读取10,然后再4字节(%d)对其读取,而此时f的传入函数的拷贝被转换为了double类型,有8字节,此时解析其低位,而其低位被填充为0处理了(小数后末尾为5),所以打印为0

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值