当前位置 : 主页 > 编程语言 > c语言 >

C语言深入探索数据类型的存储

来源:互联网 收集:自由互联 发布时间:2023-02-01
目录 数据类型介绍 类型的基本归纳 整型家族 浮点数家族 构造类型 指针类型 空类型 整型在内存中的存储 原码,反码,补码 大小端 浮点数在内存中的存储 浮点数存储的规则 数据类型
目录
  • 数据类型介绍
    • 类型的基本归纳
      • 整型家族
      • 浮点数家族
      • 构造类型
      • 指针类型
      • 空类型
  • 整型在内存中的存储
    • 原码,反码,补码
      • 大小端
      • 浮点数在内存中的存储
        • 浮点数存储的规则

        数据类型介绍

        首先,对于我们C语言中的数据类型,大家应该都有一个清晰的认识吧!如果不记得也没有关系哦~ 在这里来跟着小刘同学回顾一下吧!

        关于数据类型,我们在前面已经学习过了一些内置数据类型,以及它们所占的内存空间的大小,例如:

        char         //字符数据类型
        int          //整型
        short        //短整型
        long         //长整型
        long long    //更长的整型
        float        //单精度浮点型
        double       //双精度浮点型

        那,在这里,我们需要思考一个问题:在C语言中存在字符串类型嘛?

        这个问题的答案需要大家自己去思考哟~(当然,如果不知道的话可以私信小刘同学哟~

        类型的意义:1.使用这个类型开辟的内存空间的大小,也就是说类型决定了我们可以使用的空间的大小。2.如何看待内存空间的视角。

        类型的基本归纳

        首先,当然是我们经常使用的整型类型家族啦!

        整型家族

        char //字符数据类型
            unsigned char //无符号字符型
            signed char //有符号字符型
        short //短整型
             unsigned short[int] //无符号短整型
             signed short[int] //有符号短整型
        int //整型
           unsigned int //无符号整型
           signed int //有符号整型
        long //长整型
            unsigned long[int] //无符号长整型
            signed long[int] //有符号长整型

        当然,整型家族当然是少不了我们的long long int 型的,但是由于更长长整型的分类与整型家族其他成员的分类是相似的,所以在这里我们不加赘述。

        浮点数家族

        float
        double

        构造类型

        >数组类型
        >结构体类型 struct
        >枚举类型 enum
        >联合类型 union

        指针类型

        int * pi;
        char * pc;
        float * pf;
        void * pv; 

        空类型

        void表示空类型(无类型)

        空类型通常应用于函数的返回类型,函数的参数,指针类型。

        整型在内存中的存储

        首先,我们在之前的文章中谈到过,变量的创建是要在内存中开辟一部分空间的。而开辟的空间的大小是要根据不同的类型而决定的。那么,下面就跟着小刘同学一起来学习一下数据在它所开辟的内存中究竟是如何进行存储的吧!

        我们先来举一个例子吧:

        int a=10;
        int b=-10;

        我们知道,因为a为int型,所以为a分配了4个字节的空间,那么,在内存又该如何存储呐?

        要想了解这一点,我们先来明白几个概念:

        原码,反码,补码

        在计算机中的整数存在3种2进制的表示方式:原码、反码、补码。

        这三种方法均存在符号位和数值位这两个部分。符号位使用0表示'正',使用1表示'负'。而数值位正数的原码、反码、补码都相同。而负整数的三种表示方法都不相同。

        原码

        直接将数值按照正负数的形式转换成二进制的形式就可以得到原码。

        反码

        将原码的符号位不变,其他位依次求反就可以得到反码。

        补码

        在反码的基础上+1就可以得到补码。

        对于整形来说,数据存放在内存中其实是以补码的形式存放的。(相信学过数电的小伙伴一定知道这是为什么。那么其他不知道所以然的小伙伴请自行百度询问哦。

        在vs里面我们可以看到a和b在内存中的地址,但是,这样看是不是有点不太对?

        按照我们上面所讲的内容,列出a和b在内存中存储的二进制形式再根据二进制转为十六进制的规则,a和b在内存中的存储应该是下图所示的形式,那么为什么我们在VS的调试中会出现不一样的结果呐?

        int a=10;
        //原码:0000 0000 0000 0000 0000 0000 0000 1010
        //反码:0000 0000 0000 0000 0000 0000 0000 1010
        //补码:0000 0000 0000 0000 0000 0000 0000 1010
        // 00 00 00 0a
        int b=-10;
        //原码:1000 0000 0000 0000 0000 0000 0000 1010
        //反码:1111 1111 1111 1111 1111 1111 1111 0101
        //补码:1111 1111 1111 1111 1111 1111 1111 1010 
        //ff ff ff fa

        这个时候,我们来再了解一个概念——大小端。

        大小端

        大小端指的是两种不同的存储方式。那么下面我们来详细了解一下什么是大小端:

        大端(存储)模式:是指数据的低位数保存在内存的高地址中,而数据的高位,保存在内存的低地址中。

        小端(存储)模式:是指数据的低位数保存在内存的低地址中,而数据的高位,保存在内存的高地址中。

        当然,大端存储和小端存储是根据编译器的不同来决定的。

        这样子只看概念大家是否感到十分疑惑呐?下面,小刘同学给大家画图模拟一下大端存储和小端存储的基本情况吧!

        这样子,大家是不是对大端存储和小端存储有了一个基本的概念了解呐?

        那么,大家现在是不是非常好奇自己目前使用的编译器究竟使用了那种存储方式呐?

        下面请跟着小刘同学来判断一下自己所使用的编译器究竟是哪种存储方式吧!

        我们首先定义一个变量:int a=1;

        int a=1;
        //原码:0000 0000 0000 0000 0000 0000 0000 0001
        //反码:0000 0000 0000 0000 0000 0000 0000 0001
        //补码:0000 0000 0000 0000 0000 0000 0000 0001
        //如果是大端存储,返回的第一个字节一定为00 ——> 0
        //如果是小端存储,返回的第一个字节一定为01 ——> 1

        完整代码如下:

        #include<stdio.h>
        int chek_sys()
        {
        	int i = 1;
        	return (*(char*)&i);
        }
        int main()
        {
        	int ret = chek_sys();
        	if (ret == 1)
        	{
        		printf("小端\n");
        	}
        	else 
        	{
        		printf("大端\n");
        	}
        	return 0;
        }

        浮点数在内存中的存储

        浮点数存储的规则

        首先,我们来举一个例子

        #include<stdio.h>
        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;
        }

        以上代码执行的结果为:

        它这个结果好像和我们想象的结果不太一样哎。这是为什么呐?在这里就不得不提一下浮点数在内存中的存储规则了。

        根据国际标准IEEE(电气和电子工程协会) 754,任意一个二进制浮点数V可以表示成下面的形式:

        • (-1)^S * M * 2E
        • (-1)S表示符号位,当S=0,V为正数;当S=1,V为负数。
        • M表示有效数字,大于等于1,小于2。
        • 2^E表示指数位。

        看到这里,大家是不是有很多的疑问和不理解?别急,下面小刘同学来为大家讲解清楚。

        要想理解上面的内容,我们先来举一个例子吧!

        例如:9.5f

        小数点后面的权重是2的-1次方,-2次方这样子依次类推。

        当然,并不是所有的浮点数都可以表示出来的,有的浮点数是无法表示出来的,小数点后面的数无法精确表示,可能会出现精度缺失。

        IEEE 754对有效数字M和指数E,还有一些特别规定。

        前面说过, 1≤M<2 ,也就是说,M可以写成 1.xxxxxx 的形式,其中xxxxxx表示小数部分。 IEEE 754规定,在计算机内部保存M时,默认这个数的第一位总是1,因此可以被舍去,只保存后面的xxxxxx部分。比如保存1.01的时候,只保存01,等到读取的时候,再把第一位的1加上去。这样做的目的,是节省1位有效数字。以32位浮点数为例,留给M只有23位,将第一位的1舍去以后,等于可以保存24位有效数字。

        至于指数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。

        在我们已经知道如何存储浮点数后,我们再来学习一下如何将浮点数从内存中取出。

        指数E从内存中取出还可以再分成三种情况:

        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的很小的数字。

        E全为1

        这时,如果有效数字M全为0,表示±无穷大(正负取决于符号位s)。

        以上及是数据存储的内容。另外,关于数据存储方面的一些补充和讲解

        到此这篇关于C语言深入探索数据类型的存储 的文章就介绍到这了,更多相关C语言数据存储 内容请搜索自由互联以前的文章或继续浏览下面的相关文章希望大家以后多多支持自由互联!

        上一篇:Qt为exe添加ico图片的简单实现步骤
        下一篇:没有了
        网友评论