C语言——数据的存储:整型与浮点型数据类型的存储、大小端字节序、存储例题

内容

1.数据的类型(整数类型、浮点类型、自定义、指针、空类型)
2.整形数据在内存中的存储(原码、反码、补码)
3.大小端字节序
4.浮点型数据的存储
5.关于数据存储的例题
————————————————————————————————————————————

*1.数据的类型

C语言中数据类型包括:

  • C语言内置数据类型
    C语言——数据的存储:整型与浮点型数据类型的存储、大小端字节序、存储例题_第1张图片
  • 其中float、double为浮点类型,其余为整数类型;
  • char为整数类型,因为字符在底层存储的时候,存储的是字符所对应的ASCII值(整数);
    注意:C语言内置类型没有字符串类型,但是再写C语言代码时,可以利用字符数组表示字符串!
  • C语言字符串表示—— 例:char str[]=“hello!”; 或者: char str[]={‘h’,‘e’,‘l’,‘l’,‘o’,’!’,’\0’};
整数 类型
unsigned char signed char
unsigned short[int] signed short[int]
unsigned int signed int
unsigned long[int] signed long[int]
  • unsigned / signed的理解和区别 有符号signed的最高位为符号位,1表示负数,0表示正数, unsigned均为正数,最高位1是实数位,不为符号位;
  • char的类型到底是unsigned char还是signed char是不确定的,取决于编译器;
  • short、long 类型明确为signed型;

注意:unsigned非常容易溢出;在实际开发过程中,能不用unsigned尽量不用
C语言——数据的存储:整型与浮点型数据类型的存储、大小端字节序、存储例题_第2张图片

  • 自定义数据类型
    • 结构体类型:struct
    • 枚举类型: enum
    • 联合类型: union

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

  • 空类型(void)、指针类型
    • 用于函数返回值类型
    • 指针类型(char* 、int*…void *)
    • 函数参数

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

  • 类型的意义:

    1. 使用这个类型开辟内存空间的大小(大小决定了使用范围)。
    2. 如何看待内存空间的视角。

    例:int a=1; float b=1.0;
    虽然 a, b都是占用4个字节的空间,当我们在看待a的时候,因为其类型是int,所以我们会把a当做整型来看待,在看待b的时候,其类型是float,所以我们会把b当做小数(而非整型)来看待。

*2.整形数据在内存中的存储

  • 一个变量的创建是要在内存中开辟空间的。空间的大小是根据不同的类型而决定的。
  • 内存就是公寓,内存公寓包含很多房间,每个房间都有房间号(从0开始依次递增)——地址。
    * * 指针就是存放内存地址的变量;(打印地址或者指针变量格式为:%p)
    * * 在32位的机器上,地址是32个0或者1组成二进制序列,那地址就得用4个字节的空间来存储,所以一个指针变量的大小就应该是4个字节。那如果在64位机器上,如果有64个地址线,那一个指针变量的大小是8个字节,才能存放一个地址;
  • 内存支持随机访问;但只有申请了(创建变量,分配地址)才能访问,如果访问了未申请的内存,就是“未定义行为”;
  • 内存的访问速度更快,空间更小,成本更高,断电后数据丢失;
  • 外存的访问速度更慢,空间更大,成本更低,断电后数据还在;
  • 原码、反码、补码
    计算机中的符号数有三种表示方法,即原码、反码和补码。三种表示方法均有符号位和数值位两部分,符号位都是用0表示“正”,用1表示“负”,而数值位,三种表示方法各不相同。
    • 原码 * 直接将二进制按照正负数的形式翻译成二进制就可以。
    • 反码 * 将原码的符号位不变,其他位依次按位取反就可以得到了。
    • 补码 * 反码+1就得到补码。
    • 正数的原、反、补码都相同

例:
C语言——数据的存储:整型与浮点型数据类型的存储、大小端字节序、存储例题_第3张图片
???那么数据在计算机中内存的存储形式到底是怎么样的形式?

例:
首先,需要给程序打断点,进行编译;
然后,打开内存:调试菜单->窗口->内存;
接着,可以输入&变量名,进行变量内存查询;
C语言——数据的存储:整型与浮点型数据类型的存储、大小端字节序、存储例题_第4张图片
** 上图,显示的是——当前查询的是当前程序无法访问的非法内存(未定义);
C语言——数据的存储:整型与浮点型数据类型的存储、大小端字节序、存储例题_第5张图片
** 从上图可以看出,char在内存中确实是一个字节,字符在内存中以ASCII表示,63-c;再看内存窗口右侧,可以对应表示c;右侧是内存数据的等价表示,把每个字节按照字符理解,若我们分析的内存数据(内容)为字符或者字符串,这就很方便我们理解。

C语言——数据的存储:整型与浮点型数据类型的存储、大小端字节序、存储例题_第6张图片
** 内存中变量之后的内存表示为cc cc …原因是VS的debug模式会自动给局部变量后面加上一些0xcc.
C语言——数据的存储:整型与浮点型数据类型的存储、大小端字节序、存储例题_第7张图片
** 根据调试窗口下内存显示的变量a=2为 02 00 00 00;变量b=-1在内存中为ff ff ff ff;

  • 内存中表示的数据都是十六进制表示的;并且一个字节一个字节(8bits)显示的。(虽然和实际有顺序的差别,这个是字节序大小端问题,下一个部分说明)
    在这里插入图片描述
    在这里插入图片描述
    由此看以看到:内存中存储的数据都是按照数据的补码进行存储的

