9.数据存储

文章目录

数据类型

基本的内置类型

char //字符数据类型
short //短整型
int //整形
long //长整型
long long //更长的整形
float //单精度浮点数
double //双精度浮点数
//C语言有没有字符串类型?

long long是C99标准才引入的,某些旧的编译器无法支持的
int在16位机器下是2个字节
c语言只规定了sizeof(long)>=sizeof(int)

#include <stdio.h>
int main()
{
    printf("%d\n", sizeof(int)); 4
 	printf("%d\n", sizeof(long)); 4
	return 0;
}

类型种类参考

布尔类型(C99引入)
_Bool 表示真假

需要引入对应头文件#include <stdbool.h>

//0表示假
//非0表示真
int main()
{
	int flag = 0;
	if (flag)
	{

	}
	return 0;
}
#include <stdbool.h>
int main()
{
	_Bool flag = true;
	if (flag)
	{
		printf("hehe\n");
	}
	return 0;
}

c语言中可以完全没有_Bool
转到定义可以发现

#define false 0
#define true 1

类型基本归类

整型家族

char
    unsigned char
    signed char
short
    unsigned short [int]
    signed short [int]
int
    unsigned int
    signed int
long
    unsigned long [int]
    signed long [int]
int a = 10;默认是signed int
unsigned int age = 20;
short ==> signed short (int)
long ==> signed long (int)
int可以省略不写
    
而char到底是 signed char 还是 unsigned char 取决于编译器
常见的编译器下:char ==> signed char

%d打印有符号数
%u打印无符号数

unsigned 要配合%u来使用

int num = 10;
printf("%d\n", num);
num = -10;
printf("%d\n", num);

unsigned int num = 10;
printf("%u\n", num);

num = -10;
printf("%u\n", num);//很大的一个数

浮点数家族

float
double

构造类型

也叫自定义类型

数组类型
结构体类型 struct
枚举类型 enum
联合类型 union

int a[10];类型为int [10]
int b[5]; 类型为int [5]

指针类型

int* pi;
char* pc;
float* pf;
void* pv;

空类型

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

整型在内存中的存储

数据在内存中以2进制的形式存储
内存中存的是2进制,但在调试器的内存窗口中是以16进制的形式进行展示,以便节省空间

最右边的是将内存中的数据解析成文本内容展示,参考意义不大

9.数据存储

整数:

整数二进制有3种表示形式:原码 反码 补码
正整数三码相同
原码:按照一个数的正负,直接写出来的二进制就是原码
反码:符号位不变,其他位按位取反
补码:反码+1

最高位是符号位,后面的是有效位
符号位为1表示负数,0表示正数

-10
10000000000000000000000000001010
11111111111111111111111111110101
11111111111111111111111111110110
ff ff ff f6
因此内存中展示的就是ff ff ff f6,但需要倒着存放
f6 ff ff ff

为什么存的是补码呢

cpu只能做加法 不能做减法
减法是由加法模拟,
乘法就是多个数相加

1-1 =1+(-1)
如果用原码去计算 算出来的是-2 结果有误
00000000000000000000000000000001
10000000000000000000000000000001
10000000000000000000000000000010
用补码计算
00000000000000000000000000000001
11111111111111111111111111111111
100000000000000000000000000000000只有32位,高位舍去
00000000000000000000000000000000
结果才正确
使用补码,可以将符号位和数值域统一处理
此外,"补码与原码相互转换",运算过程是相同的,
不需要额外的硬件电路
对补码再次取反加1,又能得到原来的原码
11111111111111111111111111111111
10000000000000000000000000000000
10000000000000000000000000000001

大小端

9.数据存储
为什么是倒着存的呢?

大端字节序:
把数据的低位字节序的内容存放在高地址处,高位字节序的内容存放在低地址处
小端字节序:
把数据的低位字节序的内容存放在低地址处,高位字节序的内容存放在高地址处

地址从左到右是由低到高的
0x11223344 44是低字节序(类比个位十位百位千位)
地址从左到右(从上到下)增大

0x11223344 放到内存中是44332211就是小端存储

9.数据存储

为什么出现大小端存储?

存在如何安排多个字节存储的问题,32位,64位
9.数据存储

练习

1.大小端

请简述大端字节序和小端字节序的概念,设计一个小程序来判断当前机器的字节序。

#include <stdio.h>
int check_sys()
{
int i = 1;
return (*(char *)&i);
}
int main()
{
int ret = check_sys();
if(ret == 1)
{
printf("小端\n");
}
else
{
printf("大端\n");
}
return 0;
}

