首页 > 其他分享 >数据的存储(C语言进阶)

数据的存储(C语言进阶)

时间:2023-01-07 17:03:17浏览次数:53  
标签:存储 进阶 符号 int 补码 C语言 char 整型 原码

  • 数据类型介绍
  • 内置数据类型的归类
  • 整型在内存中的存储:①原码、反码、补码 ②大小端字节序 ③char的存储内容
  • 浮点型在内存中的存储

自学b站“鹏哥C语言”笔记。

一、数据类型介绍

1.C语言类型

  • 内置类型
  • 构造类型(自定义类型)

2.内置类型

char

字符数据类型

1字节

short

短整型

2字节

int

整型

4字节

long

长整型

4/8字节

long long

更长的整型

8字节

float

单精度浮点数

4字节

double

双精度浮点数

8字节

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

详见文章【数组】

数组类型

struct

结构体类型

enum

枚举类型

union

联合类型

4.类型的意义

  • 类型决定了开辟内存空间的大小
  • 类型决定了看待内存空间的视角(不同存储的内容不同)

二、内置数据类型的归类

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)

unsigned:无符号

signed:有符号,首位是符号位,“正0负1”

无符号比有符号存储的范围大。

2.浮点型

  • float
  • double

3.指针类型

  • int*
  • char*
  • float*
  • void*

4.空类型

  • void


通常运用于函数的返回类型(无返回)、函数的参数(无返回)、指针类型。

三、整型在内存中的存储

1.原码、反码、补码

整型有三种表示方式:原码、反码、补码。

(1)有符号整型

三种表示方式均由符号位数值位组成。符号位是最高位。

正数

原码:符号位“正0负1”,数值位直接将数值转换为二进制

反码:和原码完全相同

补码:和原码完全相同

int main()
{
int a = 20;//4个字节 - 32bit
//原码:00000000000000000000000000010100
//反码:00000000000000000000000000010100
//补码:00000000000000000000000000010100
//十六进制表示(每四位换成一位):0x00000014
return 0;
}
负数

原码:符号位“正0负1”,数值位直接将数值转换为二进制

反码:符号位“正0负1”,其他位是原码按位取反

补码:符号位“正0负1”,其他位是反码+1

原码 → 符号位不变,其它位取反 → 反码 → +1 → 补码

补码 → -1 → 反码 → 符号位不变,其它位取反 → 原码

int main()
{
int b = -10;//4个字节 - 32bit
//原码:10000000000000000000000000001010
//反码:11111111111111111111111111110101
//补码:11111111111111111111111111110110
return 0;
}

(2)无符号整型

无符号位,即所有位都是数值位。

原码:直接将数值转换为二进制

反码:和原码完全相同

补码:和原码完全相同

(3)整型的存储:补码

只要是整型,内存中存储的都是二进制补码

原因:

  • 使用补码可以将符号位和数值位统一处理
  • 加法和减法也可以统一处理(CPU只有加法器)
int main()
{
1 - 1;
//转换为1 + (-1)

// 1 原码:00000000000000000000000000000001
//-1 原码:10000000000000000000000000000001
//原码相加:10000000000000000000000000000010,是错误的

// 1 补码:00000000000000000000000000000001
//-1 反码:11111111111111111111111111111110
//-1 补码:11111111111111111111111111111111
//补码相加:100000000000000000000000000000000,整型只有32位,故最高位的1被舍去,答案正确
return 0;
}


2.大小端字节序

  • 大端字节序(存储)模式:指数据的低位保存在内存的高地址中。
  • 小端字节序(存储)模式:指数据的低位保存在内存的低地址中。

注意:描述的是字节存放的顺序,而不是二进制位存放的顺序。(8个二进制位为一组的意)

例1:

int a = 20;
//0x00 00 00 14

小端:14 00 00 00

低地址 ——————————————> 高地址

大端:00 00 00 14

例2:用代码实现判断本机器的字节序式是大端还是小端

int main()
{
int a = 1;
char* p = (char*)&a;
if(*p == 1)
{
printf("小端\n");
}
else
{
printf("大端\n");
}
return 0;
}

例3(易错):

#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);
return 0;
}

输出结果:-1 -1 255

解析:

​char a=-1;​

char默认有符号位。

虽然是给char赋值,但-1自身是整型,所以应先找出-1的补码,再取最低8位给char a,即

-1原码10000000000000000000000000000001

-1反码11111111111111111111111111111110

-1补码11111111111111111111111111111111

a补码11111111

第7行代码打印整型(%d),会发生整型提升,高位补符号位(1),则

a补码111111111111111111111111111111111

a反码111111111111111111111111111111110

a原码100000000000000000000000000000001,即-1

​signed char b=-1;​

与a的分析相同,也是-1

