您现在的位置是:首页 >技术杂谈 >学C的第二十二天【深度剖析数据在内存中的存储:1. 数据类型介绍;2. 整型在内存中的存储】网站首页技术杂谈

学C的第二十二天【深度剖析数据在内存中的存储:1. 数据类型介绍;2. 整型在内存中的存储】

高高的胖子 2024-07-01 11:59:10
简介学C的第二十二天【深度剖析数据在内存中的存储:1. 数据类型介绍;2. 整型在内存中的存储】

=========================================================================

相关代码gitee自取:C语言学习日记: 加油努力 (gitee.com)

=========================================================================

接上期:学C的第二十一天【初阶测评讲解:1. 计算递归了几次;2. 判断 do while 循环执行了几次;3. 求输入的两个数的最小公倍数;4. 将一句话的单词进行倒置,标点不倒置;补充知识点】_高高的胖子的博客-CSDN博客

=========================================================================

               

1. 数据类型介绍

(1). 基本的内置类型(C语言自带类型):

           

char        --        字符数据类型        --        1字节

            

short        --        短整型        --        2字节

            

int        --        型        --        4字节

            

long        --        长整型        --        4字节 或 8字节

sizeof(long)        >=        sizeof(int)

            

long long        --        更长的整型        --        8字节

            

float        --        单精度浮点数        --        4字节

            

double        --        双精度浮点数        --        8字节

           

                       

类型的意义:

           

1. 使用这个类型开辟内存空间的大小(大小决定了使用范围

                   

2. C语言规定了:sizeof(long) >= sizeof(int),所以long的大小可以是4字节8字节

              

3. 把整型分为短整型、整型和长整型的原因:有些整型数据可能比较小,使用short就够了,如:年龄。short的范围:-32768 ~ 32767

                        

4. 对于整型类型,还分为 有符号(signed) 无符号(unsigned)

               

               

(2). 类型的基本归类:

              

整型类型:(只有整型分有符号和无符号)

char

unsigned char

signed char

(字符存储时,存储的是ASCII码值,是整型,所以归类时把char放在整型类型中)

(直接写成 charsigned char 还是 unsigned char不确定的,取决于编译器)

         

short

unsigned short [int]

signed short [int]        ==        short [int]

(    [int]通常会省略掉    )

         

int

unsigned int

signed int        ==        int

             

long 

unsigned long [int]

signed long [int]        ==        long [int]

(    [int]通常会省略掉    )

          

long long 

unsigned long long [int]

signed long long [int]        ==        long long [int]

(    [int]通常会省略掉    )

                  

signed:二进制位的最高位符号位其它位都是 数值位

unsigned:二进制位的最高位也是 数值位,即所有位都是 数值位

             

例如(其它整型类型以此类推):

             

signed char 范围是:-128~127补码:10000000无法转换为原码,被直接当成-128

               

unsigned char 范围是:0~255,8位全是数值位,无负数

            

               

浮点数类型:

float

            

double

          

long double

           

构造类型(自定义类型):

            

数组类型:

           

数组的元素个数数组的类型 发生变化时,数组类型就不一样了:)

int arr1[10];        类型是        int [10]

int arr2[5];        类型是        int [5]

char arr3[5];        类型是        char [5]

(这是三个不同的数组类型)

          

           

结构体类型 struct

       

枚举类型 enum

      

联合类型 union

           

               

指针类型:

int* pi;

           

char* pc;

             

float* pf;

           

void* pv;

               

             

空类型:

void 表示 空类型无类型):通常应用于函数的返回类型函数的参数指针类型

                          


                             

2. 整型在内存中的存储

变量的创建时要在内存中开辟空间的,空间的大小根据不同的类型而决定

             

而开辟空间后,数据在所开辟内存中是如何存储的呢?

               

                

(1). 整数用二进制表示的三种表示形式:原码、反码、补码

             

                 

原码:

正数:直接数值按照正负数的形式翻译二进制得到原码

              

负数:直接数值按照正负数的形式翻译二进制得到原码

或者

反码按位取反得到原码

再或者

补码按位取反再+1得到原码

             

反码:

正数:原码、反码、补码 都相同

          

负数:原码符号位不变,将其它位依次按位取反得到反码

或者

补码-1得到反码

          

补码:

正数:原码、反码、补码 都相同

负数:反码+1得到补码

             

              

(2). 符号位 和 数值位(整数)

             

上面三种表示形式都有 符号位 和 数值位 两部分:

               

符号位:

二进制最高位的一位叫做符号位

符号位 用 0 表示 “”;

符号位 用 1 表示 “”。 

           

数值位:

除了符号位,其它位都是数值位

对于正数原码反码、补码 相同

对于负数三种表示方法各不相同(参考上面)

          

(演示代码:)

