数据在内存中的存储(C语言版本)!

C语言内存存储详解:数据类型与浮点数存储模式
本文详细介绍了C语言中的数据类型,包括整型、浮点型、自定义类型、指针和空类型,以及它们在内存中的存储机制,如原码、补码、反码的区别,以及大端和小端存储模式。通过实例展示了整型和浮点数的内存存储特性,以及编译器对存储模式的影响。

1.数据类型

        我们如果要解释C语言的各类数据如何在内存中进行存储,就不得不介绍一下C语言的数据类型,C语言的数据类型可分为整型家族,浮点型家族,自定义家族,指针家族和空类型。

        1.1整型家族

        我们日常中常用的就是char int类型,他们分为:以上的形式,其中由于char字符类型的在内存中以ASCII码的形式存储,本质还是整型数据,所以也把它归为整型一类.

        1.2 浮点型家族

        

        1.3 自定义家族

        

        1.4 指针家族

        

        1.5空类型

        

            以上就是C语言中常见的类型的声明形式~

            各个类型有能够开辟内存空间大小不同,能够存储的数据也不同,他们都有自己的应用场景,换句话说,他们看待数据和内存空间的视角也不同~


2.整型在内存中的存储

        2.1 原码,反码,补码        

        首先我们先了解,为什么会有原码,补码,反码这样的概念?直接引用一篇大佬的文章~什么是原码、反码和补码_原码反码补码_莪是男神的博客-CSDN博客

这里已经写的很清楚了~我们于是知道,计算机里存放的原来都是补码,接下来请看以下代码:

我们通过调试可以看到,对于a的值,内存中竟然是"倒着"的,他先把低位上的数据存放给低地址,再把高位存放的数据存放给了高地址,并不是向我们所想象的那样,按照正序存储~这是怎么一回事呢?

2.2 大端 小端存储模式

        为什么会出现以上情况呢?那是因为我们的VS编译器采用小端存储模式~

        

        为什么会有大小端之分呢?

        所以,大端小端没有优劣之分,只是由于编译器和系统环境的不同,使内存有了两种不同的存放模式,大端比较容易判断数据正负,小端比较方便强制类型转换~

    那如何判断我们的编译器使用的是大端还是小端呢?请看以下代码:

1.通过字符指针只访问一个字节的内存空间来判断

int check_sys()
{
    int i = 1;
    return (*(char*)&i);
}
int main()
{
    int ret = check_sys();
    if (ret == 1)
    {
        printf("小端\n");
    }
    else
    {
        printf("大端\n");
    }
    return 0;
}

2.通过联合体共用一块内存判断

int check_sys()
{
    union
    {
        int i;
        char c;
    }un;
    un.i = 1;
    return un.c;
}
int main()
{
    int ret = check_sys();
    if (ret == 1)
    {
        printf("小端\n");
    }
    else
    {
        printf("大端\n");
    }
    return 0;
}

通过以上两种判断方式,都可以判断出为该编译器为小端存储~

2.3 EXERCISES

no.1

int main()
{
	char a = -1;
	signed char b = -1;
	unsigned char c = -1;
	printf("a=%d,b=%d,c=%d", a, b, c);
	//第一个char和第二个char 都是有符号的char -1补码为11111111
	//%d打印时会将其认定为负数转化为原码打印 而c是无符号字符 %d认为补码就是原码 直接输出 255 
	//        -1    -1   255
	return 0;
}

 no.2   

int main()
{
	char a = -128;
	//%u 无符号整数打印
	printf("%u\n", a);
	//-128补码为 1 1000 0000 
	//char大小只有一个字节 发生截断
	//char中存放1000 0000
	//由于a是有符号的 打印时进行整型提升
	//前面补符号位 111111111111111111111110000000
	//%u无符号整数打印 认为a无符号 直接将补码转化为数值输出
	//一个很大的数出现了~
	return 0;
}

no.3

int main()
{
	char a = 128;
	printf("%u\n", a);
	//由于128 -128的特殊性,截断后二者在char类型的内存中存储相同
	//a同样有符号,进行整型提升时补1 1111111111111110000000
	//%u 认为他是一个无符号的数,直接输出一个很大的数
	//他们两个输出的数也应该是一样的~
	return 0;
}

no.4

int main() {
	int i = -20;
	//补码为          1111111111111101100
	unsigned int j= 10;
	//原码也就是补码为 1111111111111111010
	//相加为          1111111111111110110
	printf("%d\n", i + j);
	//i整型提升 i+j为unsigned int 
	//%d有符号输出 将其转化为原码 1000000000001010 结果为-10
	return 0;
}

 no.5

int main() {
	unsigned int i;
	for (i = 9; i >= 0; i--)
	{
		printf("%u\n", i);//i是无符号整数,不可能小于0
		//当i = 0,i--时,i=-1 以补码的形式存储的话为1111111111111111111 %u会认为他是一个巨大的数字
		//照样输出 这也形成了死循环
		Sleep(100);
	}
	return 0;
}

 no.6

int main()
{
	char a[1000];
	int i;
	for (i = 0; i < 1000; i++)
	{
		a[i] = -1 - i;
	}
	//strlen计算字符数组的有效长度 也就是说遇到a[i]中的'\0'字符停止
	//根据大转盘 -1为起始点 逆时针走 正好走255个 第256的数字为0
	printf("%d", strlen(a));
	return 0;
}

 no.7

unsigned char i = 0;
int main()
{
	for (i = 0; i <= 255; i++)
	{
		printf("hello world\n");
		//unsigned char”值始终介于“0”到“255”范围之间。 循环将无限执行。
	}
	return 0;
}

3.浮点数家族在内存中的存储

        猜猜下面这段代码会输出什么?

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

让我们看一下结果~

        我们不免产生疑问,第一次我们将一个整型变量的地址强转成一个浮点数指针,以浮点数类型输出值是0,我们对这个让他内部以浮点数方式存储,以整型方式输出,反而是一个特别大的数,这就说明,浮点数在内存中以自己独特的方式存储,并且和整型变量的存储完全不同(否则结果也不可能相差这么大)~ 那么浮点数到底在内存中怎么存储的呢?

这里大佬写的很明白了,本人讲的不如大佬好~ 【精选】【详细解说】单精度浮点数float取值范围_float 范围-CSDN博客

不过在这里指出,可以通过float.h查看浮点数存储的范围,非常方便~

这也就能解释为什么上面那个代码输出的值那么奇怪了~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值