​unsigned char c=-1;​

与a的分析前半部分相同

a补码也是11111111,

第7行代码打印整型(%d),会发生整型提升,无符号则高位补0,则

a补码000000000000000000000000011111111,又由于无符号位,则a的原码、补码、反码相同

a原码000000000000000000000000011111111,即255

例4(易错):

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

输出结果:4294967168

解析:

char默认有符号位。

虽然是给char赋值,但-128自身是整型,所以应先找出-128的补码,再取最低8位给char a,即

-128原码10000000000000000000000010000000

-128反码11111111111111111111111101111111

-128补码11111111111111111111111110000000

a补码100000000

第5行代码打印十进制无符号整型(%u),会发生整型提升,再以无符号形式输出。

提升时看的是“自身”。a自身是有符号数,则整型提升时补符号位

a补码11111111111111111111111111110000000

打印时是以打印的类型角度看补码。%u是无符号,则认为a补码是无符号,那么原码、反码、补码相同,直接打印出a补码的十进制值,即4294967168

例5:

#include <stdio.h>
int main()
{
int i = -20;
unsigned int j = 10;
printf("%d\n", i+j);
return 0;
}

输出结果:-10

解析:

​int i = -20;​

-20原码10000000000000000000000000010100

-20反码11111111111111111111111111101011

-20补码11111111111111111111111111101100

​unsigned int j = 10;​

10原码00000000000000000000000000001010

10补码00000000000000000000000000001010

​i+j​

i+j补码11111111111111111111111111110110

打印输出十进制有符号整型(%d)

i+j反码11111111111111111111111111110101

i+j原码10000000000000000000000000001010,即-10

例6(易错):

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

输出结果:9 8 7 6 5 4 3 2 1 0 4294967295 4294967294 4294967293(一直-1死循环)

解析:

当i=-1时,-1原码10000000 00000000 00000000 00000001

-1反码11111111 11111111 11111111 11111110

-1补码11111111 11111111 11111111 11111111

由于i是无符号整数型,则i原码和补码相同11111111 11111111 11111111 11111111

输出时看成无符号数,则值为2^32-1,即4294967295

3.char的存储内容

  • char
  • signed char
  • unsigned char

都是占1个字节,即8个bit位。易知,可以存储256(2^8)种补码。

有符号数(原码)

补码

无符号数(原码)

0

1

2

3

……

127

00000000

00000001

00000010

00000011

……

01111111

0

1

2

3

……

127

-128(不计算直接赋值)

-127

……

-3

-2

-1

10000000

10000001

……

11111101

11111110

11111111

128

129

……

253

254

255

总结

  • 有符号char:-128至127
  • 无符号char:0至255

注意:char的数值可以理解为一个圈,

  • 如有符号char中,128=127+1等价于-128,-129=-128-1等价于127。
  • 无符号char中,256=255+1等价于0。

例1(易错):

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

两段代码的结果是一样的,因为char不能存储128,其实等价于127+1,即-128。

例2(难题):

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

输出结果:255

解析:

strlen函数计算的是参数的长度,在'\0'前停止累计,'\0'的ASCII码值为0。

问题转换为何时出现0。


表面上经过for循环后,a[0]=-1,a[1]=-2,a[2]=-3,……,a[999]=-1000

实际上,数组a[1000]内的元素是char类型的,数据范围只能是-128至127

那么,a[128]=-129=-128-1等价于127,以此类推,a[255]=-256=-128-128等价于0

例3:

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

输出结果:死循环

解析:

因为i是无符号char类型,i<=255恒成立。

四、浮点型在内存中的存储

注:科学计数法

#include <stdio.h>
int main()
{
double d = 1E10;//1.0^10
printf("%lf\n", d);
return 0;
}
//输出结果:10000000000.000000

国际标准IEEE754规定

二进制浮点数V可以表示成​:​​(-1)^S * M * 2^E​

  • ​(-1)^S​​表示符号位。S=0时,V为正数;S=1时,V为负数。
  • ​M​​表示有效数字。1<=M<2。也就是说M=1.xxxxxx,因此M存储的时候只保存xxxxxx部分,右端用0补齐。
  • ​2^E​​表示指数位

对于32位浮点数,最高1位是符号位S,接着8位是指数位E,剩下23位是有效数字M。

对于64位浮点数,最高1位是符号位S,接着11位是指数位E,剩下52位是有效数字M。

指数位E详解

存储到内存中

E是一个无符号整数,这意味着8位E取值范围是0至255,11位E取值范围是0至2047。

但是,在科学计数法中E是可能出现负数的,所以IEE754规定,存入内存时E的真实值必须再加上一个中间数

对于8位E,中间数是127。

对于11位E,中间数是1023。

从内存中取出

