1.数据类型介绍
char //字符数据类型
short //短整形
int //整形
long //长整形
long long //更长的整形
float //单精度浮点数
double //双精度浮点数
整形:
char
unsigned char
signed char
short
unsigned short [int]
signed short [int]
int
unsigned int
signed int
long
unsigned long [int]
signed long [int]
浮点数:
float
double
构造类型:
数组类型
结构体类型 struct
枚举类型 enum
联合类型 union
指针类型:
int *pi;
char *pc;
float* pf;
void* pv;
空类型:
void 表示空类型(无类型)
通常应用于函数的返回类型、函数的参数、指针类型。
2.整形的存储
(1)原码 反码 补码
有符号位和数值位
符号位是最高位
符号位是0 表示是正数
符号位是1 表示是负数
十进制转化成二进制时得到的是原码
但在计算机中存储计算时用的是补码
正数
原码 反码 补码相同
负数
反码是原码符号位不变 数值位按位取反得到的
按位取反就是0变1 1变0
补码等于反码加1
存储的是补码
但是顺序会出现问题
这是为什么呢?
这涉及到大小端问题
(2)大端和小端
大端(存储)模式,是指数据的低位保存在内存的高地址中,而数据的高位,保存在内存的低地址 中;
小端(存储)模式,是指数据的低位保存在内存的低地址中,而数据的高位,保存在内存的高地址中。
相对位置
高位 低位
低地址 高地址
大端:也就是正着存进去
小端:也就是倒着存进去
练习
写一段代码告诉我们当前机器的字节序是什么
#include<stdio.h>
check_sys()
{
int a=1;
char *p=(char*)&a;//存放地址 解引用只拿一个字节
return *p;
//返回1 小端
//返回0 大端
//指针类型的意义
//1 指针类型决定了指针解引用操作符能访问几个字节
//2 指针类型决定了指针+1 -1 到底加的 减的是几个字节
}
int main()
{
int ret=check_sys();
if(ret==1)
{
printf("小端\n");
}
else
{
printf("大端\n");
}
return 0;
}
//可以再优化函数内部
int a=1;
return *(char*)&a;
大端模式
0x11223344 11 22 33 44正着存
小端模式
0x11223344 44 33 22 11倒着存
拿1来进行判断
0x00000001
大端 00 00 00 01
小端 01 00 00 00
判断第一个字节不同就可以了
char*进行强制类型转换
char范围:
有符号char范围-128到127 补码反推原码
无符号char范围0到255 补码就是原码
上面是无符号数的范围
需要说明的是符号位不会借位
也就是说补码10000000对应的就是-128
-128
原码1 10000000
反码1 01111111
补码1 10000000
因为char是1个字节 只有8个bit位
所以-128的补码就为10000000
补码的相加规律
127+1=-128
再加1就是-127 一直到-1 再加1就是0 完美闭环
练习
1输出什么?
#include <stdio.h>
int main()
{
char a= -1;
signed char b=-1;
unsigned char c=-1;
//c:11111111 无符号位补0
//补码:0000000000000011111111
//正数 原反补相同
//打印原码 255
printf("a=%d,b=%d,c=%d",a,b,c);
return 0;
}
a=-1 b=-1 c=255
a:
-1原码:10000000 00000000 00000000 00000001
-1反码:11111111 11111111 11111111 11111110
-1的补码是全1
char 1个字节 8个bit位 截断
a:11111111
整型提升
补符号位 1
补码:11111111111111111111111111111111111
存的是补码 打印原码 还是-1b:
b:11111111
同ac:
c:11111111 无符号位补0
补码:0000000000000011111111
正数 原反补相同
打印原码 255注意整型提升
2
#include <stdio.h>
int main()
{
char a = -128;
printf("%u\n",a);
return 0;
}
-128原码:1000000000010000000
-128反码:0111111111101111111
-128补码:0111111111110000000
char截断:10000000
%u打印10进制无符号位
char整型提升 补符号位 补1
整型提升后,还是补码:111111111111111111111110000000 前面25位是1 后7位是0
因为打印无符号数
原码就是补码
所以打印一个很大的数
3
#include <stdio.h>
int main()
{
char a = 128;
printf("%u\n",a);
return 0;
}
原00000001000000
补00000001000000
10000000截断
0000000001000000提升 补
0000000001000000原
128存不下
看成127+1 补码加1 最后是-128
所以和第2题结果一样
4
int i= -20;
unsigned int j = 10;
printf("%d\n", i+j);
输出-10
根据补码相加规律可得
不放心可以计算
-20
原10000000 00000000 00000000 00010100
反111111111 111111111 111111111 11101011
补111111111 111111111 111111111 11101100
10
原00000000 00000000 00000000 00001010
补00000000 00000000 00000000 00001010
补码相加
补11111111 11111111 11111111 11110110
反11111111 11111111 11111111 11110101
原10000000 00000000 00000000 00001010
原码为最后结果-10
5
unsigned int i;
for(i = 9; i >= 0; i--)
{
printf("%u\n",i);
}
无符号数 永远是正数
所以陷入死循环
先输出9 8 7 6 5 4 3 2 1 0
负数开始 符号位会被当成有效位 所以原码很大
所以是一个很大的数 一直循环
6
#include<stdio.h>
int main()
{
char a[1000];
int i;
for(i=0; i<1000; i++)
{
a[i] = -1-i;//-1到-1000
}
printf("%d",strlen(a));
return 0;
}
255
char有符号范围-128-127
所以到-129时会看成-128-1
得到127
依次类推最后可以得到0
前面128个负数加上127到1的127个数
共255个数
0是第256个数 0不算strlen中的长度
所以最后输出255
7
#include <stdio.h>
unsigned char i = 0;
int main()
{
for(i = 0;i<=255;i++)
{
printf("hello world\n");
}
return 0;
}
char无符号数范围0-255
所以陷入死循环
256看成255+1
255+1=0(补码相加)回到0
总结:
1先写出数的原码反码补码 注意正数原反补相同 负数要根据规则来计算 最后是补码的计算
2小心char只占1个字节 补码需要截断
3char截断补码后 整型提升 补充符号位 负数补1 正数补0
4补完之后进行补码的相加减
5相加减完后返回成原码 小心正数负数计算方式不同
6最后输出结果为原码
有的题有的步骤没有
注意char无符号数和有符号的范围 小心题目出现死循环
小心题目中带有无符号数和有符号数的条件 整型提升不要补错数字了
补码的计算规则需要知道
char无符号 127+1=-128
char有符号 255+1=0
加减后可以知道补码对应原码后输出的数
归根结底是补码的计算 中间有很多细节步骤要到位 还有最后的还原
3.浮点型在内存中的存储
(1)浮点数
3.14159
1E10
浮点数家族包括: float、double、long double 类型。
浮点数表示的范围:float.h中定义
(2)存储规则
与整型存储规则不同
任何一个二进制浮点数可以写成以下形式
(-1)^S * M * 2^E
(-1)^s表示符号位,当s=0,V为正数;当s=1,V为负数。
M表示有效数字,大于等于1,小于2。
2^E表示指数位(E的大小类似于科学计数法10的指数)
比如说
0.5
二进制形式0.1
(-1)^0*1*2^(-1)
S=0 M=1.0 E=-1
5.5
二进制形式101.1
(-1)^0*1.011*2^2
S=-1 M=1.011 E=2
(3)存储方式
5.5写成标准形式的数据已经得到(float型)(32位)
第一位是符号位
后面八位是E+127后的二进制形式
后面的位再是M小数点后的数字 正常写
没有到32位的位数全部用0补齐
所以
0 10000001(129) 011(1.011) 00000000000000000000
01000000101100000000000000000000
写成16进制
0100 0000 1011 0000 0000 0000 0000 0000
二进制四位对应一个十六进制位
0x4 0 b 0 0 0 0
结果为0x40b0000
如果是double型(共64位)
第一位是符号位不变
后面23位变成E+1023的二进制形式
后面的位再是M小数点后的数字 正常写
没有到64位的位数全部用0补齐
特殊情况(反过来推)
如果E全为0(整个数就是无穷小)
这时,浮点数的指数E等于1-127(或者1-1023)即为真实值,有效数字M不再加上第一位的1,而是还原为0.xxxxxx的小数。这样做是为了表示±0,以及接近于0的很小的数字。
如果E全为1(整个数就是无穷大)
E全为1 这时,如果有效数字M全为0,表示±无穷大(正负取决于符号位s);
看这段代码
int main()
{
int n = 9;
float *pFloat = (float *)&n;
printf("n的值为:%d\n",n);
printf("*pFloat的值为:%f\n",*pFloat);
*pFloat = 9.0;
printf("num的值为:%d\n",n);
printf("*pFloat的值为:%f\n",*pFloat);
return 0;
}
9:
0 00000000 0000000000000000001001-补码
E全为0 整个数就是无穷小
(-1)^0*0.0000000000000000000001001*2^-126-标准形式 这是补码 因为正数 也是原码
第一个结果输出9的原码就是9
第二个结果就为0.00000000(float保留)
9.0:
1001.0-二进制
1.001*2^3
(-1)^0*1.001*2^3
S=0 M=1.001 E=3
0 10000010 00100000000000000-标准形式 这是补码 因为正数 也是原码第三个结果输出一个很大的数
第四个结果是9.0
总结:
1十进制数转化成二进制数
2写成标准形式 得到S M E
3先确定符号位 符号位后面存E float加个127存 double加个1023存 存的时候写成二进制
4再放M小数位
5最后补0
6分成4个一组 变为十六进制
#9数据的存储#完