目录
Ⅰ、数据类型介绍
1.类型的基本归类:
Ⅱ、整形在内存中的存储
1 .原码、反码、补码
2. 大小端介绍
3 练习:
Ⅲ、浮点型在内存中的存储
1 .浮点数存储规则
1. 数据类型详细介绍2. 整形在内存中的存储:原码、反码、补码3. 大小端字节序介绍及判断4. 浮点型在内存中的存储解析
Ⅰ、数据类型介绍
空间大小(字节)char //字符数据类型 1short //短整型 2int //整形 4long //长整型 4long long //更长的整形 8float //单精度浮点数 4double //双精度浮点数 8
1. 使用这个类型开辟内存空间的大小(大小决定了使用范围)。2. 如何看待内存空间的视角。
1.类型的基本归类:
charunsigned charsigned charshortunsigned short [ int ]signed short [ int ]intunsigned intsigned intlongunsigned long [ int ]signed long [ int ]
floatdouble
> 数组类型> 结构体类型 struct> 枚举类型 enum> 联合类型 union
int * pi ;char * pc ;float* pf ;void* pv ;
void 表示空类型(无类型)通常应用于函数的返回类型、函数的参数、指针类型。
Ⅱ、整形在内存中的存储
int a = 20 ;int b = - 10 ;
1 .原码、反码、补码
原码直接将数值按照正负数的形式翻译成二进制就可以得到原码。反码将原码的符号位不变,其他位依次按位取反就可以得到反码。补码反码 +1 就得到补码。
在计算机系统中,数值一律用补码来表示和存储。原因在于,使用补码,可以将符号位和数值域统一处理;同时,加法和减法也可以统一处理( CPU 只有加法器 )此外,补码与原码相互转换,其运算过程是相同的,不需要额外的硬件电路。
我们看看内存中的储存:
2. 大小端介绍
大端(存储)模式,是指数据的低位保存在内存的高地址中,而数据的高位,保存在内存的低地址中;
小端(存储)模式,是指数据的 低位 保存在内存的 低地址 中,而数据的 高位 , ,保存在内存的 高地址 中。
为什么会有大小端模式之分呢?这是因为在计算机系统中,我们是以字节为单位的,每个地址单元都对应着一个字节,一个字节为8 bit 。但是在 C 语言中除了 8 bit 的 char 之外,还有 16 bit 的 short型,32 bit 的 long 型(要看具体的编译器),另外,对于位数大于 8 位的处理器,例如 16 位或者 32位的处理器,由于寄存器宽度大于一个字节,那么必然存在着一个如何将多个字节安排的问题。因此就导致了大端存储模式和小端存储模式。总的来说: 一个地址单元对应一个字节,那一个数据(int,double...) 就可能涉及到多个地址单元,这时候从哪边开始存数据在不同的专家的观点上就出现了不同的意见,从而出现了大小端。
我们都知道,一个int占4个字节,当我们用int去存储数字1的时候,他只会用其中一个字节去存储他并且数字 1位于32位中的最低位,将数字 1 存入int后,我们将变量的地址取出来强制类型转换为 char* 。由于(char*)类型指针每次解引用只会访问一个字节,他就会访问 int 变量地址的第一个字节,如果我们得到 1 则说明 最低位存到了地位——小端存储,如果得到的是0,则说明高位上的 0 存到了低位——大端存储;
//代码1
#include <stdio.h>
int check_sys()
{
int i = 1;
return (*(char *)&i);
}
int main()
{
int ret = check_sys();
if(ret == 1)
{
printf("小端\n");
}
else
{
printf("大端\n");
}
return 0;
}
//代码2
int check_sys()
{
union
{
int i;
char c;
}un;
un.i = 1;
return un.c;
}
3 练习:
1.
//输出什么?
#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;
}
2.
#include <stdio.h>
int main()
{
char a = -128;
printf("%u\n",a);
return 0;
}
3.
#include <stdio.h>
int main()
{
char a = 128;
printf("%u\n",a);
return 0;
}
4.
int i= -20;
unsigned int j = 10;
printf("%d\n", i+j);
//按照补码的形式进行运算,最后格式化成为有符号整数
5.
unsigned int i;
for(i = 9; i >= 0; i--)
{
printf("%u\n",i);
}
6.
int main()
{
char a[1000];
int i;
for(i=0; i<1000; i++)
{
a[i] = -1-i;
}
printf("%d",strlen(a));
return 0;
}
7.
#include <stdio.h>
unsigned char i = 0;
int main()
{
for(i = 0;i<=255;i++)
{
printf("hello world\n");
}
return 0;
}
详解:
1.
//练习
//输出什么?
#include <stdio.h>
int main()
{
char a = -1;
//10000001 a在内存中的原码
//11111110 a的补码
//11111111 a的补码--机器中存储的
//11111111 11111111 11111111 11111111 整型提升之后的十进制补码
//10000000 00000000 00000000 00000001 取反加一后得到原码--打印出来即a= -1
signed char b = -1;
//在当前编译器环境下,char默认是 有符号char 所以 b和a打印出来是一样的
unsigned char c = -1; //虽然c是无符号的char,但是他机器中存的依旧是-1的补码,
//因为赋值是先将-1存进内存再根据接收类型的需要的需要进行读取
//10000001 a在内存中的原码
//11111110 a的补码
//11111111 a的补码--机器中存储的
//
//打印c的时候是要转为整形,进行整形提升,整形提升是按照符号位提升的,这里C是无符号数
// 记住::::::这里不是看-1 看的是C
//00000000 00000000 00000000 11111111 (无符号) C 进行整形提升后
//%d 是按照十进制有符号数打印,他会将最高位看作为符号位 所以打印出来为——255
printf("a=%d,b=%d,c=%d", a, b, c);
return 0;
}
输出结果
2.
#include <stdio.h>
int main()
{
char a = -128;
//10000000 补码
//11111111 11111111 11111111 10000000 整形提升后的结果
//%u是按照10进制无符号数打印 这里将a看作无符号数打印不需要取反加一
//按照补码的形式进行运算,最后格式化成为有符号整数
printf("%u\n", a);
return 0;
}
3.
#include <stdio.h>
int main()
{
char a = 128;
//此编译器环境下,char默认有符号,128显然超过了有符号char的表示范围,经过编译器处理 a在机器中存的是
//10000000 a的机器码
//有符号的a整形提升
//11111111 11111111 11111111 10000000 整形提升后的结果
//按照无符号数打印 按照补码的形式进行运算,最后格式化成为有符号整数
printf("%u\n", a);
return 0;
}
输出结果跟第二个题一样
int main()
{
int i = -20;
//10000000 00000000 00000000 00010100 原码
//11111111 11111111 11111111 11101100 补码
unsigned int j = 10;
//00000000 00000000 00000000 00001010 原码也是补码
printf("%d\n", i + j);
//11111111 11111111 11111111 11101100
//00000000 00000000 00000000 00001010
//11111111 11111111 11111111 11110110 i与j的补码相加
// 按照有符号十进制数打印,最高位为1 既要取反加一
//10000000 00000000 00000000 00001010 结果 ==== -10
//按照补码的形式进行运算,最后格式化成为有符号整数
return 0;
}
执行结果:
5.
这个题需要了解一些存储规则,一个数的类型是被他的字节限制住的,超过了他能表示的最大的数他就会截断一部分,相当于转圈圈,这里我们以有符号char与无符号char为例:
有符号数char
无符号数char
然后看我们这个题:
int main()
{
unsigned int i;
for (i = 9; i >= 0; i--)
{
printf("%u\n", i);
}
return 0;
}
无符号数int,他最小的数就是0,转圈圈永远不可能会走到负数,所以这个代码会死循环。
通过结果我们还可以看见他也是在慢慢减小,当然这里用的是int不容易看出转圈圈效果,换为char就会看的比较清楚。
输出结果:
6.
这一个题也需要上一个题的铺垫
#include<string.h>
int main()
{
char a[1000];
int i;
for (i = 0; i < 1000; i++)
{
a[i] = -1 - i;
}
printf("%d", strlen(a));
return 0;
}
他从-1开始减,有符号char最多可以减到-128,然后再减到0,然后再见到-128,以次循环。不过这里,strlen统计的是 ‘\0' 之前的字符个数,我们第一次走到0的时候就会停止统计(’\0'的ascll码值是0);我们开始计算,-1 ~ -128是128个字符,127 ~ 1 是127个字符(0不统计!统计的是0之前的字符)这样一相加 127+128=255
输出结果:
这个题也是像上面的一样,转圈圈,死循环
#include <stdio.h>
unsigned char i = 0;
int main()
{
for (i = 0; i <= 255; i++)
{
printf("hello world\n");
}
return 0;
}
执行结果:这里为了方便看,我多打印了一个 i :
Ⅲ、浮点型在内存中的存储
3.141591E10浮点数家族包括: float 、 double 、 long double 类型。浮点数表示的范围: float.h 中定义
1 .浮点数存储规则
(-1)^S * M * 2^E(-1)^S 表示符号位,当 S=0 , V 为正数;当 S=1 , V 为负数。M 表示有效数字,大于等于 1 ,小于 2 。2^E 表示指数位。
对于 32 位的浮点数,最高的 1 位是符号位 s ,接着的 8 位是指数 E ,剩下的 23 位为有效数字 M 。
最高的 1 位是符号位S,接着的 11 位是指数 E ,剩下的 52 位为有效数字 M 。
0 01111110 00000000000000000000000
这里给一个浮点数存储例子:
int main()
{
float a = 5.5;
//101.1
//(-1)^0 * 1.011 * 2^2
// S=0 E=2+127=129=10000001 M =01100000000000000000000
// 01000000 10110000 0000000000000000
// 40 b0 00 00
//
return 0;
}
下面有一个题:
int main()
{
int n = 9;
//00000000 00000000 00000000 00001001 整数视角存整数 9
float* pFloat = (float*)&n;
//0 00000000 00000000000000000001001 用浮点数时间看待9
//S=0
//E=0-127=-127
//M= 00000000000000000001001
//*pFloat= (-1)^0 * 00000000000000000001001 * 2^(-127)
//这个数是远小于0的 而flost只能打印小数点后六位,所以打印结果为 0.000000
printf("n的值为:%d\n", n);//9
printf("*pFloat的值为:%f\n", *pFloat);//
*pFloat = 9.0;
//9.0
//(-1)^0 * 1.001 * 2^3 S=0 E=3+127=130= 10000010 M= 00100000000000000000000
//01000001 00010000 00000000 00000000 浮点数视角存储 9.0 ——机器码
// 41 10 00 00
printf("num的值为:%d\n", n);//01000001 00010000 00000000 00000000按照10进制打印为1091567616
printf("*pFloat的值为:%f\n", *pFloat);//9.0
return 0;
}