#include <stdio.h>
int main() 
{
	int num = 10;//创建一个叫num的整型变量,这时num向内存申请4个字节来存放数据
	// 4个字节 - 32比特位
	//00000000000000000000000000001010 -- 原码
	//00000000000000000000000000001010 -- 反码
	//00000000000000000000000000001010 -- 补码

	int num2 = -10;
	//10000000000000000000000000001010 -- 原码
	//11111111111111111111111111110101 -- 反码
	//11111111111111111111111111110110 -- 补码



	return 0;
}

             

                  

对于整型来说:数据存放在内存中其实存放的是补码

           

计算机系统中,数值一律用 补码表示存储

原因在于:使用补码,可以符号位数值位统一处理把符号位也看成数值位来计算

同时,加法减法也可以统一处理CPU只有加法器),

此为,补码原码相互转换,其运算过程是相同的

原码转换为补码按位取反再+1补码转换为原码也可以按位取反再+1),

不需要额外的硬件电路

               

只有加法器计算减法时1 - 1 --> 1 + (-1)

假设计算用的是原码,算出来的是 -2,是错误

而用补码进行计算后,再用原码表示,结果则是

        

(为什么会倒着存储呢?)

             

               

(3). 大小端介绍

                  

字节序:

字节单位讨论存储顺序大端字节序存储 / 小端字节序存储

                  

低位 / 高位:

十进制:数字123,1是百位,2是十位,3是个位。这里的1就是高位,3就是低位。

十六进制0x 11 22 33 44,这里 11 就是高位44就是低位

                  

大端字节序存储:

大端(存储)模式:指数据的低位字节内容保存在内存的高地址中,而数据的高位字节内容,保存在内存的低地址中;

低位高地址高位低地址

            

小端字节序存储:

小段(存储)模式:指数据的低位字节内容保存在内存的低地址中,而数据的高位字节内容,保存在内存的高地址中;

低位低地址高位高地址

            

为什么有大端和小段:

一个数据只要超过一个字节,在内存中存储的时候就必然涉及到顺序的问题,所以要有大端小端存储模式对该数据进行排序

                

为什么会有大小端模式之分,是因为在计算机系统中,我们是以字节为单位的,每个地址单元对应一个字节一个字节8bit 。但是在C语言中除了 8bitchar 之外,还有 16bit short 类型,32位 long 类型(具体要看编译器),另外,对于位数大于8位的处理器,例如 16位 或者 32位处理器,由于寄存器宽度大于一个字节,那么就存在着一个如何将多个字节安排的问题。因此就导致了大端存储模式小端存储模式 。

              

例如:一个 16bit short 类型 x ,在内存中的地址0x0010x 的值0x1122,那么 0x11 高字节0x22低字节。对于大端模式,就将 0x11 放在低地址中,即地址 0x0010 中, 0x22 放在高地址中,即地址 0x0011 中。小端模式相反

          

我们常用的 x86 结构小端模式,所以上面的图数据会“倒着放”低位字节放在了低地址高位字节放在了高地址。而 KEIL C51 则为大端模式。很多的ARM,DSP都为小端模式。有些ARM处理器还可以由硬件来选择为大端模式还是小端模式。

               

             

(4). 写个程序判断大小端:

               

思路:

变量a存放在内存中的十六进制数为:01 00 00 00(小端存储),地址第一位是1

如果是大端存储:则应该是:00 00 00 01,地址第一位是0

可以把 a 的地址取出第一位,如果

第一位地址 == 1,说明是小端存储

第一位地址 == 0,说明是大端存储

         

取出 int类型a 的 地址 第一位方法:

*(char*)&a

int* 强制转换为 char*,再解引用,即可取出一位地址的内容

             

实现代码:

#include <stdio.h>
int check_sys()
{
	int a = 1;
	//要大于一个字节的数据,才有顺序可言

	判断大小端
	//if (*(char*)&a == 1)
	//	//把int*强制转换a的地址为char*再解引用,判断地址第一位的内容
	//{
	//	return 1;
	//}
	//else
	//{
	//	return 0;
	//}

	//可以直接写成
	return *(char*)&a;
}

