C语言笔记13:数据在内存中的存储

文章目录

C语言笔记13:数据在内存中的存储

一、整数在内存中的存储

整数在内存中存放的是补码
补码:

正数原码、反码、补码一样

负数:

  • 原码是符号位为1

  • 反码是原码除了符号位按位取反(其实反码和原码是一个性质)

  • 补码是反码+1
    存放补码的原因:

  • 可以使用加法来进行减法运算,补码是模运算

  • 0的表示只有一个没有+0和-0

二、大小端字节序和字节序判断

什么是大小端

当一个数据比如short类型等,大小超过一个字节,就会有字节序的问题。

一个整数1存放在内存中:

大端字节序:

小端字节序:

练习

2.1判断当前机器大小端
c 复制代码
int Check_Sys()//或者也可以叫做Is_Small()
{
	int i = 1;
	return (*(char*)&i);
}

int main()
{
	int ret = Check_Sys();
	if(ret == 1)
	{
		printf("小端\n");
	}
	else
	{
		printf("大端\n");//ret==0
	}
}
c 复制代码
int Check_Sys()
{
	union
	{
		int i;
		char c;
	}un;
	un.i = 1;
	return un.c;
}
2.2判断下面程序的运行结果
c 复制代码
#include <stdio.h>

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

字符型整数运算会发生整型提升。赋值的时候就是把寄存器中的4字节整型的-1最低位字节赋值给了三个变量,看这三个数据在内存中的情况:

printf("a=%d",a);这里会把a的值拿出来,放入寄存器中,由于a是一个char类型,在visual studio 2022下默认是有符号的类型,所以进行符号位扩展。所以在寄存器中ff扩展为 ff ff ff ff

同理b在寄存器中也扩展为了ff ff ff ff

而c是一个无符号数,进行0扩展,所以就是00 00 00 ff

ff ff ff ff 按照%d的形式打印就是-1

00 00 00 ff按照%d的形式打印就是255
结果:

2.3
c 复制代码
#include <stdio.h>

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

-128一个字节时是1000 0000 由于a是有符号类型,符号位扩展为ff ff ff 80,按照无符号打印,就是4,294,967,295 - 127 = 4,294,967,168。
结果:

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

按理来说,char类型的变量表示的数值范围是0~127-1~-128然而这里却用将128赋值给了a,那么实际是怎么运算的呢?

其实赋值操作的时候是把四字节数据截断赋值给一个字节,虽然char类型不能表示128,但是int类型可以,int 类型的128用4字节表示就是00 00 00 80。截断之后就变成了80,然后按照%u打印,先将80进行整型提升,符号位是1,那就是ff ff ff 80,按照%u打印,就是4,294,967,168和上面一样。
运行结果:

2.4
c 复制代码
#include <stdio.h>
#include <string.h>

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

开辟一块空间,然后赋值。内存示意图:

问:什么时候数组a里的元素值等于0?

补码数值示意图:

char类型表示的负数范围是-1~-128-129会被如何解释?-129首先赋值是四字节的-129截断成一字节:ff ff ff 7f,也就是7f,也就是说,负数的值小于-128之后就会像图片一样往0靠近。

所以从-1开始递减到0一共有256个数字,其中0不计,所以strlen就是255。
运行结果:

2.5
c 复制代码
#include <stdio.h>
unsigned char i = 0;
int main()
{
	for(i = 0;i <= 255;i++)
	{
		printf("hello world\n");
	}
    return 0;
}

所谓i++,其实是,i = i + 1;是一个赋值操作,unsigned char 类型的 i 表示的最大数应该是255,那么256是怎么表示的呢?四字节的256也就是00 00 01 00然后发生截断变成00,也就是说,对i一直++它的值也会像这个环一样循环:并且判断i <= 255的时候,进行了0扩展,所以寄存器中使用的值和它的值一样。

所以这个程序是个死循环。

运行结果:死循环打印hello world

c 复制代码
#include <stdio.h>
int main()
{
	unsigned int i;
	for(i = 9;i >= 0;i--)
	{
		printf("%u\n",i);
	}
	return 0;
}