???那么数据在内存中的存储为什么不是原码而是补码?

C语言——数据的存储:整型与浮点型数据类型的存储、大小端字节序、存储例题_第8张图片
C语言——数据的存储:整型与浮点型数据类型的存储、大小端字节序、存储例题_第9张图片
可以明显看出,以补码计算结果才符合运算结果。

  • 在计算机系统中,数值一律用补码来表示和存储。原因在于,使用补码,可以将符号位和数值域统一处理; 同时,加法和减法也可以统一处理(CPU只有加法器)此外,补码与原码相互转换,其运算过程是相同的,不需要额外的硬件电路。对于乘法也是连加的原理,除法的实现就是看减了几次的结果。总结,引入补码,是为了让计算机硬件设计成本更低!!!

*3.大小端字节序

C语言——数据的存储:整型与浮点型数据类型的存储、大小端字节序、存储例题_第10张图片
可以从内存其中看到,数据在内存中的存储的地址高低顺序可能和实际数据高低位不同;
这就引出一个历史存留问题——大小端字节序问题:
C语言——数据的存储:整型与浮点型数据类型的存储、大小端字节序、存储例题_第11张图片

** 制作CPU时,不同公司设计不同,有的按照升序,低址值存低位数据(44 33 22 11)——小端字节序;高地址存低位(11 22 33 44)——大端字节序;一般我们使用的PC笔记本CPU都为小端字节序; 约定小端字节序!

如何判断一个CPU是大端还是小端?

int IS_BSendian()
{
	int a = 1;
	char* p = (char*)&a;
	if (*p == 1)
		return 1;//小端
	else
		return 0;//大端
}

** 在32位机器上,一个地址单元就是一个字节;故只要截取低位的一个字节,判断值就可判断是大端还是小端;

???为什么会出现大小端?
** 因为在计算机系统中,是以字节为单位的,每个地址单元都对应着一个字节,一个字节为8bit。在C语言中除了8bit的char之外,还有16bit的short型,32bit的long型(要看具体的编译器gdp调试器做一般开发),另外,对于位数大于8位的处理器,例如16位或者32位的处理器,由于寄存器宽度大于一个字节,那么必然存在着一个如果将多个字节安排的问题。因此就导致了大端存储模式和小端存储模式。

例如:一个16bit的short型x,在内存中的地址为0x0010,x的值为0x1122,那么0x11为高字节,0x22为低字节。对于大端模式,就将0x11放在低地址中,即0x0010中,0x22放在高地址中,即0x0011中。小端模式,刚好相反。我们常用的X86结构是小端模式,而KEIL C51则为大端模式。很多的ARM,DSP都为小端模式。有些ARM处理器还可以由硬件来选择是大端模式还是小端模式。

*4.浮点型数据的存储

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

    • (-1)^S * M *2 ^E;
    • (-1)^s表示符号位,当s=0,V为正数;当s=1,V为负数;
    • M表示有效数字,大于等于1,小于2;
    • 2Ê表示指数位;
  • 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。

  • 例:

    • 十进制的5.0,写成二进制是 101.0 ,相当于 1.01×2^2 。 那么,按照上面V的格式,可以得出s=0,M=1.01,E=2。
    • 十进制的-5.0,写成二进制是 -101.0 ,相当于 -1.01×2^2 。那么,s=1,M=1.01,E=2。
    • 32位计算机中S占1个bit,E占8个bit,M占23个bit;64位计算机中S占1个bit,E占11个bit,M占52个bit;

总结:一个浮点数在计算机中用“科学计数法”表示;E越大能表示的数据范围越大;M越大数据精度越大,在开发中需要用到浮点数优先考虑double;该标准下很多浮点数不能准确表示;所以进行浮点数比较时,一定要注意利用差值进行误差控制。

*5.数据存储的例题

1>下面这段代码的结果是什么?

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

C语言——数据的存储:整型与浮点型数据类型的存储、大小端字节序、存储例题_第12张图片
2>下面这段代码的结果是什么?

#include
int main()
{
	char a = -1;
	signed char b = -1;
	unsigned char c = -1;
	printf("a = %d,b = %d,c = %d\n", a, b, c);
	return 0;
}

C语言——数据的存储:整型与浮点型数据类型的存储、大小端字节序、存储例题_第13张图片
3>下面这段代码的结果是什么?

#include
int main()
{
	char a = 128;
	printf("%u\n", a);
	return 0;
}

C语言——数据的存储:整型与浮点型数据类型的存储、大小端字节序、存储例题_第14张图片
4>下面这段代码的结果是什么?

int main()
{
	int i = -20;
	unsigned int j = 10;
	printf("%d\n", i + j);
	return 0;
}

C语言——数据的存储:整型与浮点型数据类型的存储、大小端字节序、存储例题_第15张图片
5>下面这段代码的结果是什么?

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

C语言——数据的存储:整型与浮点型数据类型的存储、大小端字节序、存储例题_第16张图片
6>下面这段代码的结果是什么?

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

C语言——数据的存储:整型与浮点型数据类型的存储、大小端字节序、存储例题_第17张图片
7>下面这段代码的结果是什么?

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

C语言——数据的存储:整型与浮点型数据类型的存储、大小端字节序、存储例题_第18张图片

你可能感兴趣的:(补码,c语言,内存结构)