- 符号位(Sign) : 第31位,0代表正,1代表为负
- 指数位(Exponent):用于存储科学计数法中的指数数据,并且采用移位存储,-127~128
- 尾数部分(Mantissa):尾数部分
SEEE EEEE EMMM MMMM MMMM MMMM MMMM MMMM
float在内存中的存储遵循IEEE 754标准。在C/C++中,float类型占4个字节即32位 , 这32位分成了3部分:
符号位:转化成二进制后,第31位。 0代表正数,1代表负数
阶码:30-23位,转化成规格化的二进制之后与127之和
尾数:22-0位
例如:13.625在内存中的存储
首先将13.625转化成二进制
整数部分除2取余,直到商为0停止 。最后读数时,从最后一个余数读起,一直到最前面的一个余数
所以13的二进制位 1101
小数部分乘2取整,然后从前往后读。
0.625*2 = 1.25 取整 1
0.25*2 = 0.5 取整 0
0.5*2 = 1 取整 1
所以小数部分的二进制 101
然后将 1101.101的小数点向左移至小数点前只有一个1,即左移3位 。
阶码就是3+127 = 130 即:1000 0010
符号位:0
尾数 :因为小数点前必为1,所以记录小数点后面的数即可:101101
0100 0001 0101 1010 0000 0000 0000 0000
转换成16进制后为 41 5A 00 00
#include "stdio.h"
union{
int a;
float b;
char c;
}x;
int main()
{
// union占用的内存等于最长的成员占用的内存,这里为float和int,大小为4字节
printf("sizeof(x)=%d\n", sizeof(x));
x.b = 13.625;
printf("x.a=%d x.b=%f x.c=%c\n",x.a, x.b, x.c);
x.c = '!'; // 33 = 0x21
printf("x.a=%d x.b=%f x.c=%c\n",x.a, x.b, x.c);
x.a = 25;
printf("x.a=%d x.b=%g x.c=%c\n",x.a, x.b, x.c);
return 0;
}
PS E:\gocode\test> cd "e:\gocode\test\" ; if ($?) { g++ union.cpp -o union } ; if ($?) { .\union }
sizeof(x)=4
x.a=1096417280 x.b=13.625000 x.c=
x.a=1096417313 x.b=13.625031 x.c=!
x.a=25 x.b=0.000000 x.c=
x.c = '!'之后,存储的二进制变为0100 0001 0101 1010 0000 0000 0010 0001,如果要浮点数表示,从10000010可以知道小数点左移了3位,即1010 0000 0000 0010 0001表示实际的小数点后面的数,转换成十进制,0.625+1/(2的15次)+1/(2的20次)=0.625031
x.a = 25,b的数值用%g打印了,用%f打印的话x.b=0.000000。
%f 表示按浮点数的格式打印。 小数点后固定6位
%e 表示以指数形式的浮点数格式输出。
%g 表示自动选择合适的表示法输出。
#include <stdio.h>
union data{
int n;
char ch;
short m;
};
int main(){
union data a;
printf("%d, %d\n", sizeof(a), sizeof(union data) );
a.n = 0x40;
printf("%X, %c, %hX\n", a.n, a.ch, a.m);
a.ch = '9';
printf("%X, %c, %hX\n", a.n, a.ch, a.m);
a.m = 0x2059;
printf("%X, %c, %hX\n", a.n, a.ch, a.m);
a.n = 0x3E25AD54;
printf("%X, %c, %hX\n", a.n, a.ch, a.m);
return 0;
}
4, 4 40, @, 40 39, 9, 39 2059, Y, 2059 3E25AD54, T, AD54
上图是在绝大多数 PC 机上的内存分布情况,字节序为小序,如果是 51 单片机,情况就会有所不同:
大端字节序:高位字节数据存放在低地址处,低位数据存放在高地址处;大端模式和字符串的存储模式类似
小段字节序:高位字节数据存放在高地址处,低位数据存放在低地址处;
网络字节序就是大端字节序:4个字节的32bit值以下面的次序传输,首先是0~7bit,其次8~15bit,然后16~23bit,最后是24~31bit。UDP/TCP/IP协议规定:把接收到的第一个字节当作高位字节看待,这就要求发送端发送的第一个字节是高位字节;而在发送端发送数据时,发送的第一个字节是该数值在内存中的起始地址处对应的那个字节,也就是说,该数值在内存中的起始地址处对应的那个字节就是要发送的第一个高位字节(即:高位字节存放在低地址处);网络发送时,是从起始地址开始操作处理变量数据的,因此会先传输变量的低地址数据,后传输高地址数据。
主机字节序 就是 小端字节序,现代PC大多采用小端字节序。
最高内存地址 0xFFFFFFFF
栈区(从高内存地址,往低内存地址发展。即栈底在高地址,栈顶在低地址)
堆区(从低内存地址 ,往高内存地址发展)
全局区(常量和全局变量)
代码区
最低内存地址 0x00000000
void byteorder()
{
union
{
short value;
char union_bytes[sizeof(short)];
}test;
test.value = 0x0102;
if (sizeof(short) == 2)
{
if (test.union_bytes[0] == 1 && test.union_bytes[1] == 2)
cout << "big endian" << endl;
else if (test.union_bytes[0] == 2 && test.union_bytes[1] == 1)
cout << "little endian" << endl;
else
cout << "unknown" << endl;
}
else
{
cout << "sizeof(short) == " << sizeof(short) << endl;
}
return ;
}
#include <stdio.h>
#include <stdlib.h>
#define TOTAL 4 //人员总数
struct{
char name[20];
int num;
char sex;
char profession;
union{
float score;
char course[20];
} sc;
} bodys[TOTAL];
int main(){
int i;
//输入人员信息
for(i=0; i<TOTAL; i++){
printf("Input info: ");
scanf("%s %d %c %c", bodys[i].name, &(bodys[i].num), &(bodys[i].sex), &(bodys[i].profession));
if(bodys[i].profession == 's'){ //如果是学生
scanf("%f", &bodys[i].sc.score);
}else{ //如果是老师
scanf("%s", bodys[i].sc.course);
}
fflush(stdin);
}
//输出人员信息
printf("\nName\t\tNum\tSex\tProfession\tScore / Course\n");
for(i=0; i<TOTAL; i++){
if(bodys[i].profession == 's'){ //如果是学生
printf("%s\t%d\t%c\t%c\t\t%f\n", bodys[i].name, bodys[i].num, bodys[i].sex, bodys[i].profession, bodys[i].sc.score);
}else{ //如果是老师
printf("%s\t%d\t%c\t%c\t\t%s\n", bodys[i].name, bodys[i].num, bodys[i].sex, bodys[i].profession, bodys[i].sc.course);
}
}
return 0;
}
Input info: HanXiaoXiao 501 f s 89.5↙ Input info: YanWeiMin 1011 m t math↙ Input info: LiuZhenTao 109 f t English↙ Input info: ZhaoFeiYan 982 m s 95.0↙ Name Num Sex Profession Score / Course HanXiaoXiao 501 f s 89.500000 YanWeiMin 1011 m t math LiuZhenTao 109 f t English ZhaoFeiYan 982 m s 95.000000
因为输入的名字都是超过8个字符,所以这个打印规整,如果输入的名字小于8个字符,会不规整,因为Name后面跟了两个\t,界面上输入名字超过8个字符,会把前8个字符舍弃,从第9个字符开始算一个\t的长度,一般是8个字符,这样就刚好符合