C语言:深度刨析数据在内存中的存储——整型数据

一、整型数据及其取值范围

在这里插入图片描述
对于有符号整型数据,有一点需要注意:C语言中用最高位位1,其余位全为0这种状态来表示某一类型中最小的负数。char类型为例,在内存中占8个比特位,则用1000 0000来表示该类型最小的负数-128,其余有符号整型原理相同。

二、整型数据在内存中的存储

(一)原码、反码与补码

计算机中的有符号数有原码、反码、补码三种表示方法,这三种表示方式均有符号位和数值位,符号位用0表示**“正数”1表示”负数“**。正数原码、反码、补码都与原码相同,负数按一定规律变化。

原码将十进制数转换成二进制数,负数最高位写1,正数最高位写0。
反码将原码符号位不变,其余位按位取反。
补码反码+1得到补码

所有的整型数据在计算机中都以补码的形式存储,因为补码使得符号位能与有效值部分一起参加运算,从而简化运算规则,使减法运算转换为加法运算。

(二)大小端问题

1.什么是大小端
大端(存储)模式,是指数据的低权值位保存在内存的高地址中,而数据的高位,保存在内存低地址中;大小大
小端(存储)模式,是指数据的低权值位保存在内存的低地址中,而数据的高位,保存在内存高地址中;小小小

2.为什么要有大端和小端
计算机系统中,以字节为单位存储,最小的char类型占一个字节,即八个比特位,其余数据类型均大于一个字节,如int类型占四个字节,对于位数大于8位的处理器,例如16位或者32位的处理器,由于寄存器宽度大于一个字节,那么必然存在着一个如何多个字节安排的问题。因此就导致了大端存储模式和小端存储模式。

3.判断大小端程序

#include<stdio.h>
#include<windows.h>
int main()
{
	union
	{
		int a;
		char c;
	}un;
	un.a = 1;
	if (un.c){
		printf("小端\n");
	}
	else{
		printf("大端\n");
	}
	system("pause");
	return 0;
}

在这里插入图片描述
在VS环境中,存储情况如图,int类型在内存中占4个字节,给int型变量a赋值1,在内存中这个1存储在低地址位,说明VS环境是小端存储。

(三)整型提升、截断

整型提升:C的整型算术运算总是至少以缺省整型类型的精度来进行的。为了获得这个精度,表达式中的字符和短整型操作数在使用之前被转换为普通整型,这种转换称为整型提升。(发生在读取数据的时候)
整型提升的规则:
有符号的:整型提升时是按照变量的补码被截断时的最高位是什么进行补位的,如果截断后最高位即最左面的一位数为 1 则在最高位前补 1 ,如果最高位是 0 则在前面补 0 ,补够32位即int类型可。
无符号的:直接在被截断的前面补 0 即可。
截断:在c语言中进行变量赋值的时候,如果将字节多的数据类型赋给一个占字节少的变量类型,会发生“截断”。(发生在存入数据的时候)
eg1:
在这里插入图片描述
如图,'c’实际为整型,大小为4个字节,而字符型的大小为1个字节,在把’c’赋给char类型变量c的过程中,发生了截断。
eg2:
在这里插入图片描述
在这个程序中,将-129赋值给了char类型的变量c,超出了c的最小值,所以发生了截断,
-129的补码:
在这里插入图片描述
由于c只有8个比特位的空间,将-129存入c的空间会发生截断,前面的24个1被舍弃,只保留后8位。打印时从内存读取0111 1111序列,发生整型提升,变量c本身的类型是char类型,不足的位补符号位0,提升后的序列位:
在这里插入图片描述
输出的值为127。

三、练习题

求下面代码输出结果
1.

#include<stdio.h>
#include<windows.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);
	system("pause");
	return 0;
}

a和b输出不变,仍为-1,c变量读取时发生整型提升,高位补0,按正数打印,结果为255。

#include<stdio.h>
#include<windows.h>
int main()
{
    
    
	char a = 128;
	printf("%u\n", a);
	system("pause");
	return 0;
}

结果:
在这里插入图片描述
128的补码:
在这里插入图片描述
在char类型中存储时发生截断,存储1000 0000,以%u形式打印时,发生整型提升,序列为:
在这里插入图片描述
将十进制转换为二进制可得到:
在这里插入图片描述
所以输出结果为4294967168。
3.

#include<stdio.h>
#include<windows.h>
int main()
{
    
    
	char a = -128;
	printf("%u\n", a);
	system("pause");
	return 0;
}

-128的补码为:
在这里插入图片描述
存入char类型变量a中发生截断,存入:1000 0000,以%u形式打印输出时发生整型提升,其二进制序列为:
在这里插入图片描述
与上一题相同,输出结果为:4294967168。
4.

#include<stdio.h>
#include<windows.h>

int main()
{
    
    
	int i = -20;
	unsigned int j = 10;
	printf("%d\n",i+j);
	
	system("pause");
	return 0;
}

-20补码:
在这里插入图片描述
10补码:
在这里插入图片描述
-20与10相加,发生整型提升成为unsigned int型,二进制序列为:
在这里插入图片描述
通过%d形式输出,原码为:
在这里插入图片描述
所以输出结果为-10。
5.

#include<stdio.h>
#include<windows.h>
int main()
{
    
    
	unsigned int i;
	for (i = 9; i >= 0; i--)
	{
    
    
		Sleep(100);
		printf("%u\n", i);
	}
	system("pause");
	return 0;
}

运行结果:
在这里插入图片描述

变量i为unsigned int类型,当i–值减小到-1,其二进制序列为:
在这里插入图片描述
与0进行比较时发生整型提升,提升为unsigned int型,111111111 11111111 11111111 11111111的值为最大的无符号整型,而每次减小到-1时都会发生这一情况,所以程序形成死循环。
6.

#include<stdio.h>
#include<windows.h>
int main()
{
    
    
	char a[1000];
	int i;
	for (i = 0; i < 1000; i++)
	{
    
    
		a[i] = -1 - i;
	}
	printf("%d\n", strlen(a));
	system("pause");
	return 0;
}

输出结果:255
如图所示,a[i]的值从-1变化到0,绕圆逆时针转一圈:
在这里插入图片描述

a[i]的值从i=0时的-1,减小到i=127时的-128,a[i]继续减小下去,值为-129,溢出,发生截断
-129在内存中的存储:

截断后在内存中存储01111111,值为127,此时为128,i继续增大,此时随着i增大,a[i]的值会减小,从127减小到0,当遇到0时strlen函数会返回,所以输出255。
7.

#include<stdio.h>
#include<windows.h>
unsigned char i = 0;
int main()
{
    
    
	for (i = 0; i <= 255; i++)
	{
    
    
		Sleep(30);
		printf("hello word%d\n",i);
	}
	system("pause");
	return 0;
}

同第5题,i为unsigned char类型变量,当i的值减小到-1时,发生截断存入i的序列为1111 1111。所以程序形成死循环,每当i的值减小到-1时都会变成255。

Guess you like

Origin blog.csdn.net/qq_44631587/article/details/120556085