【C语言】数据在内存中的存储

简介: 前言不同的数据在内存中的存储形式是不同的,而当我们掌握数据在内存中的存储形式之后,会帮助我们更加了解计算机深层工作原理废话不多说,我们接下来直接进入正题

前言

不同的数据在内存中的存储形式是不同的,而当我们掌握数据在内存中的存储形式之后,会帮助我们更加了解计算机深层工作原理

废话不多说,我们接下来直接进入正题



一:数据类型详细介绍

##1.整型家族

char

unsigned char

signed char

int

unsigned int

signed int

short

unsigned short

signed short

long

unsigned long

signed long#

#2.浮点型家族

double

float

##3.构造类型

1.数组类型 int arr[10]={0};

2.结构体类型 struct stu{}

3.枚举类型 enum

4.联合类型 union


##4.指针类型

1.数组指针

2.整型指针

3.字符指针

4.无类型指针

5.单精度浮点型指针


##5.空类型

void*p

这种类型指针可以接收任意的数据类型的地址


二:整型在内存中的存储

2.1原码,反码,补码的介绍

这三种整型的表达形式均有符号位和数值位


原码其实就是把我们所直观看到的数字,用二进制形式表达出来

例如:char型的数字-1表示为10000001

反码其实就是让原码符号位不变,其他数值位按位取反

例如:char型的数字-1表示为11111110

补码其实就是反码+1

例如:char型数字-1表示为11111111

无符号数,有符号数中的正数的原码反码补码相同



2.2整型提升的规则

非常简单,一定要记住啊!!!

有符号数高位补符号位,无符号数高位补0


2.3大小端字节序的介绍


大端字节序存储方式:补码的低位放在内存的高位,高位放在内存的低位

例如:00000000 00000000 00000000 00010100int型的20在表现形式为16进制的大端字节序下存为00 00 00 14

小端字节序存储方式:补码的低位放在内存的低位,高位放在内存的高位

例如:00000000 00000000 00000000 00010100int型的20在表现形式为16进制的大端字节序下存为14 00 00 00

d8c94063dd30459681d7c1b669cb29b8.png


C语言整型提升的规则及样例详解(转载自csdn博主恋浅笑的文章)

C语言的int类型与unsigned int运算的问题。(转载自csdn博主漩涡小林的文章)

2.4还是上练习题吧(介绍的好累😩)


先说一些看似是废话,实际上却是非常重要的话语(不知道这些话很可能产生很多傻逼的疑问,)


1.c语言默认数字是int型的也就是32比特位

2.%d是打印int型的,也就是输出有符号的十进制数字,%u是打印unsigned int,也就是输出无符号的十进制整数

3.整型提升是c程序设计语言中的一项规定,在表达式进行计算时,所有的整型首先要提升为int类型,因为cpu中的整型运算器的操作数的字节长度是int类型的字节长度

OK,有了这些知识的铺垫,我们就可以砍瓜切菜般处理这些智障问题了,哈哈哈哈哈

1.
//输出什么?
#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;
}


bf224e3345d94b83b59aa3daeb731cfb.png



储存: -1的补码为全1,当发生截断后存储在变量abc中的内存形式均为11111111,但打印的是int型的十进制数字,所以要进行整型提升。

ab是有符号数字,高位全补符号位,也就是补1,c是无符号数字高位补0

a:11111111 11111111 11111111 11111111-补码

b:11111111 11111111 11111111 11111111-补码

c: 00000000 00000000 00000000 11111111-原码,反码,补码


读取:ab都是有符号数字,原码,反码,补码都是不相同的所以在我们想要知道他的结果时,要去读取他的原码,但c是无符号数字,所以他的原码,反码,补码相同,直接读取就OK了。

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

282d8bd16431422c88494121d9005463.png



储存:(注意char默认为signed char)-128的原码是10000000 00000000 00000000 10000000,反码为11111111 11111111 11111111 01111111 ,补码为11111111 11111111 11111111 10000000截断后为10000000。打印的形式是%u,所以要进行整型提升。

怎么进行整型提升呢?当然要先看他是什么类型的啦,既然是有符号数10000000的高位就是符号位,补1后结果为11111111 11111111 11111111 10000000

读取:打印时,因为打印的是无符号数,那么高位就会被我们的电脑认为是数值位,因为不方便读出他的数字,所以我们可以借助程序员专属计算器,算一下它的结果就是4294967168

085d0c0736b148e29424267818004f48.png



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

b91ed31a507d4036be71802c2eb2b224.png

我们在看到char和unsigned char时一点要敏感他们的范围

1.char -128到127

2.unsigned char 0到255

存储:128在有符号数中其实就是127+1,也就是-128那么他的结果就是4294967168

 4.
int i= -20;
unsigned  int  j = 10;
printf("%d\n", i+j); 
//按照补码的形式进行运算,最后格式化成为有符号整数

5aa2862924dd4cfd93c0bb2cdd59495f.png


存储:i为有符号整型,存储的是补码,-20的原码为10000000 00000000 00000000 00010100,反码为11111111 11111111 11111111 11101011,补码为11111111 11111111 11111111 11101100,j为无符号整数补码即为原码,10的原码为00000000 00000000 00000000 00001010,i+j结果为11111111 11111111 11111111 11110110

