文章目录前言1. 整数在内存中的存储2. 大小端字节序和字节序判断2.1 什么是大小端?2.2 为什么有大小端?2.3 练习2.3.1 练习12.3.2 练习2最关键的坑:printf 与 %d(整型提升)为什么 b (signed char) 打印出 -1?为什么 c (unsigned char) 打印出 255?为什么 a (char) 是不确定的?2.3.3 练习3第一步:8位内存存储第二步:整型提升与格式说明符第三步:数学计算(算出结果)核心要点总结2.3.4 练习4第一步:变量赋值(溢出)第二步:整型提升与符号扩展(“大变身”)第三步:%u 无符号打印核心要点总结2.3.5 练习51. 核心前提:char 的取值范围2. for 循环与二进制溢出(回绕)3. strlen 的工作原理4. 最终结果5. 深入理解:为什么是255而不是256?6. 扩展思考:如果数组更大呢?7. 实际应用中的注意事项8. 总结要点2.3.6 练习62.3.7 练习71. 环境前提:小端字节序 X86环境2. 第一个输出:ptr1[-1]3. 第二个输出(难点):*ptr24. 图解 *ptr2 到底读了什么5. 最终数字的由来3. 浮点数的存储3.1 浮点数存储3.2 IEEE 754 对有效数字 M 和指数 E 的特殊规定3.3 浮点数的三种存储情况详解🚨 1. 浮点数在内存中的根本结构(以 32 位 float 为例)⚙️ 情况一:规格化数(最常见的数字)⚙️ 情况二:非规格化数(接近 0 的小数)⚙️ 情况三:特殊值(无穷大和 NaN)总结对比表3.4 题目详解示例代码第一部分:写入整数,按浮点数读取1. 整数 9 在内存中的存储2. 强制类型转换3. 按浮点数规则解读第二部分:写入浮点数,按整数读取1. 写入浮点数 9.02. 按整数规则读取3. 验证浮点数读取内存变化示意图核心要点总结扩展思考前言在C/C++编程中,理解数据在内存中的存储方式是深入掌握底层原理的关键。无论是简单的整数运算,还是复杂的浮点数处理,最终都需要转化为二进制形式在计算机内存中进行存储和操作。然而,许多开发者在面对类型转换、字节序、整型提升、浮点数精度等问题时,常常感到困惑,甚至写出存在潜在风险的代码。本文旨在系统性地解析C/C++中数据在内存中的存储机制,从最基础的整数原码、反码、补码表示,到复杂的大小端字节序,再到浮点数的IEEE 754标准存储格式。通过大量实际代码示例和逐步分析,我们将深入探讨:整数存储原理:为什么计算机使用补码?原码、反码、补码之间如何转换?大小端字节序:什么是大端和小端?为什么会有这两种存储模式?如何判断当前系统的字节序?整型提升与类型转换:当char类型用%d打印时发生了什么?为什么unsigned char和signed char会有不同的结果?浮点数存储细节:IEEE 754标准如何表示浮点数?规格化数、非规格化数、特殊值(无穷大、NaN)有什么区别?经典案例分析:通过多个练习题目,深入理解数据存储的实际应用和潜在陷阱。无论你是正在学习C/C++的初学者,还是希望深入理解计算机底层原理的中级开发者,本文都将为你提供清晰、系统的知识体系。通过阅读本文,你将能够:准确理解数据在内存中的二进制表示掌握类型转换和整型提升的底层机制熟练分析涉及字节序和内存对齐的问题深入理解浮点数精度问题和特殊值的处理编写更加健壮、可移植的底层代码让我们开始这段探索内存存储奥秘的旅程吧!1. 整数在内存中的存储在讲解操作符的时候,我们就讲过了下面的内容:整数的二进制表示方法有三种,即:原码、反码和补码。有符号的整数,三种表示方法均有符号位和数值位两部分。符号位都是用0表示“正”,用1表示“负”,最高位的一位是被当做符号位,剩余的都是数值位。正整数的原、反、补码都相同。负整数的三种表示方法各不相同。原码:直接将数值按照正负数的形式翻译成二进制得到的就是原码。反码:将原码的符号位不变,其他位依次按位取反就可以得到反码。补码:反码 + 1 就得到补码。对于整型来说:数据存放在内存中其实存放的是二进制的补码。为什么呢?在计算机系统中,数值一律用补码来表示和存储。原因在于:使用补码,可以将符号位和数值域统一处理;同时,加法和减法也可以统一处理(CPU只有加法器);此外,补码与原码相互转换,其运算过程是相同的,不需要额外的硬件电路。2. 大小端字节序和字节序判断当我们了解了整数在内存中存储后,我们调试看⼀个细节:2.1 什么是大小端?其实超过一个字节的数据在内存中存储的时候,就有存储顺序的问题,按照不同的存储顺序,我们分为大端字节序存储和小端字节序存储,下面是具体的概念:大端(存储)模式:是指数据的低位字节内容保存在内存的高地址处,而数据的高位字节内容,保存在内存的低地址处。小端(存储)模式:是指数据的低位字节内容保存在内存的低地址处,而数据的高位字节内容,保存在内存的高地址处。上述概念需要记住,方便分辨大小端。2.2 为什么有大小端?为什么会有大小端模式之分呢?这是因为在计算机系统中,我们是以字节为单位的,每个地址单元都对应着一个字节,一个字节为 8 bit 位。但是在 C 语言中除了 8 bit 的char之外,还有 16 bit 的short型,32 bit 的long型(要看具体的编译器)。另外,对于位数大于 8 位的处理器,例如 16 位或者 32 位的处理器,由于寄存器宽度大于一个字节,那么必然存在着一个如何将多个字节安排的问题。因此就导致了大端存储模式和小端存储模式。例如:一个 16 bit 的short型变量x,在内存中的地址为0x0010,x的值为0x1122,那么0x11为高字节,0x22为低字节。对于大端模式,就将0x11放在低地址中,即0x0010中,0x22放在高地址中,即0x0011中。小端模式,刚好相反。我们常用的 X86 结构是小端模式,而 KEIL C51 则为大端模式。很多的 ARM、DSP 都为小端模式。有些 ARM 处理器还可以由硬件来选择是大端模式还是小端模式。2.3 练习2.3.1 练习1请简述⼤端字节序和⼩端字节序的概念,设计⼀个⼩程序来判断当前机器的字节序。(10分)–百度笔试题代码展示intmain(){inta=1;if(*(char*)a==1){printf("小端\n");}else{printf("大端\n");}return0;}//代码2intcheck_sys(){inta=1;if(*(char*)a==1)return1;//小端elsereturn0;//大端}//代码3intcheck_sys(){inta=1;return(*(char*)a);}intmain(){if(check_sys()==1){printf("小端\n");}else{printf("大端\n");}return0;}2.3.2 练习2思考一下为什么会出新这种结果详细讲解这段代码定义了三个字符类型的变量,并赋了同样的值-1:signedcharb=-1;// b 在内存中是:11111111 (8位,代表 -1)unsignedcharc=-1;// c 在内存中也是:11111111 (但代表 255)chara=-1;// a 在内存中是:11111111 (但代表什么取决于编译器)在计算机内存中,整数是以补码形式存储的。对于 8 位(1字节)的char类型,-1的二进制补码是11111111。注意:标准 C/C++ 中,默认的char到底是有符号(signed char)还是无符号(unsigned char)是由编译器决定的。所以变量a的值存在不确定性。最关键的坑:printf 与 %d(整型提升)代码中使用了:printf("a = %d, b = %d, c = %d\n"