C语言进阶(七) - 数据的储存

前言

本节深入解释整型数据与浮点型数据在内存中的存储方式,旨在进一步理解底层的数据存储。

1. 数据类型回顾

1.1 基本数据类型

类型字节数
char字符型1byte
short短整型2byte
int整型4byte
long长整型
long long更长的整型
float单精度浮点型4byte
double双精度浮点型8byte
long double多精度浮点型(长精度浮点型)

关于类型字节数的大小:

具体的长度(大小)标准并没有规定,只是规定了以下:
长整型至少应该和整型一样长,整型至少应该和短整型一样长。
long double 至少和 double 一样长, double 至少和 float 一样长。

1.2 类型的意义:

  1. 使用这个类型开辟内存空间的大小。
  2. 看待内存空间的视角。

1.3 整型

charsigned char
unsigned char
short intsigned short int
unsigned short int
intsigned int
unsigned int
long intsigned long int
unsigned long int

有符号类型与无符号类型在取值范围上有差异。
无符号类型没有负数,有符号类型既有正数,也有负数。
char类型数据在内存中本质上是以整数的形式存放的,也属于整形家族。
char类型到底是有符号还是无符号C语言标准并没有规定,大多数编译器中默认是有符号char

1.4 浮点型

float
double
long double

1.5 指针类型

char*字符指针
int*整型指针
float*单精度浮点型指针
double*双精度浮点型指针
void*无类型指针(空类型)

1.6 构造类型

1.6.1 数组类型

数组也是一种类型。

例如:int arr[10];去掉数组名就是数组的类型,其数组类型是 int [10]

1.6.2 结构体类型 struct

struct tag{
    member_list;
}member_veriable;

1.6.3 枚举类型 enum

enum color{
    RED,//RED == 0
    YELLOW = 100,
    BLUE//BLUE == 101
}a;

enum color是一个枚举类型,a是一个枚举变量。

1.6.4 联合类型 union

一个例子:

union data {
	int a;
	float b;
	double c;
};

2. 整型在内存中的存储

创建一个整型类的变量时,首先在内存中为其开辟相应的空间,接着就是如何存储这个变量的值。

2.1 原码、反码、补码的介绍

一个十进制整数可以按其他进制进行表示,如:二进制、八进制、十六进制等。

在计算机中任何数据本质上都以二进制的0和1进行储存。
而二进制又有三种表示形式:原码、反码、补码。
而这三种表示形式中都可以分为两部分:符号位 + 数值位
符号位表示整数的正负:0为正整数,1为负整数。
数值位表示整数的具体大小。

正整数的原码、反码、补码均相同。

int a = 10;
//00000000 00000000 00000000 00001010 - 原码 - 反码 - 补码

负整数需要注意:

原码:整数直接写出的二进制形式。
反码:原码的符号位不变,其它位按位取反得到反码。
补码:反码+1得到补码。

在计算机中数据均以二进制形式的补码进行储存的,因为使用补码可以将符号位和数值位进行统一处理;
加法与减法也可以统一处理;
补码与原码的相互转换运算过程是相同的,不需要额外的硬件电路。

int a = -10;
//10000000 00000000 00000000 00001010 - 原码
//11111111 11111111 11111111 11110101 - 反码
//11111111 11111111 11111111 11110110 - 补码

2.2 具体的例子

#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; 
}

//a
//-1的原码:10000000 00000000 00000000 00000001
//-1的反码:11111111 11111111 11111111 11111110
//-1的补码:11111111 11111111 11111111 11111111 -> 11111111 -> a
//%d输出时:	 11111111 11111111 11111111 11111111 - 整形提升之后的补码
//         	10000000 00000000 00000000 00000000 - 反码
//          10000000 00000000 00000000 00000001 - 原码 - > -1
//b与a同理
//c
//-1的原码:10000000 00000000 00000000 00000001
//-1的反码:11111111 11111111 11111111 11111110
//-1的补码:11111111 11111111 11111111 11111111 -> 11111111 -> c
//%d输出时:	 00000000 00000000 00000000 11111111 - 整形提升之后的补码 -> 255

运行结果:
.png

#include <stdio.h>
int main(){
    char a = -128;
    printf("%u\n",a);
    return 0; 
}

//a
//原码 - 10000000 00000000 00000000 10000000
//反码 - 11111111 11111111 11111111 01111111
//补码 - 11111111 11111111 11111111 10000000 -> 10000000 -> a
//%u输出
//先整形提升 - 11111111 11111111 11111111 10000000 -> 4294967168

运行结果:
.png

#include <stdio.h>
int main(){
    char a = 128;
    printf("%u\n",a);
    return 0;
}

//a
//原码、反码、补码 - 00000000 00000000 00000000 10000000 -> 10000000 -> a
//%u形式输出
//先整形提升 11111111 11111111 11111111 10000000 -> 4294967168

运行结果:
KDO2KE64@BYE_PUQK)WYINO.png

#include <stdio.h>

int main() {
	int i = -20;
	unsigned  int j = 10;
	printf("%d\n", i + j);
	return 0;
}

