C语言--数据的存储1
创始人
2024-05-23 15:43:45

目录

  • 数据类型的介绍
    • 类型的意义
  • 类型的基本归类
    • 整形家族
    • 浮点型家族
    • 构造类型--自定义类型
    • 指针类型
    • 空类型
  • 整形在内存中的存储
  • 大小端
    • 大小端如何区分
    • 为什么会有大小端
    • 判断机器字节序

从本章开始,我们将正式进入C语言的进阶学习中。
本篇内容我们将学习 数据的存储

数据类型的介绍

在以往的学习当中,我们已经接触过许多的基本数据类型了,如下:

char         //字符数据类型
short        //短整型
int          //整形
long         //长整型
long long    //更长的整形
float        //单精度浮点数
double       //双精度浮点数

那么C语言中有没有字符串类型呢?
其实在C语言的环境中是没有的,很多高级语言java #c就有字符串类型,有string来表示字符串,用法和int很类似,但在C语言中字符串是通过字符指针来间接实现的。

类型的意义

我们使用这个类型可以开辟不同大小的空间(大小决定了使用范围)(例如int类型就会开辟4个字节大小的空间),同时也为程序员提供了一个观察内存空间的视角

类型的基本归类

整形家族

charunsigned charsigned char
shortunsigned short [int]signed short [int]
intunsigned intsigned int
longunsigned long [int]signed long [int]

我们发现,char类型也被归在了整形家族中,这是因为在存储字符的时候,实际上是储存的ascll码值,而ascll码值实际上也是一串数字,所以char类型实际上也隶属于整形家族。

浮点型家族

double
float

构造类型–自定义类型

//结构体类型
//数组类型
//枚举类型
//联合体

指针类型

int* pi;
char* pc;
float* pf;
double* pb;

空类型

void

void 表示空类型
空类型的作用在于:
1,可作为函数的返回类型: void test ()
2,可作为函数的参数: void test ();
3,指针: void* p;

整形在内存中的存储

我们之前讲过一个变量的创建是要在内存中开辟空间的。空间的大小是根据不同的类型而决定的。
那接下来我们谈谈数据在所开辟内存中到底是如何存储的
举个例子:

int main()
{int a = -10;return 0;
}

图1

我们知道数据在内存中以2进制的形式存储。
而对于整数来说:
整数二进制有三种表示形式:原码,反码,补码
而对于正整数来说:原码,反码,补码相同。
对于负整数来说:原码,反码,补码是需要计算的。

就以a=-10为例,
先写原码
-10为负数,符号位就应该为1(正数就为0,负数就为1),10用二进制表示为1010

//原码
100000000000000000000000000000001010

反码就是符号位不变,将原码按位取反

//反码
111111111111111111111111111111110101

补码就是反码+1

111111111111111111111111111111110110

那么我们在内存中储存的究竟是-10的原码还是反码,还是补码呢?我们用监视的方法来看一下,如图2
图2
我们把数据提出来:FFFFFFF6→转换为2进制就为111111111111111111111111111111110110(比如最后四位对应6,6转换为2进制就为0110).

所以,整数在内存中的储存是以补码的形式储存的

那么为什么是存的补码而不是其他形式呢?我们举个例子:
我们首先要知道,编译器里是没有减法计算的,所谓的减法实际上还是加法运算,比如:1-1其实是1+(-1)

假设储存的是原码1的原码:00000000000000000000000000000000001
-1的原码:100000000000000000000000000000000011+(-1)=:10000000000000000000000000000000010这是-2的原码,与我们想得到的0不相符

所以原码储存是行不通的,我们再来试试补码

假设储存的是补码
1的补码:00000000000000000000000000000000001-1的补码:111111111111111111111111111111111111+(-1)=:100000000000000000000000000000000000//多出的一位1丢掉变为00000000000000000000000000000000000这就是0的补码

所以只有用补码来计算才是正确的。
除此之外,补码与原码相互转换,其运算过程是相同的,不需要额外的硬件电路。

到这里我们发现有一个问题没有被解决,那就是监视器中显示的明明是F6 FF FF FF,但我们在读的时候却是反着读的,这里就要涉及新的概念:大端和小端

大小端

我们来看一个地址的存放,如图3
图3
图中第一和第二种就是大端字节序和小端字节序,第三种和第四种方法虽然在理论上来讲也是可行的,但是存放的过程就会变得过于复杂,所以不采用。

大小端如何区分

图4

如图4,我们假设地址11 22 33 44为一个数字,那么越靠近右边,代表着位数越低,而越靠近左边,代表着位数越高,所以右边为低位字节,左边为高位字节
根据我们定义的左边为低地址,右边为高地址,
大端(存储)模式定义为:数据的低位保存在内存的高地址中,而数据的高位,保存在内存的低地址中
小端(存储)模式定义为:数据的低位保存在内存的低地址中,而数据的高位,保存在内存的高地址中

速记的口诀是:小同大异
那么通过我们所学的知识,上述图片中的F6 FF FF FF就为小端存储模式

为什么会有大小端

为什么会有大小端模式之分呢?这是因为在计算机系统中,我们是以字节为单位的,每个地址单元都对应着一个字节,一个字节为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处理器还可以由硬件来选择是大端模式还是小端模式。

判断机器字节序

我们通过设计一个小程序来判断当前机器的字节序
假设我们要存放的地址为 00 00 00 01

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

因为我们只想访问一个字节来判断是否为1,所以我们需要将&a强制转换为char类型,这样char就指向整形a的四个字节中的首个字节*。

我们同样可以通过函数的方法实现:

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

优化一下

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

三种方法运行结果如图4
图4
所以我的电脑采用的是小端存储模式

以上就是本章的全部内容了,如有出入,欢迎指正。

相关内容

热门资讯

北京的名胜古迹 北京最著名的景... 北京从元代开始,逐渐走上帝国首都的道路,先是成为大辽朝五大首都之一的南京城,随着金灭辽,金代从海陵王...
苗族的传统节日 贵州苗族节日有... 【岜沙苗族芦笙节】岜沙,苗语叫“分送”,距从江县城7.5公里,是世界上最崇拜树木并以树为神的枪手部落...
世界上最漂亮的人 世界上最漂亮... 此前在某网上,选出了全球265万颜值姣好的女性。从这些数量庞大的女性群体中,人们投票选出了心目中最美...
长白山自助游攻略 吉林长白山游... 昨天介绍了西坡的景点详细请看链接:一个人的旅行,据说能看到长白山天池全凭运气,您的运气如何?今日介绍...