• C语言-整数和浮点数在内存中的存储-详解-上


    1.前言

    在C语言的使用中,需要时刻关注数据的类型,不同类型交替使用可能会发生错误,下面通过一个简单的例子来说明这一点:

    #include 
    int main()
    {
    	int n = 5.5;
    	float* p = (float*)&n;
    	printf("%d\n", n);
    	printf("%f\n", *p);
    	*p = 5.5;
    	printf("%d\n", n);
    	printf("%f\n", *p);
    	return 0;
    }
    

    这段代码的运行结果:
    在这里插入图片描述
    同一个5.5,为什么会输出不同的结果?
    要理解这个问题,需要清楚整数和浮点数如何在内存中存储。
    下文将详细介绍整数在内存中的存储。

    2.整数

    2.1无符号整数

    这种数只能表示非负整数。
    在二进制表示中,每一位都是数值位,没有符号位。
    如:

    unsigned int n1 = 0xffffffff;
    printf("(n1 = 0x%x)%u",n1,n1);
    printf("INT_MAX=%d\n", INT_MAX);
    

    n1的二进制表示:
    在这里插入图片描述
    运行结果:
    在这里插入图片描述
    这里的INT_MAX指的是有符号整数的最大值,可以发现,无符号整数能比有符号整数更大。

    2.2原码、反码、补码

    原码、反码、补码是整数的三种二进制表示方法。
    对于无符号整数,三种方法没有区别。
    对于有符号整数,三种方法均分为符号位和数值位两个部分。

    符号位

    符号位指的是最高位,用0表示正数,用1表示负数。

    最大值

    整数占32个比特位,
    因此,无符号的最大值为:
    在这里插入图片描述
    有符号的多了一个符号位,因此,最大值为:
    在这里插入图片描述
    可以简记为二十亿。
    在实际应用中,特别是在OJ题中,题目最后有时会给数据范围。
    这时,需要与数据类型可存储的最大值比较,判断是否会溢出,以此调整做题方法。
    例如,在我的用C语言完成高精度问题中,就展示了一种溢出的实例与相应的解决方法。
    再例如:

    int n2 = 0xffffffff;
    printf("(n2 = 0x%x)%d\n", n2, n2);
    

    n2的二进制表示:
    在这里插入图片描述

    运行结果:
    在这里插入图片描述
    n2 在内存中的表示为 0xffffffff,运行结果会显示 n2 的值为 -1。
    最高位是1,因此是个负数,但-1是怎么来的,这就涉及到了原码、反码、补码的相互转换。

    转换过程

    正数:原码、反码、补码相同。
    负数:

    • 原码由数值直接翻译为二进制得到。
    • 反码由原码符号位不变,其他位按位取反得到。
    • 补码由反码加一得到。

    示例:
    在这里插入图片描述
    在内存中,整数存的是补码,而赋值操作,是将二进制值直接赋给变量,并直接影响它在内存中的值,这里可以调试看看:
    在这里插入图片描述
    而打印的是数值,需要将补码再次转化成原码:
    补码->符号位不变,其他位按位取反->反码->加一->原码
    这就是为什么给n2赋值了0xffffffff后,打印结果为-1。

    负数原码、反码、补码的相互转换:

    符号位不变,其他位按位取反
    加一
    符号位不变,其他位按位取反
    加一
    原码
    反码
    补码

    补码的意义

    现代计算机系统几乎都采用补码表示有符号的整数,原因在于补码具有许多优点:

    简化算术运算

    例如,补码能使减法变成加法:
    1 - 1 == 1 + (-1):
    在这里插入图片描述
    因此,在硬件设计中,只需要一个加法器就能完成所有的基本算术运算。

    易于转换

    转换一个有符号整数到它的相反数的过程在补码表示中非常简单:

    1. 取反
    2. 加一
    按位取反,加一
    按位取反,加一
    正数
    负数

    还是用1和-1举例子:
    在这里插入图片描述
    这个过程之所以简单,是因为它只需要两次基本的硬件操作:一次按位取反操作和一次加法操作。这对于硬件设计来说是非常高效的,因为它不需要额外的复杂逻辑来区分正数和负数的转换。

    方便溢出处理

    在补码系统中,如果发生溢出,结果将会被截断,只保留最低的有效位。
    这就是为什么1的补码加-1的补码结果为0。
    这也意味着,如果一个运算的结果超出了表示范围,它将会被“环绕”回到可用的表示范围内:
    在这里插入图片描述


    希望本篇文章对你有所帮助!并激发你进一步探索C语言的兴趣!
    本人仅是个C语言初学者,如果你有任何疑问或建议,欢迎随时留言讨论!让我们一起学习,共同进步!

  • 相关阅读:
    云原生Kubernetes:CRI 容器运行时接口
    Day26-Xpath数据解析
    在 Docker 容器中配置 PIP 源:加速 Python 包安装的实践指南
    使用Pyarmor保护Python脚本不被反向工程
    21 搜索二维矩阵 II
    手机打开 第三方 “微信、快手、QQ、电话、信息” 等
    计算机竞赛 基于深度学习的中文情感分类 - 卷积神经网络 情感分类 情感分析 情感识别 评论情感分类
    【Ubuntu笔记】录屏安装
    互联网加竞赛 机器视觉目标检测 - opencv 深度学习
    hadoop2.4.1伪分布式搭建
  • 原文地址:https://blog.csdn.net/2401_86587256/article/details/142116078