手把手教你C语言‘数据的存储‘[doge]

@[toc](目录)

想来学完大多语法后,大家都对整型,浮点型,字符型都有了大概的认识,简而言之,大概知道是什么.

对其相关的了解想要更进一步,那就必须要知道这些类型在内存中是如何存储的了.

彳亍,那我们接下来就浅谈这几种类型的存储.

目录

数据类型

整型,浮点型的存储

大小端讲解

例题巩固


数据类型

想想到现在我们学了什么?               好像啥也没学

那从最基本的类型说起,下面是我们学过的几种类型

short   2
int     4
long    4
long long    8

float   4
double  8

char   1

他们的类型决定了我们看待内存的视角,也就是我们怎么看它,像一个标签一样

而要讨论他们在内存中的存在方式,肯定要讨论它的大小了

大小(所占字节数)决定了他们的精度,也决定了取值的范围

自然类型的意义就出来了:

1.类型决定了看待内存的角度

说人话:计算机怎么看它的,我们又是怎么看它的

2.类型决定了他们的使用范围

简单点:一个桶子只能装1L的水,你装1.1L就会溢出

下面我们就具体分一下类

Q:啥?上面不是分了?

A:那只是概述

具体分类如下

整型:

char:   (别急后面解释为啥放这)

unsigned char
signed char

short:
unsigned short
signed short


int:
unsigned int
signed int

long:
unsigned long
signed long

long long :
unsigned long long
signed long long

char字符型放在‘整型家族’的原因是char存的不是字符,而是字符的ASCII值,是个数字

浮点型:

float
double

构造类型:数组,struct,enum,union  (注意是数组和结构体,后面的枚举和联合知道有它就行了)

指针:
char*
int*
float*
double*
void*    等等等等
指针类型太多在这不一一列举了

空类型:
void
函数返回值,函数参数,指针类一般都可以见到它

现在你应该明白数据的分类了.

那么归好类,就得讲讲这个东西是怎么存的了

整型和浮点型的数据存储方式不同

所以小白时期明明感觉没错,但是打印的结果很奇怪,有一部分就是这个问题没有解决

先说整型:

整型在内存里存的是补码

我们进行的加减乘除全是操作补码

计算机里表示一个数有三种方法--原码,反码,补码

原码--符号位+二进制序列

第一位是符号位  0代表正,1代表负

例:

5的二进制101,如果放进int里面,int是32位

那么就写成

正数的原反补三码相同,写出原码就等价于写出补码

负数复杂一点

反码==原码符号位不动,其余位按位取反

解释:第一位不动,其余位0变成1,1变成0

补码==反码+1

例:

这就是整型,long,longlong之类的都可以类推

值得一提的是char类型,一个字节,八位,所以取值范围:-128-127

下面推导char的类型(有符号的char)


1000 0001是几?

由计算机算出来是127,加上符号位的1表示负数,所以是-127

那最后11111111是几?

显然是-1,可以自己动笔推算一下

由以上就可以知道char类型的取值是一个循环

假若我们从0开始

那就是0  ->  127  ->  (-128)  ->  (-1)  ->  0

这是有符号的char的范围:-128-127

注:127+1==-128

127是百尺竿头,再进一步就是-128

同理也可知道无符号的char: 0 - 255

到这里,我们就知道了整型在内存种的存储方式,存的是补码,我们也要知道怎么表示他们

同时也推导了char的取值范围,整型到这就告一段落了

---------------------------

整型,浮点型的存储

下面我们谈谈浮点型:

来一道典型的例题看看:

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; }

猜猜这四个打印的值是多少?

想好了没?

公布答案:

小朋友是不是有很多的问号?

哈哈哈哈哈哈,说回正题

具体介绍下浮点型咋存的,再来解释打印的结果

float

记住这么几个数字  :1-8-23

double   

记住这么几个数字:  1-11-52

这几个数字意义是什么:

从前往后:符号位 -指数- 有效数字(S-E-M)

E是一个无符号数,后面解释为什么

例:

S:0表示正数,1表示负数(这点和整型对应上了)

E有八位,第八位是符号位吗?前面说过转换过程+127所以一定是正数,

所以E的第八位不是符号位,这是一个无符号数

上面是E不全为0或者不全为1

那如果E(指数)是全0或者全1呢,属于特殊情况,分类讨论

全0:说明这个数很小,想想+127都是0了,那读取的时候应用规则:E取1-127的值或者1-1023的值

全1:这个数的绝对值很大,趋于无穷(指数不能决定正负)