&i访问的int* 要强制转换成char* 来访问第一个字节

char类型数据没有大小端问题
大小端是字节序,2个及以上字节才有
char类型数据只有1个字节

9.数据存储

2.截断

//输出什么?
#include <stdio.h>
int main()
{
   char a= -1;
   signed char b=-1;
   unsigned char c=-1;
   printf("a=%d,b=%d,c=%d",a,b,c);//-1 -1 255
   return 0;
}
char a = -1;
10000000000000000000000000000001   原码
11111111111111111111111111111110   反码
11111111111111111111111111111111   补码
补码存到a里面去,发生截断,只截断最低的8个bit位
a里面放的是 11111111
char a 与signed char b等价(VS编译器)
c里面放的也是11111111
-1是整数,a是char类型,会发生截断
以%d格式打印,a 是char类型,也就是signed char
要"发生整型提升",高位补符号位
11111111111111111111111111111111  提升之后的补码
打印打的是原码
10000000000000000000000000000000
10000000000000000000000000000001      -1
11111111
c是unsigned char
c也要发生整型提升,"无符号高位补符号位"
00000000000000000000000011111111   补码/反码/原码
正数3码相同
以%d格式打印,认为内存中存的是有符号数
2^8 - 1 = 255

3.%u打印负数

#include <stdio.h>
int main()
{
   char a = -128;
   printf("%u\n",a);//4294967168
   return 0;
}
-128
10000000000000000000000010000000   -128原码
11111111111111111111111101111111
11111111111111111111111110000000 
存到char a中会发生截断 10000000
整型提升后
11111111111111111111111110000000
以%u打印,认为内存中存的是无符号数
三码相同
转换为10进制:4294967168

4.%u打印正数

char a = 128;
以%u打印
00000000000000000000000010000000
截断后10000000放到a里面
整型提升
11111111111111111111111110000000
%u无符号数打印
转换为10进制:4294967168

5.正负相加

int i= -20;
unsigned  int  j = 10;
printf("%d\n", i+j);
//按照补码的形式进行运算,最后格式化成为有符号整数
(不用再去考虑把 int 转换为 unsigned int 再计算,内存中肯定是会转换的)
10000000000000000000000000010100
11111111111111111111111111101011
11111111111111111111111111101100  -20补码
    
00000000000000000000000000001010   10补码
    
11111111111111111111111111110110    加完之后的补码
11111111111111111111111111110101
10000000000000000000000000001010    -10  原码

9.数据存储

6.unsigned int i循环变量

#include <stdio.h>
#include<Windows.h>
int main()
{
	unsigned int i;
	for (i = 9; i >= 0; i--) 
	{
		printf("%u\n", i);
		Sleep(1000);//为了看得更清楚
	}
	return 0;
}

死循环了

9.数据存储

无符号整数i恒大于0的
一旦变成-1,而-1放到unsigned int里面
11111111111111111111111111111111
每一位都是有效位
再以%u打印
对应十进制4294967295
2 ^ 32 - 1

7.char取值范围

int main()
{
   char a[1000];
   int i;
   for(i=0; i<1000; i++)
  {
       a[i] = -1-i;
  }
   printf("%d",strlen(a));
   return 0;
}

9.数据存储

char数组最多储存 2 ^ 8 - 1个元素
signed char 1byte = 8bit
unsigned char 1byte = 8bit

内存中放的是补码
如果是无符号char取值范围是0 ~ 255
有符号的char -128 ~ 127
11111111  补码
11111110
10000001    -1(原码)

10000001   补码
10000000
11111111   -127(原码)
10000000直接解析成-128
110000000		-128
101111111
110000000
取完8位也还是10000000

9.数据存储

9.数据存储

又strlen遇到\0才停止故刚好转一圈停下来255个数
\0的ASCII值也就是0
9.数据存储

巧记:超出范围的数据如果是正数则减去256,如果是负数则加上256
128减去256 = -128

signed short 16 bit
00000000 00000000   0
00000000 00000001   
00000000 00000010    
01111111 11111111    32767
10000000 00000000   -32768
...
11111111 11111110    -2
11111111 11111111   -1

取值范围就是

#include<limits.h>
中有数据类型的取值范围
9.数据存储

8.无符号变量

#include <stdio.h>
unsigned char i = 0;
int main()
{
   for(i = 0;i<=255;i++)
  {
       printf("hello world\n");
  }
   return 0;
}

死循环
unsigned char取值范围0 ~ 255
i<=255恒成立

