【C数据的存储】

目录

 

一、数据类型基本归类

二、整数的存储

        原码反码补码

        大小端字节序

        练习一

        练习二

三、浮点数的存储


一、数据类型基本归类

signed:有符号

unsigned:无符号

数据在内存中默认为signed,除了char,char是否有符号取决于不同编译器。

整型:

char(char类型的本质是ASCII码值)、short、int、long

浮点型:

float、double

构造类型:

        数组类型:int [n]、char [n] ......

        n表示元素个数,元素个数不同,类型就不同,如int [1] 和 int [2] 就是不同类型

        结构体类型:struct

        枚举类型:menu

        联合类型:union

指针类型:

int* p、char* p、float* p、void* p ......

空类型:void

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

☆C语言中没有字符串类型,只能靠字符数组存储

二、整数的存储

        原码反码补码

整数在内存中以补码的方式存储。

原码:整数转化为二进制得到,最高位为符号位(1为负数,0为正数)

反码:原码符号位不变,其他位按位取反得到

补码:反码+1得到

正数的原码反码补码相同。

负数需要通过转换。

为什么在内存中整数要以补码的方式存储?

这是因为:

1.使用补码,可以将符号位和数值域统一处理,同时,加法和减法也可以统一处理(将减法转化为加法)。

2.补码与原码原码相互转换,其运算过程相同,无需额外硬件电路。

简单来说就是:

原码(符号位不变按位取反)→反码(+1)→补码

补码(符号位不变按位取反)→反码(+1)→原码

 我们可以通过查看变量中存放的数据来得知整数在内存中是否存放的是补码。

 由此可见整数在内存中存放的是补码,但是顺序是反着的,这是因为大小端字节序的问题。

        大小端字节序

为什么有大小端字节序?

计算机内部优先处理低字节,效率更高,所以在大部分计算机中都是使用小端存储模式,而在其他一些场景中,大端存储模式效率更高。

大端:将数值低位放入高地址处,高位放入低地址处。

小端:将数值低位放入低地址处,高位放入高地址处。

在我的编译器下,存储方式为小端。

        练习一

 写一段判断当前机器大小端字节序的代码。

#include<stdio.h>
int main()
{
	int a = 1;
	if (*(char*)&a)//将a的地址强制转换为char*类型,一次只能向后访问1个字节
		printf("小端");
	else
		printf("大端");
	return 0;
}

        练习二

判断下列代码输出结果

 1.

int main()
{
	char a = -1;
	signed char b = -1;
	unsigned char c = -1;
	printf("a=%d,=%d,c=%d", a, b, c);
	return 0;
}

答案:-1,-1,255

a 和 b 都是正常打印,主要分析 c

2. 

int main()
{
	char a = -128;
	printf("%u\n", a);
	return 0;
}

答案:4294967168

 3.

int main()
{
	char a = 128;
	printf("%u\n", a);
	return 0;
}

答案:4294967168

 4.

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

答案:-10

5.

int main()
{
	unsigned int i;
	for (i = 9; i >= 0; i--)
	{
		printf("%u\n", i);
	}
	return 0;
}

答案:死循环。

 

int main()
{
	char a[1000];
	int i;
	for (i = 0; i < 1000; i++)
	{
		a[i] = -1 - i;
	}
	printf("%d", strlen(a));
	return 0;
}

答案:255

首先我们要知道char类型的取值范围是:-128~127

可以将它们想象成一个圈。

为什么10000000是-128

 同时,我们也可以观察到,在这个‘蛋’中,只要不断加1,就可以得到下一个数,反之,不断-1,也可以得到下一个数。

只有当-1+1时,会得到0,而strlen遇到0会停下,因为 i 是从0开始的,所以我们只需要求出从0到1之间有多少个数字就可以了,这时我们根据图片就可以看出从0~1正好是一整个循环,其中共有255个数字。

6.

unsigned char i = 0;
int main()
{
	for (i = 0; i <= 255; i++)
	{
		printf("hello world\n");
	}
	return 0;
}

答案:死循环

无符号char类型的取值为0~255,当值为255时再+1,就又变回1,再次循环。

7.

int main()
{
	unsigned int a = -1;
	printf("%d", a);
	return 0;
}

答案:-1

打印结果是根据打印类型为准的,不管数据原来的类型。

-1在内存中为:11111111111111111111111111111111

虽然类型是unsigned int ,但是打印是以 int 类型打印,所以还是将根据符号位判断这个数为负数,所以需要转换为原码,最后得到-1。

8.

int main()
{
	if (strlen("abc") - strlen("abcdef") >= 0)
		printf(">");
	else
		printf("<");

	return 0;
}

答案:>

因为 strlen 返回类型为 size_t = unsigned int (无符号整型),而一个无符号整型减去无符号整型,最后的结果还是无符号整型,不可能是负数。

三、浮点数的存储

 首先来一段代码,看它的输出结果。

int main()
{
	int n = 9;
	float* p = (float*)&n;

	printf("n的值为:%d\n", n);
	printf("*p的值为:%d\n", *p);

	*p = 9.0;
	printf("n的值为:%d\n", n);
	printf("p的值为:%d\n", *p);

	return 0;
}

输出结果:

虽然不知道为什么会是这样的结果,但是我们可以初步得出结论:整数和浮点数在内存中的存储方式是不同的。

浮点数的存储规则

 任意一个二进制浮点数都可以表示成以下形式:

V=(-1)^S*M*2^E

V表示任意一个浮点数

S代表符号位,当S=0时,V为正数,当S=1时,V为负数

M代表一个有效数字,大于1,小于2

E表示指数位

而最后在内存中存储的就是S、M、E这三个值

简单来说,就是先将浮点数写成科学计数法的形式,最后在前面加上正负就可以了。

 不精准的情况:

不同类型浮点数存储方式:

 对于32位的浮点数(float),最高的一位是符号位S,接着8位是指数E,剩下23位为有效字母M

 对于64位的浮点数(double),最高的一位是符号位S,接着11位是指数E,剩下52位为有效字母M

 在存储浮点数时,还有如下规则:

1.由于M始终是以1开头,所以将M存入内存中时,省去开头的1,拿出数据时再加上。这样就可以多存一位数,提高数据精度。

2.将E的值设为无符号类型,这样可以存放更多的数据。

到第二条就出现问题了,因为我们的E是有可能出现负数的。

这时,为了不出现负数,规定在E存入内存时,需要加上一个中间数,float类型下中间数为127,double类型下中间数为1023

例如,在 float 下E为2时,存入内存中的值就是2+127=129,写成二进制就是10000001

验证存储规则:

 在取出浮点数的时候,又分为三种情况:

1. E不为全0或不为全1

将指数的值减去127(或1023),得到真实值,再将有效数字M加上原来省去的1,得到原来的值,进行正常运算。

2. E为全0

直接将E的值等于1-127= - 126,M前也不加上1,而是变成0,为了表示0或是接近于0的数字。

3. E为全1

表示为正负无穷大

解释题目:

int main()
{
	int n = 9;
	float* p = (float*)&n;

	printf("n的值为:%d\n", n);
	printf("*p的值为:%d\n", *p);

	*p = 9.0;
	printf("n的值为:%d\n", n);
	printf("p的值为:%d\n", *p);

	return 0;
}

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值