数据在内存中的存储
整数在内存中的存储
在讲解操作符时 我们就已经学习了该部分的内容 这里我们回顾一下
整数的二进制表示方法有三种:原码 反码 补码
有符号的整数(unsigned) 三种表达方式均有符号位和数值位两部分 最高位的一位被当作符号位 符号位用0表示正 用1表示负 除符号位之外 其余为数值位
正整数的原、反、补码都相同
负整数的三种表达方式各不相同
原码:直接将数值按照正负数的形式翻译成二进制 得到的就是原码
反码:将原码的符号位不变 其他位依次取反 得到的就是反码
补码:反码+1 得到补码
对于整形来说:数据存放内存中其实存放的是补码
原因我这里就不过多叙述了 之前有讲过
大小端字节序和字节序判断
下面我们先给个代码来欣赏一下
整数在内存中存储的是二进制的补码
在调试窗口中 观察内存的时候 为了方便展示 显示的是16进制的值
存储的顺序是倒过来的!
接下来我们来了解两个知识点来解决为什么这里存储的顺序是倒过来的
什么是大小端?
其实超过一个字节的数据在内存中存储的时候 就有存储顺序的问题 按照不同的存储顺序 我们分为大端字节序存储和小端字节序存储 下面是具体的概念:
大端(存储)模式:
把一个数据的低位字节的内容存储到高地址处 把高位字节的内容存储到低地址处
(举例:123 这里的3即为低位)
小端(存储)模式:
把一个数据的低位字节的内容存储到低地址处 把高位字节的内容存储到高地址处
大小端不是以二进制位来讨论的 是以字节序来讨论的
我们之前的0x11223344的存储顺序就应该是小端存储
为什么有大小端?
这是因为在计算机系统中 我们是以字节为单位的 每个地址单元都对应着一个字节 一个字节为8bit位 但是在C语言中除了8bit位的char之外 还有16bit位的short型(要看具体的编译器) 另外 对于位数大于8位的处理器 例如16位或者32位的处理器 由于寄存器宽度大于一个字节 那么必然存着一个如何将多个字节安排的问题 因此就导致了大端存储模式和小端存储模式
练习1
这里的意思是 取n的地址 并指向地址首元素的地址 然后解引用得到该位置的值
解释:为什么我们不能写成char n
首先这种写法无法实现我们本题 其次将int型强转成char会有影响
在C或C++等语言中,当一个整型(int)被强制类型转换(cast)为字符型(char)时,内存中存储的数值实际上会发生截断。这是因为整型通常占用更多的内存空间(比如32位或64位,取决于具体的编译器和平台),而字符型通常只占用一个字节(8位)。
当进行这种转换时,只有整数值的最低有效字节(least significant byte, LSB)会被保留,并作为字符型值存储在内存中。其余的高位字节会被丢弃。
举个例子,假设在一个32位系统中,我们有一个整数值 int x = 65537(在二进制中表示为 0001 0000 0000 0001)。如果我们把这个整数值强制转换为 char 类型,只有最低的一个字节(0000 0001)会被保留,转换后的字符型值将是 1。
这种转换可能会导致数据丢失,特别是当整数值大于一个字节能够表示的范围(即大于255或小于-128,取决于是有符号还是无符号字符)时。因此,在进行这种转换时需要特别小心,确保转换后的值仍然有意义。
另外,还需要注意的是,字符型在内存中实际上也是以整数形式存储的,只不过它们的解释方式(即如何映射到字符集)与普通的整型不同。在ASCII编码中,字符型值 65 对应的是大写字母 'A',而字符型值 97 对应的是小写字母 'a'。因此,整型值转换为字符型后,可能会根据当前的字符编码(如ASCII、UTF-8等)被解释为一个特定的字符
练习2
对于signed char来说 有一个特殊的存在(-128)--它在内存中存放的是10000000
我们知道有符号的数来说 最高位代表符号位
对于10000000 这里就无法对其取反 +1操作得到原码了
而是直接翻译成 -128 因为它实际上无法存放在一个字节里
如果要将-128转换成原码 应该为 110000000(最高位表示符号位 这里就9个二进制数了)
反码 101111111 补码 110000000(内存中)---所以我们记住这个特殊的数即可
signed char类型的取值范围是-128~127
对于unsigned char(无符号) 取值范围为0~255
练习3
接下来我们解释一下
这里的char在VS中 会被认为signed char
然后将 -128存放在a中 即(10000000)
当我们使用printf函数并指定%u 作为格式说明符时 相当于我们告诉printf函数要将其对应的参数变为一个无符号整数(unsigned int)
a是一个char类型的变量。当a被传递给printf函数时,它首先会经历整型提升
整型提升是C语言标准规定的一种隐式类型转换,用于确保小的整数类型(如char和short)在作为函数参数传递或进行算术运算时能够转换为更大的整数类型(至少为int大小)。
当a作为参数传递给printf函数时,它首先被整型提升到int类型(如果int比char大)。然后,printf函数使用%u格式说明符来解释这个提升后的int值,将其作为无符号整数打印出来。
这里重要的是要理解,%u格式说明符并不“认为”a是unsigned int类型;它只是告诉printf函数以无符号整数的方式解释传递给它的参数
因此,当你看到%u打印出来的值时,你实际上看到的是a的补码表示被解释为一个无符号整数的结果
练习4
在这题之前 我们给一个小知识点
signed char的取值范围为 -128~127
我们看了图之后 这题就异常简单了
练习5
由于unsigned char的取值范围 使得i满足0<=i<=255 循环不会终止
死循环
因为unsigned 是无符号数 它的值大于等于0 会一直满足i>=0
当i变成-1时 unsigned int会让其变成一个很大的正数
所以这里依旧是死循环
练习6
在指针ptr2中 对数组名a强制类型转换为整形 整形+1
再对其转换为(int*)
意思就是ptr2刚开始是指向数组首元素 整形+1 使其向后挪动一个字节的位置
(int*) ptr2表示ptr2是int类型(4个字节)的指针 表示我们从当前位置访问四个字节
即变为00 00 00 02(在内存中的存储形式---小端) 所以输出应该为20000000