union理解及float变量在内存中的存储方式 大小端字节序

本文介绍了浮点数在内存中的存储格式,遵循IEEE754标准,包括符号位、指数位和尾数部分。通过实例展示了13.625在内存中的存储过程,并通过C语言的共用体理解不同类型数据在内存中的相互转换。同时,讲解了字节序的概念,包括大端和小端模式,并提供了检测字节序的代码。此外,还展示了如何使用共用体在结构体中存储不同数据类型的例子,用于输入和输出人员信息。
摘要由CSDN通过智能技术生成

  1. 符号位(Sign) : 第31位,0代表正,1代表为负
  2. 指数位(Exponent):用于存储科学计数法中的指数数据,并且采用移位存储,-127~128
  3. 尾数部分(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 表示自动选择合适的表示法输出。

转自:C语言共用体(C语言union用法)详解

#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个字符,这样就刚好符合

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值