和上面一样,i的值不会被解释成小于0的数,所以这也是个死循环。先打印9 ~ 0后打印极大的数递减。其实就是2^32 - 1开始递减。

2.6
c 复制代码
#include <stdio.h>

int main()
{
	int a[4] = {1,2,3,4};
	int *ptr1 = (int*)(&a + 1);
	int *ptr2 = (int*)((int)a + 1);
	printf("%x,%x",ptr1[-1],*ptr2);
	return 0;
}

内存示意图:小端字节序,x86地址。

ptr1[-1]应该是00 00 00 04

*ptr2应该是02 00 00 00
运行结果:

三、浮点数在内存中的存储

浮点数的表示:

一张图搞清楚:

上面的是float的存储,下面的是double,至于long double 类型,根据平台不同大小不一样。

  • S:0表示正数,1表示负数

  • M:当E不为全0时,M表示为1.M,当E为全0时,M表示为0.M,E为全1时,如果M为全0就表示无穷大,否则就是NaNNot a Number

  • E:E是一个无符号整数,但是指数有负数,所以存入内存的E的值是真实值+8/11位指数位的中间数(127/1023)。全0时为-126,全1时不为128。

为什么E为全0的时候,M就要表示为0.M呢?

我们来看两种方案:

方案1:

E为全0时,M表示为0.M

最小规格化数:1.0*2-126

最大非规格化数:0.111...11*2-126

最小非规格化数:0.000...000*2-126

方案2:

E为全0时,M依然表示为1.M

最小规格化数:1.0*2-126

最大非规格化数:1.111...111*2-127

最小非规格化数:1.000...000*2-127

对比之下我们可以发现,方案1和方案2都实现了规格化数和非规格化数的过渡,但是方案1可以一直表示接近甚至是等于0的值。而方案二却因为要保证1.M而无法表示接近或者等于0的值。

什么是规格化和非规格化?

规格化就是保证精度但是牺牲表示范围,非规格化就是丢失一定的精度去保证表示更大的范围。

例题
c 复制代码
#include <stdio.h>
int main()
{
	int n = 9;
	float *pFloat = (float*)&n;
	printf("n的值为:%d\n",n);
	printf("*pFloat的值为:%f\n",*pFloat);
	
	*pFloat = 9.0f;
	printf("n的值为:%d\n",n);
	printf("*pFloat的值为:%f\n",*pFloat);
	
	return 0;
}

00 00 00 09:0000 0000 0000 0000 0000 0000 0000 1001

S:0

E:0000 0000

M:0000 0000 0000 0000 0001 001

此时E为全0,M表示0.M,也就是0.0000 0000 0000 0000 0001 001 * 2-126。而float默认打印小数点后六位所以打印结果应该是0.000000

*pFloat = 9.0f;

9.0表示为二进制就是1001.0也就是1.001*23,内存中就是:

S:0

E:1000 0010

M:0010 0000 0000 0000 0000 000

也就是:0100 0001 0001 0000 0000 0000 0000 0000 十六进制:41 10 00 00

打印成%d就是1,091,567,616
运行结果:

相关推荐
saoys9 小时前
Opencv 学习笔记:图像绘制(直线 / 圆 / 椭圆 / 矩形 / 多边形 + 文字添加)
笔记·opencv·学习
不会c嘎嘎9 小时前
QT中的常用控件 (四)
开发语言·qt
bing.shao9 小时前
AI在电商上架图片领域的应用
开发语言·人工智能·golang
数据轨迹0019 小时前
AAAI AMD:多尺度预测MLP反杀Transformer
经验分享·笔记·facebook·oneapi·twitter
Aliex_git9 小时前
性能优化 - Vue 日常实践优化
前端·javascript·vue.js·笔记·学习·性能优化
栈与堆9 小时前
LeetCode-88-合并两个有序数组
java·开发语言·数据结构·python·算法·leetcode·rust
彩妙不是菜喵9 小时前
C++:类与对象
开发语言·c++
董世昌419 小时前
添加、删除、替换、插入元素的全方法指南
java·开发语言·前端
Yu_Lijing9 小时前
基于C++的《Head First设计模式》笔记——抽象工厂模式
c++·笔记·设计模式