使用无符号变量要小心!!!一不注意就死循环了

浮点型在内存中的存储

常见浮点数

3.14159
1E10 科学计数法1.0 * 10^10
浮点数表示的范围:float.h中定义

9.数据存储

存储规则

根据国际标准IEEE(电气和电子工程协会) 754,任意一个二进制浮点数V可以表示成下面的形式:

(-1)^S * M * 2^E
(-1)^s表示符号位,当s=0,V为正数;当s=1,V为负数。
M表示有效数字,"大于等于1,小于2。""
2^E表示指数位。
十进制的5.0,写成二进制是 101.0 ,相当于 1.01×2^2 。
s=0,M=1.01,E=2。
十进制的-5.0,写成二进制是 -101.0 ,相当于 -1.01×2^2 。s=1,M=1.01,E=2。
十进制5.5   101.1     1.011 x 2 ^ 2(二进制)
-1 ^ 0 x 1.011 x 2 ^ 2
123.4 = 1.234 x 10 ^ 2
* 2 ^ 2 等价于小数点右移2位

float32bit位
最高位当符号位 1/0

9.数据存储

double 64个bit位

9.数据存储

1≤M<2 ,M可以写成 1.xxxxxx 的形式,
其中xxxxxx表示小数部分。
IEEE 754规定,在计算机内部保存M时,
默认这个数的第一位总是1,因此可以被"舍去",
只保存后面的xxxxxx部分。
比如保存1.01的时候,只保存 01,等到读取的时候,再把第一位的 1加上去。
这样做的目的,是节省1位有效数字,提高精度
以32位浮点数为例,留给M只有23位,将第一位的1舍去以后,等于可以保存24位有效数字。
至于指数E,情况就比较复杂。
首先,"E为一个无符号整数"(unsigned int)
这意味着,如果E为8位,它的取值范围为0~255;
如果E为11位,它的取值范围为0~2047。
但是科学计数法中的E是可以出现负数的,所以IEEE 754规定,存入内存时E的真实值必须"再加上一个中间数",
对于8位的E,这个中间数是127;
对于11位的E,这个中间数是1023。
2^10的E是10,所以保存成32位浮点数时,必须保存成10+127=137,对应二进制为10001001。

9.数据存储

从内存中取出指数E

1.E不全为0或不全为1

指数E的值减去127/1023得到真实值
再将有效数字M前加上第一位的1

2.E全为0

±1.xxx * 2^(-)127  无限接近于0的数
浮点数的指数E等于1-127(或者1-1023)即为真实值
有效数字M不再加上第一位的1,而是还原为0.xxxxxx的小数
表示±0,以及接近于0的很小的数字

3.E全为1

±1.xxxx * 2^(128)
表示±∞

例子

int main()
{
int n = 9;
float *pFloat = (float *)&n;
printf("n的值为:%d\n",n);//9
printf("*pFloat的值为:%f\n",*pFloat);//0.000000
*pFloat = 9.0;
printf("num的值为:%d\n",n);//1091567616
printf("*pFloat的值为:%f\n",*pFloat);//9.000000
return 0;
}

n的值为:9
*pFloat的值为:0.000000
num的值为:1091567616
*pFloat的值为:9.000000

以%f或%lf格式打印,默认小数点后有6位

整型的9放进去,%d打印出来还是9
整型的形式放进去,浮点型方式拿出来不一样
浮点数形式放进去,%d拿出来不一样
浮点数形式放进去,浮点数形式拿出来一样

9的三码相同
00000000000000000000000000001001
9放到n里面去
pFloat指针变量,4个字节
整型的9放进去,%d打印出来还是9
整型的形式放进去,浮点型方式拿出来
0  00000000  00000000000000000001001
S   E              M
E为全0 E的真实值1-127 = -126
2^(-126)
0.00000000000000000001001

(-1)^0 * 0.00000000000000000001001 * 2^(-126)
所以打印出来是0
*pFloat = 9.0;
printf("num的值为:%d\n",n);
1001.0  --二进制
1.001 * 2 ^ 3
9.0是正数  S=0
E=3  要存3+127进去 130 也就是 10000010
有效数字M等于001后面再加20个0,凑满23位
0 10000010 00100000000000000000000
S  E           M
0x 41 10 00 00在内存中也是小端存储
    00 00 10 41
%d打印出来,最高位为0  三码相同
01000001000100000000000000000000
1091567616
上一篇:4-11.1 C++ 整型的隐式类型转换


下一篇:sql prompt 安装使用教程