情况一(一般):E不全为0/1

  • E的真实值:对于8位E,减去127。对于11位E,减去1023。
  • M的真实值:将有效数字M前加上第一位1。

情况二:E全为0

  • E的真实值:规定对于8位E,是1-127=-126。对于11位E,是1-1023=-1022。
  • M的真实值:不变,为0.xxxxxx。

情况三:E全为1

  • 如果M全为零,则表示±无穷大,不做讨论。

例1:浮点型存储

#include <stdio.h>
int main()
{
int n = 9;
float* pfloat = (float*)&n;
printf("%d\n", n);
printf("%f\n", *pfloat);

*pfloat = 9.0;
printf("%d\n", n);
printf("%f\n", *pfloat);
return 0;
}

输出结果:

9
0.000000
1091567616
9.000000

解析:

第6行:略


第7行:9原码00000000 00000000 00000000 000010019补码和原码相同00000000 00000000 00000000 00001001

由于pfloat是浮点数,*pfloat解引用时认为被解的对象是浮点数。

用浮点数存储的视角看9补码:0 00000000 00000000000000000001001

E全为0,则E的真实值是1-127=-126

M的真实值是0.00000000000000000001001

则浮点数为(-1)^0 * 0.00000000000000000001001 *2^(-126) 

而%f默认打印6位小数,因此输出结果为0.000000


第10行:9.0本身是浮点数,存储方式按照浮点型9.0 → 1001.0 → (-1)^0 * 1.001 *2^3

按照标准公式,此时S=0,M=1.001,E=3+127=130

内存中存储为0 10000010 10010000000000000000000

由于n是整型,应该用n的视角看9.0的内存

n补码:01000001 01001000 00000000 00000000

正数原码和补码相同:01000001 01001000 00000000 00000000,即1091567616


第11行:浮点数默认输出6位小数


标签:存储,进阶,符号,int,补码,C语言,char,整型,原码
From: https://blog.51cto.com/u_15883132/5995634

相关文章

  • 指针详解(C语言进阶)
    字符指针指针数组自学b站“鹏哥C语言”笔记。本章笔记不全。回顾:在文章【初识指针】中,我们已经了解到的指针概念有指针是一种变量,用来存放地址,地址唯一标识一块内存空间。指......
  • 【C语言 数据结构】二叉树
    文章目录​​二叉树​​​​一、二叉树的概念​​​​二、二叉树的基本形态​​​​三、二叉树的性质​​​​四、特殊的二叉树​​​​五、二叉树的存储结构​​​​5.1......
  • 客服系统即时通讯IM开发(三)访客实现一对一聊天-访客生成唯一id标识存储到全局变量【唯
    在访客进入聊天界面的时候,就要调用接口生成一个唯一ID标识然后前端链接WebSocket的时候,传递这个访客ID进来 如果你想在前端访客连接时生成一个UUID,可以使用Go语言的......
  • C语言校园跳蚤市场信息交流平台
    C语言校园跳蚤市场信息交流平台[任务描述]设计一个校园跳蚤市场信息交流平台,为同学们交换二手物品提供便利。[功能要求](1)管理员功能:管理员对待销或求购的二手物品......
  • C语言程序设计课程设计[2023-01-07]
    C语言程序设计课程设计[2023-01-07]C语言程序设计课程设计要求一、课程设计目的1.进一步掌握和利用C语言进行程设计的能力;2.进一步理解和运用结构化程设计的思想和......
  • Python----函数进阶
    函数的返回值作为参数传递给其他函数deffunc():return50deffunc1(num):print(num+100)func1(func())函数返回多个值deffunc():#返回值可以是......
  • c语言的主要用途是什么?
    C语言的用途可以概括如下: 1)系统编程C语言可移植性好,性能高,能够直接访问硬件地址,而且到达某个地址的时间非常短,这使得C语言天生适合开发操作系统或者嵌入式应用程序。在......
  • 【数据结构】C语言实现的AVL树操作集
    看到网上完整的AVL树操作集较少,索性自己写了一个,望大佬指教!不多废话,上代码:AVLTREE.h头文件1#pragmaonce2#include<stdio.h>3#include<stdlib.h>4#inclu......
  • C语言百日刷题第一天
    猜名次5位运动员参加了10米台跳水比赛,有人让他们预测比赛结果:A选手说:B第二,我第三;B选手说:我第二,E第四;C选手说:我第一,D第二;D选手说:C最后,我第三;E选手说:我第四,A第一;比赛结束后,每......
  • dremio 系统内部存储插件与自定义存储插件加载的区别
    dremio整体包含了两大类存储扩展,系统内部使用的,以及用户开发的,整体区别系统的目前是在dremio自己启动的时候就会注册以及使用的,比如加速反射的,home,元数据存储插件用......