读取:将i+j的结果强制输出为有符号数,则我们想要知道结果,就得先把它转换为原码,原码就是我们能够看到的输出结果,现在我们进行原码的读取(写的好累啊),i+j的反码为10000000 00000000 00000000 00001001,反码+1结果为10000000 00000000 00000000 00001010,那么输出结果就是-10

5.
unsigned int i;
for(i = 9; i >= 0; i--)
{
    printf("%u\n",i);
}


c694e382553d40c697b4591d55a61e3c.png



答案陷入了死循环

**存储:**首先i是个无符号整型0-255,当i=-1时,由于是无符号数,则原码,反码补码都相同,所以全1(补码)会被当做原码处理,并且打印出来,下一次i又变为-2,则原码,反码,补码依旧还是相同的,那其实就是全1减1(补码)的形式,被继续当作原码处理并输出,那么程序会一直走下去

32个比特位,从4294967295开始走,输出一次-1一下,把程序都干死了走的

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


e490fdf84b764baaaf68d5b5be1cea24.png


知识准备:1.strlen是求字符串长度的库函数,计算\0之前的字符串个数

2.char能表示的范围是-128到127,所以创建的char型的大小为1000的数组a只能存放-128到127的数字

3.char型数字表示的范围,我们可以把它想像成一个钟表,从右向左超过127时,回到-128的位置(我们的知识储备OK了,可以消灭这道题了)

储存:a[i]从-1开始被赋值,会被先赋值到-128,然后再到127,逐次-1,最后被赋值成0,那么被赋值为0的那个数组元素在数组的哪个位置呢?经过加法运算,我们知道下标为255的数组元素被赋值成0,那个数字在我们看来其实就是第256个数字

输出结果:我们知道第256个数组元素被赋值成为了0,那么strlen只能计算到255个元素,所以我们的结果为255

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


9a1b6ae9a74947b2975bc6c42c99ea55.png



知识准备:我们定义了一个无符号char型变量i,我们知道无符号整型能表示的范围为0到255

程序的解读:我们在上面的题目中可以知道,char型表示数字范围可以想象为一个钟表,那么unsigned char当然也可以这样表示啦!

输出结果:我们其实可以知道,无论i的数值为多少,它永远都在0到255这个循环里面,那么for循环的判断条件就永远成立,所以这个循环就会一直进行下去,自然程序输出的结果为死循环


三:浮点型在内存中的存储

3.1浮点型在内存中的存储规则(概念来了😂):

3.1.1直接上一波硬性规定的概念(哈哈哈🤣):


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

1.(-1)^S * M * 2 ^ E

2.S代表符号位,当S等于0,二进制浮点数为正数,当S等于1,二进制浮点数为负数

3.M表示为有效数字,大于等于1小于2

4.E表示指数位:为什么他是2的E次方呢?(当然是因为他是二进制啦,你十进制用的不是10的E次方?哼😝)


3.1.2举例解释上面的概念:

举例来说:


十进制的浮点数9.0,转换为二进制为1001.0,那么他又可以写成1.0012^3,那么他的符号位是0,有效位是1.001,指数位是3
十进制的浮点数-9.0,转换为二进制为-1001.0,那么它又可以写成-1.001
2^3,那么他的符号位是1,有效位是1.001,指数位是3


3.1.3描述浮点数在内存中的存储形式:

单精度浮点数: 对于32比特位的单精度浮点数,最高位是符号位S,接着的8比特位是指数位,剩下的23比特位是有效位

faa9ad1a174c4129b89a09f61df171fa.png

双精度浮点数: 对于64比特位的双精度浮点数,最高位是符号位S,接着的11比特位是指数位,剩下的52比特位是有效位


e6d1b02b87b547aea1d1d82fa0224de5.png

科学研究表明人类的大脑更容易记住图像哦😎

记住表示形式是SME,储存时却是按照SEM的顺序



3.1.4IEEE的一些特殊规定(针对于指数位和有效数字位):


1.有效数字位: 有效数字M可以表示成1.xxxxxx,其中xxxxxx是小数部分,所以IEEE规定,当储存有效数字位时,我们只储存小数部分,等到读取时,我们再把开头的1补上去,这样做可以帮助机器多储存一个有效数字位

2.指数位(指数是非常复杂的):

(1.指数的存储):首先E为一个无符号整数(unsigned int),如果是单精度浮点数,E的取值范围是0到255,如果是双精度浮点数,E的取值范围是0到2047.但是科学计数法中的E是可以出现负数的,所以IEEE规定,当我们在内存中存储整数E时,E的真实值要加上一个中间数,对于不同的精度浮点数,这个中间数分别是127和1023

(2.指数从内存中的取出):当我们存储的知识点介绍完之后,读取指数的方式又分为3种

当E不全为0或不全为1时: 这时浮点数采取下面的规则表示,真实有效数字=内存存储+1,真实指数数字=内存数字-127或1023