int main() 
{
	int ret = check_sys();
	if (ret == 1)
	{
		printf("小端
");
	}
	else
	{
		printf("大端
");
	}

	return 0;
}

            

                  

(5). 练习:(重点在注释)

             

1. 区别 unsigned 和 signed :

//练习:1.区别 unsigned 和 signed
#include <stdio.h>
int main() 
{
	char a = -1; //char 在 VS中 默认是 signed char
	//10000000000000000000000000000001 -- 原码
	//11111111111111111111111111111110 -- 反码
	//11111111111111111111111111111111 -- 补码
	//	-1是整数,存进char类型中会发生截断
	//				11111111	--	  补码,截断获取最低8位
	// 最高位 是 符号位

	signed char b = -1;
	//和 char a 相同

	unsigned char c = -1;
	//				11111111	--	  补码,截断获取最低8位
	// 最高位 是 数值位

	printf("a=%d,b=%d,c=%d", a, b, c);
	//%d - 十进制形式打印有符号整型数据,
	//这里会发生 整型提升
	//	11111111	--	  补码,截断获取最低8位
	// 
	//	整型提升,有符号位按符号位补满,补满后:
	//11111111111111111111111111111111 -- 整型提升后补码
	//11111111111111111111111111111110 -- 反码
	//10000000000000000000000000000001 -- 原码
	//	
	//	整型提升,无符号位高位补0,补满后:
	//00000000000000000000000011111111 -- 整型提升后补码
	//		整数原码、反码、补码相同

	return 0;
}

                

            

2. 使用 %u 打印 有符号整型:

            

(补充:%u -- 十进制形式打印无符号整型

               

(1). 打印 -128

//练习:2.使用 %u 打印 有符号整型:
#include <stdio.h>
int main() 
{
	char a = -128;
	//10000000000000000000000010000000 -- 原码
	//11111111111111111111111101111111 -- 反码
	//11111111111111111111111110000000 -- 补码
	//				截断后:
	//			10000000	-- 截断后补码

	printf("%u
", a);
	// %u -- 十进制形式打印无符号的整型
	// 对 char 变量 打印 整型数字,进行整型提升
	// char类型 有符号位,按符号位补满:
	//11111111111111111111111110000000 -- 补满后的补码
	//因为是以 无符号整数 打印,所以 原码、反码、补码 相同
	// 那么这个数就很大了

	return 0;
}

                       

(2). 打印 128

//练习:3.使用 %u 打印 有符号整型:
#include <stdio.h>
int main()
{
	// -128 改成 128
	char a = 128;
	//00000000000000000000000010000000 -- 原码
	//11111111111111111111111101111111 -- 反码
	//11111111111111111111111110000000 -- 补码
	//				截断后:
	//			10000000	-- 截断后补码
	//	跟-128是一样的,
	//只是原码的符号位不一样,但截断后都是10000000

	printf("%u
", a);
	// %u -- 十进制形式打印无符号的整型
	// 对 char 变量 打印 整型数字,进行整型提升
	// char类型 有符号位,按符号位补满:
	//11111111111111111111111110000000 -- 补满后的补码
	//因为是以 无符号整数 打印,所以 原码、反码、补码 相同
	// 那么这个数就很大了

	return 0;
}

                

               

3. 用%d打印:有符号整型 + 无符号整型

//练习:3. 用%d打印:有符号整型 + 无符号整型
#include <stdio.h>
int main() 
{
	//有符号整型:
	int i = -20;
	//10000000000000000000000000010100 -- 原码
	//11111111111111111111111111101011 -- 反码
	//11111111111111111111111111101100 -- 补码

	//无符号整型:
	unsigned int j = 10;
	//00000000000000000000000000001010 -- 原码
	//			原码、反码、补码 相同

	printf("%d
", i + j);
	// i的补码 和 j的补码 相加
	//11111111111111111111111111101100 -- i的补码
	//				+
	//00000000000000000000000000001010 -- j的补码
	//				=
	//11111111111111111111111111110110 -- 两补码相加后的补码
	//该补码再通过%d打印有符号数,最高位是符号位,知道补码,要计算得到原码
	//11111111111111111111111111110101 -- 反码
	//		反码 符号位 不变,其它位 按位取反
	//10000000000000000000000000001010 -- 原码 -》 -10

	return 0;
}

                

               

4. 使用无符号整数(无负数)进行自减循环

//使用无符号整数(无负数)进行自减循环
#include <stdio.h>
#include <windows.h>

int main() 
{
	unsigned int i;
	for (i = 9; i >= 0; i--)
	{
		printf("%u
", i);
		Sleep(1000);
		//单位是毫秒,休眠1秒再继续下个语句
	}

	return 0;
}

               

                  

5. 字符数组存储整型数字:

//字符数组存储整型数字:
#include <stdio.h>
#include <string.h>
int main() 
{
	char a[1000];
	//char类型数组,整型数字只能存储0~-128

	int i;
	for (i = 0; i < 1000; i++)
	{
		a[i] = -1 - i;//-1,-2......
	}
	//-1,-2,-3...-128,127,126,...3,2,1,0 -- 存一轮:256个元素
	//-1,-2,-3...-128,127,126,...3,2,1,0 -- 存一轮:256个元素
	//。。。。。

	printf("%d", strlen(a));
	//strlen 是求字符串长度的,
	//统计的是  之前出现的字符的个数
	//  的ASCII码值是 0,找到0就停止计算

	return 0;
}

              

               

6. unsigned char 取值范围:

//unsigned char 取值范围:
#include <stdio.h>

unsigned char i = 0;//全局变量
//unsigned char 的整型取值范围是:0~255

int main() 
{
	// 255 再 +1 又变成 0
	for (i = 0; i <= 255; i++)
	{
		printf("hello world
");
	}
	return 0;
}

风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。