目录
一:简介库函数:
二、类型的意义:
三、unsigned char 与signed char 范围
四、构造类型:
五、 问题:为什么用补码表示比原码表示好呢?
六、大端和小端
七、浮点型在内存中的存储
八、浮点数的取出:
库函数是独立于C语言之外的,是编译器的厂商提供给的
C语言标准也规定了一些库函数:
函数名,参数类型,返回值类型,函数功能等
size_t strlen(const char* str);
//求字符串长度
//但是每一家厂商在库函数中都可能会有一些不同
//比如vs : scanf - scanf_s
strcpy - strcpy_s
我们生活中无非就是整数和小数,所以划分为整型家族和浮点型家族
//整型家族:
char //字符数据类型
short //短整型
int //整型
long //长整型
long long //更长的整型
//浮点型家族
float //单精度浮点型
double //双精度浮点型
1.使用这个类型开辟的空间大小不同
2.看待内存空间的视角不同
比如:
int 开辟4个字节的空间,float 也是开辟4个字节的空间,但是从内存空间的视角来看,
变量a里面存放的是整数,变量f里面存放的是小数
总结:
有符号位:-(2^n-1) - (2^n-1)-1
无符号位:0 - (2^n)-1
1.数组类型 2.结构体类型struct 3.枚举类型 enum 4. 联合体类型 union
//数组的类型: int main() { int a = 10; int arr[10] = {0}; printf("%d\n",sizeof(a));//4 printf("%d\n", sizeof(int));//4 ,a的类型 printf("%d\n", sizeof(arr));//40 printf("%d\n", sizeof(int [10]));//40,所以数组arr的类型是int [10] return 0; }
指针类型:
//指针类型 int* p1; char* p2; float* p3; void* p4; //void 表示空类型(无类型) //通常应用于函数的返回类型,函数的参数,指针类型 void test(void)//这里void 表明不需要参数 { printf("hehe\n"); } int main() { test(); }
对于整形来说:数据存放内存中其实存放的是补码
1.补码可以将符号位和数值域统一处理
2.cpu只有加法器,加法和减法可以统一处理
3.补码与原码相互转换,运算过程是相同的,不需要额外的硬件电路
解释:
一个数据以字节为单位存储顺序,对于大于一个字节的数据,,那么必然存在着如何将多个字节安排的问题,所以导致了大端和小端存储模式。
vs2019 运用的是小端存储
案例1:
a,b 解释: 对于有符号位,先将数据写成二进制原码,通过原码转换成补码,因为a的类型是char类型,只访问8个Bit位,所以进行截断,又因为最后以%d打印,所以按符号位进行整形提升,但是因为还是在计算机内存中的,所以还是补码,因为打印需要的是原码,所以转换成原码,最后打印-1
c解释: 对于无符号位,先将数据写成二进制原码,通过原码转换成补码,因为c的类型是char类型,只访问8个Bit位,所以进行截断,又因为最后以%d打印,所以按符号位进行整形提升,因为为无符号位,所以符号位为正数,所以补0,补齐32位,因为32位都是有效位,并且为正数,所以原反补相同,所以最后打印为255
//案例2: int main() { char a = -128; printf("%u\n",a); return 0; //10000000 00000000 00000000 10000000 -128原码 //11111111 11111111 11111111 01111111 补码 //11111111 11111111 11111111 10000000反码 //10000000 char型 //11111111 11111111 11111111 10000000 //因为%u是以无符号数形式打印整形,所以整形提升,并且整形提升之后就为无符号数,所以原反补相同 //4294967168 }
//案例3: int main() { int a = 128; printf("%u\n",a); //00000000 00000000 00000000 10000000 //10000000 //char型 //11111111 11111111 11111111 10000000//整形提升 //4,294,967,168 }
//案例4: int main() { int i = -20; unsigned int j = 10; printf("%d\n",i + j); //10000000 00000000 00000000 00010100 //11111111 11111111 11111111 11101011 //11111111 11111111 11111111 11101100//-20 补码 //00000000 00000000 00000000 00001010//10的原反补 //11111111 11111111 11111111 11110110 补码 //10000000 00000000 00000000 00001001 //10000000 00000000 00000000 00001010 原码 //-10 %d 有符号位整形打印 return 0; }
//案例5: int main() { char a[1000]; int i; for (i = 0;i< 1000;i++) { a[i] = -1 - i; } printf("%d",strlen(a));//255 return 0; }
char : -128 --- 127,strlen()直到找到'\0'停止
图解:
整形家族定义的头文件为#include
浮点数家族定义的头文件为#include
根据国际标准IEEE(电气和电子工程协会)754,任意一个二进制浮点数可以表示成下面的形式:
1. (-1)^S* M* 2个E
2. (-1)^s表示符号位,当s=0,V为正数; 当s=1,V为负数3. M表示有效数字,大于等于1 ,小于2。
4. 2^E表示指数位。
举例:
9.0 -> 二进制可写为 1001.0 -> 科学技术法表示为 1.001 * 2 ^ 3
则s = 0, M = 1.001, E = 3
只要能用IEEE754标准写出来的二进制浮点数,只要存储S,M,E即可
float:
double:
IEEE 754对于M的规定:
1. 1≤M<2 , M 可以写成 1.xxxxxx 的形式,其中 xxxxxx 表示小数部分。2 . IEEE 754 规定,在计算机内部保存 M 时,默认这个数的第一位总是 1 ,因此可以被舍去,只保存后面的 xxxxxx 部分。比如保存 1.01 的时 候,只保存 01 ,等到读取的时候,再把第一位的 1 加上去。这样做的目的,是节省 1 位有效数字。以 32 位 浮点数为例,留给 M 只有 23 位, 将第一位的 1 舍去以后,等于可以保存 24 位有效数字。
IEEE 754对于E的规定:
首先, E 为一个无符号整数( unsigned int )这意味着,如果 E 为 8 位,它的取值范围为 0~255 ;如果 E 为 11 位,它的取值范围为 0~2047 。但是,我们知道,科学计数法中的 E 是可以出 现负数的,所以 IEEE 754 规定,存入内存时 E 的真实值必须再加上一个中间数,对于 8 位的 E ,这个中间数 是 127 ;对于 11 位的 E ,这个中间 数是 1023 。比如, 2^10 的 E 是 10 ,所以保存成 32 位浮点数时,必须保存成 10+127=137 ,即 10001001 。
//浮点数的存储
//int main()
//{
// float f = 5.5f;
// //101.1
// //(-1)^0 * 1.011 * 2^2
// //s = 0;
// //M = 1.011
// //E = 2 + 127 存储
// // s:0 E:10000001 M:01100000000000000000000
// //0100 0000 10110000 00000000 00000000
// //内存中存储为16进制,4个Bit一个十六进制
// //40 B0 00 00 内存中:0x009BFB2C 00 00 b0 40 小端存储
//}
分为 3 种情况:
E 不全为 0 或不全为 1这时,浮点数就采用下面的规则表示,即指数 E 的计算值减去 127 (或 1023 ),得到真实值,再将 有效数字 M 前加上第一位的 1 。比如:0.5 ( 1/2 )的二进制形式为 0.1 ,由于规定正数部分必须为 1 ,即将小数点右移 1 位,则为1.0*2^(-1) ,其阶码为 -1+127=126 ,表示为 01111110 ,而尾数 1.0 去掉整数部分为 0 ,补齐 0 到 23 位 00000000000000000000000 ,则其二进 制表示形式为: 0 01111110 00000000000000000000000
E 全为 0这时,浮点数的指数 E等于1-127(或者1-1023) 即为真实值,有效数字 M 不再加上第一位的 1 ,而是 还原为0.xxxxxx的小数 。这样做是为了表示 ±0 ,以及接近于 0 的很小的数字。比如:+ - 0.001 * 2 ^ (-126)
E 全为 1这时,如果有效数字 M全为0 ,表示 ± 无穷大(正负取决于符号位 s );比如:+ - 1. 00 * 2 ^ 128
//案例:
int main()
{
int n = 9;
float* pFloat = (float*)&n;
//9 存入计算机内存中为补码:00000000 00000000 00000000 00001001
//%d打印为有符号整形,9 原反补相同,所以打印为9
printf("n的值为: %d\n",n);//%d 打印为9
printf("*pFloat的值为:%f\n",*pFloat);
//%f 打印为浮点型,浮点型存储 00000000 00000000 00000000 00001001时,
//认为S:0 E:00000000 M:00000000000000000001001
// 取出:
//当E为全0时:直接就是 1-127 = -126;
//M: 0.00000000000000000001001
//(-1)^0 * 0.00000000000000000001001 * 2 ^ -126
//因为%f默认打印小数点后6位,所以打印0.000000
*pFloat = 9.0;
//1001.0
//(-1) ^ 0 * 1.001 * 2 ^ 3
// S : 0
// E : 3 + 127 = 130
// M : 1.001
// 放入内存时:
// 以浮点数存储:0 10000010 00100000000000000000000
//%d 认为内存中存储的 0 10000010 00100000000000000000000为二进制补码
//所以打印为 1091567616
printf("n的值为: %d\n", n);//1091567616
printf("*pFloat的值为:%f\n", *pFloat);
//以浮点数打印则打印的还是浮点数9.0
return 0;
}
此篇为本人数据存储章节的课堂笔记整理,如果有误,请在下面留言,感谢支持!