当前位置:   article > 正文

C语言——深度剖析数据在内存中的存储——第1篇——(第24篇)

C语言——深度剖析数据在内存中的存储——第1篇——(第24篇)

坚持就是胜利


一、数据类型详细介绍

前面我们学习了基本的内置类型,以及它们所占存储空间的大小。

char        //字符数据类型  1 字节
short       //短整型        2 字节 
int         //整型          4 字节
long        //长整型        4/8 字节  sizeof(long) >= sizeof(int)
long long   //更长的整型    8 字节
float       //单精度浮点数  4 字节
double      //双精度浮点数  8 字节
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

1、什么是 内置类型?

答:C语言本身自带的类型

2、为什么整型分为 short int long?

答:比如存储年龄 age,用 int 太大了,用 short 就可以。short 取值范围:-32768 ~ 32767

#include <limits.h>   //注意头文件
                      //在此头文件下,可以查询内置类型的最大值,最小值

int main()
{
	INT_MAX;   //转到定义

	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

在这里插入图片描述

类型的意义:

1、使用这个类型开辟内存空间的大小(大小决定了使用范围)。
2、如何看待内存空间的视觉。

1、类型的基本归类

有符号数、无符号数,只针对 整型。
浮点数 没有 有符号数和无符号数 的说法和区分。

(1)整型家族

//字符 char 在存储的时候,存储的是 ASCII值,是整型所以归类的时候,放在整型家族。
char         
	unsigned char 
	signed char
short
	unsigned short
	signed short
int 
	unsigned int
	signed int
long 
	unsigned long [int]
	signed long [int]
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
对于整型家族的类型来说,有:“有符号”和“无符号”的区分

1char 到底是 signed char 还是 unsigned char 不确定
  (C语言没有给出明确的规定)
  (char 在 VS 上是 signed char2short == signed short
   unsigned short
3int  ==  signed int
   unsigned int
4long  ==  signed long
   unsigned long
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

在这里插入图片描述
在这里插入图片描述

(2)浮点数家族

float
double
  • 1
  • 2

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

1、数组类型
2、结构体类型 struct
3、枚举类型 enum
4、联合类型 union

//int arr1[10] ——> int [10]
//int arr2[20] ——> int [20]
//char arr3[10] ——> char [10]
//数组 arr1 和 arr2 和 arr3 类型各不相同
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

(4)指针类型

1int* pi;
2char* pc;
3float* pf;
4void* pv;
5、结构体指针类型;
  • 1
  • 2
  • 3
  • 4
  • 5

(5)空类型

void 表示 空类型(无类型)
通常应用于 函数的返回类型、函数的参数、指针类型。
//void* ps;
  • 1
  • 2
  • 3

二、整型在内存中的存储解析

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

1、原码、反码、补码

计算机中的整数有 3 种 2进制 表示方法,即原码、反码、补码。
三种表示方法均有 符号位 和 数值位 两部分,符号位都是用 0 表示 “正”,用 1 表示 “负”,
而数值位:
正整数的原码、反码、补码都相同。
负整数的三种表示方法各不相同。

原码
直接将数值按照正负数的形式翻译成二进制就可以得到原码

反码
将原码的符号位不变,其他位依次按位取反就可以得到反码

补码
反码 + 1 就得到补码
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

对于整型来说:数据存放内存中,其实存放的是补码。
为什么呢?

在计算机系统中,数值一律用补码来表示和存储。
原因在于,使用补码,可以将符号位和数值域统一处理;
同时,加法和减法也可以统一处理(CPU只有加法器),
此外,补码与原码相互转换,其运算过程是相同的,不需要额外的硬件电路。
  • 1
  • 2
  • 3
  • 4

在这里插入图片描述

为什么用 补码 计算,而不用 原码 计算?

答:用 原码 计算,有些运算会出错

计算 1 - 1  (由于CPU只有加法器,所以 1 - 1 就是 1 +  (-1)1+(-1)

原码 计算 
00000000 00000000 00000000 00000001  数值 1
10000000 00000000 00000000 00000001  数值 -1
10000000 00000000 00000000 00000010  相加等于 -2 ,计算错误

补码计算:将 符号位 和 数值域 统一处理
00000000 00000000 00000000 00000001  数值1原码
00000000 00000000 00000000 00000001  数值1反码
00000000 00000000 00000000 00000001  数值1补码

10000000 00000000 00000000 00000001  数值-1原码
11111111 11111111 11111111 11111110  数值-1反码
11111111 11111111 11111111 11111111  数值-1补码

  00000000 00000000 00000000 00000001  数值1补码  
  11111111 11111111 11111111 11111111  数值-1补码
1 00000000 00000000 00000000 00000000  只能存3200000000 00000000 00000000 00000000  结果为 0 ,正确
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

2、大小端字节序介绍

什么是 字节序?
答:以 字节 为单位,讨论存储顺序的。

(1)什么是大端小端?

大端(存储)模式,是指数据的低位保存在内存的高地址中,而数据的高位,保存在内存的低地址中。
小端(存储)模式,是指数据的低位保存在内存的低地址中,而数据的高位,保存在内存的高地址中。

(2)为什么有大端和小端?

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

例如:一个 16bit 的 short型 x,在内存中的地址为 0x0010,x 的值为 0x1122,那么 0x11 为高字节,0x22 为低字节。对于大端模式,就将0x11 放在低地址中,即 0x0010 中,0x22 放在高地址中,即 0x0011 中。小端模式,刚好相反。
我们常用的 x86 结构 是 小端模式,而 KEIL C51 则为大端模式。很多的 ARM,DSP 都为 小端模式。有些 ARM 处理器 还可以由 硬件 来选择是大端模式还是小端模式。

在这里插入图片描述

(3)设计一个小程序来判断当前机器的字节序

在这里插入图片描述

由于 int a;
所以 &a 是 int* 类型
需要判断 int a 的 第1个字节
将 int* 转换为 char* 即可!
再 解引用* 
  • 1
  • 2
  • 3
  • 4
  • 5
#include <stdio.h>

int main()
{
	int a = 1;

	//char* p = &a;   //这么写是错误的,因为 &a 是 int* 类型

	char* p = (char*)&a;

	if (*p == 1)
	{
		printf("小端存储\n");
	}
	else
	{
		printf("大端存储\n");
	}
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

在这里插入图片描述

#include <stdio.h>

int main()
{
	int a = 1;
	if (*(char*)&a == 1)
	{
		printf("小端存储\n");
	}
	else
	{
		printf("大端存储\n");
	}
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
#include <stdio.h>

int check_sys()
{
	int a = 1;
	if (*(char*)&a == 1)  //再精简为:if(*(char*)&a)
	{
		return 1;
	}
	else
	{
		return 0;
	}
}

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

	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
#include <stdio.h>

int check_sys()
{
	int a = 1;  

	return *(char*)&a;  //一步步精简成这样
	
}

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

	return 0;
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

3、练习

(1)

对于整型家族的类型来说,有:“有符号”和“无符号”的区分

1char 到底是 signed char 还是 unsigned char 不确定
  (C语言没有给出明确的规定)
  (char 在 VS 上是 signed char2short == signed short
   unsigned short
3int  ==  signed int
   unsigned int
4long  ==  signed long
   unsigned long
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

在这里插入图片描述
在这里插入图片描述

#include <stdio.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);  // a=-1 b=-1 c=255 
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

//知识点(之前学习过)
//1、长度大于 int 的,就不需要进行 ”整型提升“
//2、有符号的整型提升:高位补充符号位
//3、无符号的整型提升,高位补 0
//4、%d 十进制的形式打印有符号整型整数

//正确的解答过程
//整数 -1 存入 char a 中
//整数 -1 原码:10000000 00000000 00000000 00000001
//        反码:11111111 11111111 11111111 11111110
//        补码:11111111 11111111 11111111 11111111

//截断,因为只有 1 字节 才能存入 char a 中:11111111

//%d 十进制的形式打印有符号整型整数
//对char a 进行 整型提升
//补码:11111111 11111111 11111111 11111111
//反码:10000000 00000000 00000000 00000000
//补码:10000000 00000000 00000000 00000001
//结果显示:-1


//signed char b 和 char a 是一样的解答过程
//在本电脑中的VS编译器,char 类型是 signed char 

//unsigned char c
//整数 -1 存入 unsigned char c 中
//整数 -1 原码:10000000 00000000 00000000 00000001
//        反码:11111111 11111111 11111111 11111110
//        补码:11111111 11111111 11111111 11111111

//截断,因为只有 1 字节 才能存入 unsigned char c 中:11111111

//无符号整型提升,高位补 0
//补码:00000000 00000000 00000000 11111111
//%d 十进制的形式打印有符号整型整数
//因为此时已经按照 %d 的形式输出,最高位为 0,是 正数
// 正数的原码、反码、补码相同
//原码:00000000 00000000 00000000 11111111
//结果:255

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41

(2)

#include <stdio.h>

int main()
{
	char a = -128;
	printf("%u\n", a);
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

在这里插入图片描述
在这里插入图片描述

//知识点:
//%u 十进制的形式打印无符号的整型整数

//整数-128,存入 char a 中
//-128补码:10000000 00000000 00000000 10000000
//    反码:11111111 11111111 11111111 01111111
//    补码:11111111 11111111 11111111 10000000

//截断:10000000  存入 char a 中

//%u 十进制的形式打印无符号的整型整数
//整型提升,也就是对 char a 进行整型提升
//char a 是有符号的,符号位是 1
//补码:11111111 11111111 11111111 10000000  
//%u 是无符号数,内存中就没有符号位,内存中存入的就是:11111111 11111111 11111111 10000000
//无符号数可以看成 正数,补码,反码,原码都是一样的
//结果就是:11111111 11111111 11111111 10000000
//十进制就是:4294967168
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

(3)

#include <stdio.h>

int main()
{
	char a = 128;
	printf("%u\n", a);
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

在这里插入图片描述

//知识点:
//%u 十进制的形式打印无符号的整型整数

//整数128,存入 char a 中
//128补码:00000000 00000000 00000000 10000000
//正数的原码、反码、补码相同

//截断:10000000  存入 char a 中

//%u 十进制的形式打印无符号的整型整数
//整型提升,也就是对 char a 进行整型提升,char a 是有符号的,符号位为 1
//char a 是有符号的,符号位是 1
//补码:11111111 11111111 11111111 10000000  
//%u 是无符号数,内存中就没有符号位,内存中存入的就是:11111111 11111111 11111111 10000000
//无符号数可以看成 正数,补码,反码,原码都是一样的
//结果就是:11111111 11111111 11111111 10000000
//十进制就是:4294967168
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

(4)

在这里插入图片描述

#include <stdio.h>

int main()
{
	int i = -20;
	unsigned int j = 10;
	printf("%d\n", i + j);
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

在这里插入图片描述

//知识点:
//1、补码得到原码有两种方式:
//                          (1)补码-1得到反码,反码取反得到原码
//                          (2)补码先取反再加1,得到原码

//整数-20原码:10000000 00000000 00000000 00010100
//       反码:11111111 11111111 11111111 11101011
//       补码:11111111 11111111 11111111 11101100

//unsigned int j = 10原码:00000000 00000000 00000000 00001010
//无符号数的反码、原码、补码相同
//                   补码:00000000 00000000 00000000 00001010

//i+j  补码相加 11111111 11111111 11111111 11101100
//              00000000 00000000 00000000 00001010
//              11111111 11111111 11111111 11110110 
//截断:11111111 11111111 11111111 11110110 
// (1)补码-1得到反码,反码取反得到原码
//补码:11111111 11111111 11111111 11110110  
//反码:11111111 11111111 11111111 11110101
//原码:10000000 00000000 00000000 00001010
//结果:-10

//(2)补码先取反再加1,得到原码
//补码:11111111 11111111 11111111 11110110 
//取反:10000000 00000000 00000000 00001001
//+1  :10000000 00000000 00000000 00001010
//结果:-10
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

(5)

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

int main()
{
	unsigned int i;
	for (i = 9; i >= 0; i--)
	{
		printf("%u\n", i);
		
		Sleep(1000);  //单位:毫秒   5000毫秒
	}
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

在这里插入图片描述
死循环,因为 unsigned int i 是 恒大于等于零 的。
在这里插入图片描述

//9 8 7 6 5 4 3 2 1 0 的原码、反码、补码 相同
//0 原码:00000000 00000000 00000000 00000000

//-1 原码:10000000 00000000 00000000 00000001
//   反码:11111111 11111111 11111111 11111110
//   补码:11111111 11111111 11111111 11111111

//0-1  0+(-1)
//0 补码:00000000 00000000 00000000 00000000
//-1补码:11111111 11111111 11111111 11111111
//相加  :11111111 11111111 11111111 11111111

//以 %u 输出:4294967295
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

在这里插入图片描述

(6)比较难,好好理解

#include <stdio.h>
#include <string.h>

int main()
{
	char a[1000];
	int i;
	for (i = 0; i < 1000; i++)
	{
		a[i] = -1 - i;
		printf("%d\n", a[i]);
	}
	printf("%d\n", strlen(a));  //答案是:255

	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

结果:255
原因:strlen() 统计的是 ‘\0’,之前出现的数字的个数!!!
在这里插入图片描述
在这里插入图片描述

(7)好题

#include <stdio.h>

unsigned char i = 0;   //unsigned char 的取值范围:0 ~ 255

int main()
{
	for (i = 0; i <= 255; i++)      //此时:i<=255 条件恒成立,所以死循环
	{
		printf("hell0 world\n");    //死循环
	}
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

微软雅黑字体
黑体
3号字
4号字
红色
绿色
蓝色

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Cpp五条/article/detail/176383
推荐阅读
相关标签
  

闽ICP备14008679号