//i 10000000 00000000 00000000 00010100 - 原码
//  11111111 11111111 11111111 11101011 - 反码
//  11111111 11111111 11111111 11101100 - 补码
//---------------------------------------------
//j 00000000 00000000 00000000 00001010 - 原码、反码、补码
//---------------------------------------------
//11111111 11111111 11111111 11101100 - i - 补码
//00000000 00000000 00000000 00001010 - j - 补码
//11111111 11111111 11111111 11110110 - i + j - 补码
//以%d形式输出
//11111111 11111111 11111111 11110110 - 补码
//10000000 00000000 00000000 00001001 - 反码
//10000000 00000000 00000000 00001010 - 原码 -> -10

运行结果:
@WLZKTUP856GT24U$H7T{H2.png

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

strlen()函数求的是字符串的长度,到'\0'为止,关键是找到'\0'的位置。
一个有符号字符所能储存整数的范围是-128~127,超过范围时就要舍去一定的二进制位数。
字符数组存放的依次是:
-1, -2, -3, ... , -128, 127, 126, ... , 2, 1, 0, -1...
在第一次出现'\0'(就是0)之前共有255个字符。

unsigned_char范围.png
运行结果:
.png


3. 浮点型数据在内存中的储存

浮点型的意思是数据在内存中的储存是浮动的,也就是不准确的,所以不叫做实数型数据。

3.1 浮点型数据的几种表示:

3.141591.01f2.5lf2E102E-103e93e-9等。

3.2 具体存储介绍

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

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

IEEE754规定float类型:最高位是符号位S,接着是8位指数E,剩下的是23位的有效数字M。

T}(P1VV46I@NV6@`@6WMSMA.png

规定double类型:最高位是符号位S,接着是11位的指数E,剩下的是52位的有效数字M。

{~7FGJNO5TDQ2T43PDM8DYK.png
一些特别规定:
有效数字M

有效数字M介于1与2之间,形式为**1.XXXXXXXXXX****XXXXXXXXXX**表示小数部分。
IEEE754规定,在计算机内部保存M时,默认这个数的第一位总是1,因此便省去1,只保存后面的小数部分。
也就是说,保存时把1与小数点去掉,读取时在把1与小数点加上。
好处是节省了1位有效数字。

指数E
存入内存时:

E是一个无符号整数,如果E为8位,取值范围为 0 → 255 0\to255 0255;如果E为11位,取值范围为 0 → 2047 0\to2047 02047。但科学计数法中E可以是负数,IEEE754规定,存入内存时E的真实值必须再加上一个中间数。
对于8位的E,中间数为127;对于11位的E,中间数为1023。

从内存中取出时:

E不全为0或不全为1:
指数E的计算值减去127(或1023)得到真实值,再将有效数字M前加上第一位的1。

E全为0
指数E等于1-127(或1-1023)直接就是真实值,不在计算。
有效数字M前不再加上第一位的1,而是加上0,表示0和接近±0的很小的数字。

E全为1
表示一个很大的数,当有效数字全为0时,表示±无穷大(正负有符号位决定)。

3.3 一个例子

#include <stdio.h>

int main()
{
	int n = 9;
	float* pFloat = (float*)&n;
	printf("n的值为:%d\n", n);
	printf("*pFloat的值为:%f\n", *pFloat);
	//9 - 00000000 00000000 00000000 00001001 - 原码、反码、补码
	//单精度浮点数视角
	//0 00000000 00000000000000000001001
	//(-1)*0 * 0.00000000000000000001001 * 2^(-126) ->1.001*2^(-146) -> 0.000000
	*pFloat = 9.0;
	printf("num的值为:%d\n", n);
	printf("*pFloat的值为:%f\n", *pFloat);
	//9.0 - (-1)*0 * 1.001 * 2^3
	//S==0, M==1.001, E==3
	//0 10000010 00100000000000000000000
	//有符号整数视角
	//01000001 00010000 00000000 00000000 - 补码、反码、原码 -> 1,091,567,616

	return 0;
}

在这里插入图片描述


4. 字节序 - 大小端

4.1 大小端字节序出现的原因

数据储存的是以字节(byte)为单位的(或者说以char类型为单位),如果一个数据使用一个字节就可以储存就不存在大小端字节序的问题了。但存在数据类型超过一个字节的情况(如:int(4byte),float(byte)等),这几个字节在内存中的顺序就可以有不同的方式,有两个常用的情况:大端字节序、小端字节序。
X86结构是小端模式。

4.2 大小端概念

大端储存:数据的低位保存到内存的高地址中,数据的高位保存到内存的低地址中。
小端储存:数据的低位保存到内存的低地址中,数据的高位保存到内存的高地址中。

.pngEF7_6%9HNJ}X5@$9~YRW9M2.png

4.3 设计程序检查当前系统的字节序

#include <stdio.h>

int check_ststem();

int main() {
    if (check_ststem()) {
        printf("小端\n");
    }
    else {
        printf("大端\n");
    }
    return 0;
}
int check_ststem() {
    int a = 1;
    //00000000 0000000 00000000 00000001 - 二进制序列
    //0x00 00 00 01 - 16进制序列
    char* p = (char*)&a;
    return *p;
}

结语

本文主要介绍了整型数据域浮点型数据在内存中的储存,同时还了解了字节序的相关概念。


END

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

re怠惰的未禾

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值