当E全为0时: 读取有效数字时,不再加上1,直接写成0.XXXXXX,读取指数数字时,指数都被相关组织硬性规定为1-127和1-1023,其实就是-126和-1022

当E为全1时: 这时如果有效数字M全为0,表示±无穷大

由于上面两种的极端特殊情况计算机也无法进行相应的计算来体现,所以不是很重要的,我们就介绍到这里,大家把第一种经常常见的读取方式就完全OK了嘻嘻😁

————————————————


3.2 上一个例题(要不然不会运用知识内容😋):

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


372d609105eb48a1bcf1274b550e439e.png

知识准备: int型的数字9在内存中的存储形式为00000000 00000000 00000000 00001001,转换为float型后被分为0 00000000 00000000000000000001001,分别对应的是符号位,指数位,有效数字位。

第一部分的结果剖析: 所以我们打印整数n时,结果肯定是9,因为他的存储方式和读取方式我们都很熟悉,存储按二进制,读取也是按二进制。

但当我们打印浮点数时,我们在存储和读取是于int型是不同的,符号位是0,指数位也是0,因为指数位是全0,那么属于读取的第二种情形,将浮点数表示为0.92^-126,这个数字表示非常小的数字,机器就会输出0

第二部分的结果剖析: 十进制的浮点数9.0表示为二进制为1001.0,浮点表示为1.0012^3,存储到内存里为

0 10000010 00100000000000000000000按照整数形式输出我们可以看到结果就是1091567616

最后那个9.000000肯定不用我说了吧,你按浮点数存储,又按浮点数的形式进行打印,结果当然不变啦!!!😍😎😋


bf21d359825548f3abfc073c788c2cc9.png


3.3总结整数和浮点数在存储时的一些区别



在小数部分的存储,我们直接将浮点数二进制表示形式下的小数部分存到23比特位的部分即可(或者52比特位),在读取时我们也直接将其整体拿出来加上个1就是二进制形式下的浮点数表达了


为什么要特殊说一句这个呢?因为我们已经习惯读取整数的方法了,我怕你们在读出内存中的小数部分时,忍不住按照二进制翻译为十进制的形式把它作为小数写到浮点数二进制的表示形式当中


而且我们存储小数部分时,必须要将二进制的小数部分从左向右排放到相应的比特位当中,(后面的空余比特位直接补0即可),千万不要按照高位低位的形式存储小数位,这样存储直接就反过来了,存到后面去了 举例:比如例题中的001,你若存错的话,就变为00000000000000000000001,直接这题就被你给做报废了,大兄弟!!!😱😱😱



四:小编对你说的话


小编写这篇稿子真的是用心了,如果你觉得小编的文章对你真心有帮助的话,请不要吝啬您的点赞和关注,随着我的实力的成长,将来小编也会继续发出更多优秀的文章,帮助大家进步的,我们一起卷死别人吧!!!😎😎😎哈哈哈


































相关文章
|
1天前
|
存储 C语言
【海贼王编程冒险 - C语言海上篇】C语言中的数据类型有哪些?又是如何存储?
【海贼王编程冒险 - C语言海上篇】C语言中的数据类型有哪些?又是如何存储?
5 0
|
1天前
|
存储 C语言
【C语言进阶篇】整数在内存的存储——原码、反码、补码
【C语言进阶篇】整数在内存的存储——原码、反码、补码
|
1天前
|
程序员 编译器 C语言
【c语言】详解动态内存管理
【c语言】详解动态内存管理
3 0
|
2天前
|
程序员 C语言 C++
【C语言】:柔性数组和C/C++中程序内存区域划分
【C语言】:柔性数组和C/C++中程序内存区域划分
4 0
|
2天前
|
编译器 测试技术 C语言
【C语言】:自定义类型:结构体的使用及其内存对齐
【C语言】:自定义类型:结构体的使用及其内存对齐
16 7
|
2天前
|
C语言
【C语言】:4大内存函数
【C语言】:4大内存函数
8 2
|
2天前
|
C语言
【C语言】:总结动态内存的常见错误
【C语言】:总结动态内存的常见错误
6 0
|
2天前
|
C语言
【C语言】:动态内存管理函数malloc,calloc,realloc和free的介绍的介绍
【C语言】:动态内存管理函数malloc,calloc,realloc和free的介绍的介绍
10 0
|
3天前
|
存储 Java C++
Java虚拟机(JVM)管理内存划分为多个区域:程序计数器记录线程执行位置;虚拟机栈存储线程私有数据
Java虚拟机(JVM)管理内存划分为多个区域:程序计数器记录线程执行位置;虚拟机栈存储线程私有数据,如局部变量和操作数;本地方法栈支持native方法;堆存放所有线程的对象实例,由垃圾回收管理;方法区(在Java 8后变为元空间)存储类信息和常量;运行时常量池是方法区一部分,保存符号引用和常量;直接内存非JVM规范定义,手动管理,通过Buffer类使用。Java 8后,永久代被元空间取代,G1成为默认GC。
11 2
|
3天前
|
存储 C语言
数据在内存中的储存
数据在内存中的储存
7 3

热门文章

最新文章