到此我们知道了浮点数的存储方式

十进制的浮点数->写成二进制->转换成指数形式->根据规则确定存入的S-E-M的值,再按规则取出来

这就是浮点数的存储

那么我们来解释一下最开始的例题:

不用往上翻了

从内存入手:

先看这四句话:

n以%d(整型的方式读取)打印肯定是9,没问题

float*和int*都操作四个字节,都可以拿到32位,不存在截取的问题

所以%f就是以浮点数的读取方式读取整型,由补码入手:

E全是0,无限接近0的一个数,float默认打印小数点后6位,

自然结果:

 

那再看后面两行:

第一行赋值为9.0,float型指针,以浮点数的方式存进去

那就有:

以%d的形式打印,也就是以整型的办法去读取:

正数,所以直接补码==原码

读取的十进制数也就是计算器所呈现的1091567616

以%f,也就是浮点型的方式去读取它,自然是9.000000

我们再比对一下结果:

ohhhhhhhh,一样的对吧.

------------------

到这里我们就知道了浮点数的存储方式,进度条过半

-----------------

大小端讲解

接下来就是大小端的问题了

先给定义:

大端(存储)模式,是指数据的低位保存在内存的高地址中,而数据的高位,保存在内存的低地址中;
小端(存储)模式,是指数据的低位保存在内存的低地址中,而数据的高位 , ,保存在内存的高地址中。
简单点,说话的方式简单点
数字有高位和低位
例:12345  5是个位,是低位
10101010   0是2^0  是低位
地址有高地址和低地址
例:0x00000001  低地址
       0x00000005  高地址
知道这两个再理解大小端:
大端:数据的低位 放在  高地址   
小端:数据的低位 放在 低地址
这么说还是太抽象了,举例说明:
这是VS编译器,通过调试我们知道a里面存的是这玩意?
所以VS是大端还是小端?
前情提要:
'0x'告诉编译器这是个十六进制数字,由于32个二进制位显示过长,所以调试显示的是16进制.
一个十六进制位==4个二进制位
两个十六进制位==1个字节,所以上面显示的一组数字就是一个字节,四组四个字节,满足int的大小.
由上图可知:低位放到了低地址,高位放到了高地址,对应定义,确实是小端存储.
对大小端有了一个认识后,我们来看一道题:
请简述大端字节序和小端字节序的概念,设计一个小程序来判断当前机器的字节序。( 10 分)
大端字节序:高字节内容保存在内存的低地址处,低字节内容保存在内存的高地址处
小端字节序: (看啥?补充啊,上面给了你个范本了)
小程序:
以1为例(数字简单好推算结果)
1==0x00 00 00 01
若是大端字节序,存的就是:00 00 00 01(正着存)
若是小端字节序,存的就是:01 00 00 00(倒着存)
显然只要看第一个字节的内容是1还是0?-> 操作一个字节-> char类型的指针 ->用拿到的数去 '&1' 就可以知道拿到的数是不是1
代码如下:
int check(int a)
{
	char* tmp = (char*)&a;
	if (*tmp & 1 == 1)
	{
		return 1;
	}
	return 0;
}

int main()
{
	int a = 1;
	if (check(a))
	{
		printf("小端\n");
	}
	else
	{
		printf("大端\n");
	}
	

再次证明VS是小端字节序机器.
------------------进度过去3/4,就快结束了
对于基础知识我们有了一定的了解,下面就是做题巩固了

例题巩固

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; }

因为是第一题,讲详细一点

某些编译器默认char是有符号的signed char,有些又当作unsigned char处理。VS默认signed char

char a==signed char a (VS编译器下)

有符号的char:

我们再来看看结果:
没问题!
有个诀窍:char--  在-128-127这个范围里面的肯定就是这个数本身
          unsigned char   ---  0-255  在这个范围里面的肯定就是这个数本身,此题-1不在那就加或减256,让它进入这个范围(-1+256==255)

2.

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

-128在这个范围内,但是注意是无符号数打印(%d打印那就是-128),所以还是从补码入手

让我们来比对下答案:
没问题
3.最后一题
int i= -20;
unsigned  int  j = 10;
printf("%d\n", i+j); 
//按照补码的形式进行运算,最后格式化成为有符号整数

可以,没问题!

------------------终于写完了!-------------

能看到这真的强:

----------------------------

感谢阅读,如果有帮助别忘了点赞,这对我帮助很大,下次再见!

  • 23
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 16
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 16
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

喜欢乙醇的四氯化碳

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值