C进阶——数据的存储(整形)

C语言的基础知识我们已经经过了简单的了解,现在我们开启C语言中进阶的部分。

        在之前的文章中我们简单的提及过Debug和Release两种编译方式在编译时的区别。

现在我们在使用上次的一个例子来进行说明:

#include <stdio.h>

int main(void)
{
	int i = 0;
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	for ( i = 0; i <= 12; i++)
	{
		arr[i] = 0;
		printf("hehe\n");
	}
	return 0;
}

        这段代码在VS2017编译器下在Debug版本下执行的结果是死循环,而在Release版本下执行的结果是有限值,这种现象我们同样可以通过打印地址的方式来发现其中的原因。

	int i = 0;
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	
	printf("%p\n", &i);     
	printf("%p\n", &arr[0]);
    
    //在Debug版本下运行的结果
    //00EFF760
    //00EFF730

    //在Release版本下运行的结果
    //0056F730
    //0056F734

从上面的运行结果结果中我们就可以发现:在Debug版本下数组的首地址比变量i的地址小,而在Release版本下数组的首地址比变量的地址大。这就说明了Release版本会对我们的代码进行优化

目录

数据在内存中的存储

数据类型的详细介绍

数据的基本归类

整型在内存中的存储

原码、反码、补码

大小端字节序的介绍及判断

什么是大小端:

为什么会有大小端:

 一些跟数据存储相关的练习题


数据在内存中的存储

数据类型的详细介绍

基本的内置类型:

char  (1)                             //字符数据类型

short  (2)                            //短整型

int  (4)                                //整型

long  (4/8)                          //长整型

long long  (8)                     //更长的整型

float  (4)                             //单精度浮点型

double  (8)                         //双精度浮点型

类型的意义:

        1.使用该类型开辟的内存空间的大小(大小决定了使用范围)。

        2.看待内存的视角。同样的数据,如果使用的是浮点类型,内存就会将它视为浮点类型的数据;如果是整型,内存就会将它视为一个整型数据。

数据的基本归类

整型家族

char
        unsigned char
        signed char
short
        unsigned short [int]
        signed short [int]
int
        unsigned int
        signed int
long
        unsigned long [int]
        signed long [int]

long long
        unsigned long long[int]
        signed long long[int]

注:

        1.字符的本质是ASCII码,是整型划分到整型家族;char是unsigned char还是signed char的标准未定义,取决于编译器的实现

        2.int a;——>signed int a;

浮点型家族

float

double

只要是表示小数就可以使用浮点类型,float的精度低,存储的数值范围较小,double类型的精度高,存储的数据的范围更大。

构造类型        自定义类型 - 我们可以自己创建出新的类型

数组类型 int[ ]
结构体类型 struct
枚举类型 enum
联合类型 union

指针类型

int *pi;
char *pc;
float* pf;
void* pv;

空类型

void 表示空类型(无类型)
通常应用于函数的返回类型、函数的参数、指针类型

//第一个void 表示函数不会返回值
//第二个void 表示函数不需要传入任何参数
void test(void)
{
	printf("hhh");
}
int main(void)
{
	test();
	return 0;
}

整型在内存中的存储

一个变量的创建是要在内存中开辟空间的,空间的大小根据不同的类型而决定。

例如:int a = 20;        int b = -10;        a就是占据了四个字节的空间。

原码、反码、补码

计算机中的整数有三种表示方法,原码、反码、补码。

这三种表示方法都有符号位与数值位,符号位使用0表示正,使用1代表负。

对于正数而言,原码、反码、补码都相同。

对于负数:

原码:直接将二进制按照正负数的形式翻译成二进制

反码:源码的符号位不变,其它位依次取反

补码:反码的二进制数 + 1

	int a = 20;
	//00000000 00000000 00000000 00010100
	//00000000 00000000 00000000 00010100
	//00000000 00000000 00000000 00010100 	//0x 00 00 00 14

	int b = -10;
	//10000000 00000000 00000000 00001010 - 原码
	//11111111 11111111 11111111 11110101 - 反码
	//11111111 11111111 11111111 11110110 - 补码 	//0x ff ff ff f6

 

         通过代码的实现我们可以得到在计算机的内存中存放的补码。

对于整型来说,数据在内存中存放的是补码。

原因:

        在计算机系统中,数值一律用补码来表示和存储。原因在于,使用补码,可以将符号位和数值域统一处理;同时,加法和减法也可以统一处理(CPU只有加法器)此外,补码与原码相互转换,其运算过程是相同的,不需要额外的硬件电路。

在计算机中计算1-1时,是看做1+(-1)的运算
00000000 00000000 00000000 00000001

10000000 00000000 00000000 00000001 - 原码
11111111 11111111 11111111 11111110 - 反码
11111111 11111111 11111111 11111111 - 补码

结果为:
1 00000000 00000000 00000000 00000000 
由于只有32位,高位丢失,因此结果为:
00000000 00000000 00000000 00000000 

补码 = 原码取反 + 1  ;原码  = 补码取反 +1         补码和原码可以互相转化

在分析调试结果后,得到了在内存中存储的是补码,但是同样可以看到补码以16进制存储的顺序感觉怪怪的,下面来介绍一下其中的原因。

大小端字节序的介绍及判断

什么是大小端:

大端(存储)模式,是指数据的低位保存在内存的高地址中,而数据的高位,保存在内存的低地址中;
小端(存储)模式,是指数据的低位保存在内存的低地址中,而数据的高位,,保存在内存的高地址中。

为什么会有大小端:

为什么会有大小端模式之分呢?

        这是因为在计算机系统中,我们是以字节为单位的,每个地址单元都对应着一个字节,一个字节为8bit。但是在C语言中除了8bit的char之外,还有16bit的short型,32bit的long型(要看具体的编译器),另外,对于位数大于8位的处理器,例如16位或者32位的处理器,由于寄存器宽度大于一个字节,那么必然存在着一个如何将多个字节安排的问题。因此就
导致了大端存储模式和小端存储模式。
        例如:一个 16bit 的 short 型 x ,在内存中的地址为 0x0010 , x 的值为 0x1122 ,那么 0x11 为高字节, 0x22 为低字节。对于大端模式,就将 0x11 放在低地址中,即 0x0010 中,0x22 放在高地址中,即 0x0011 中。小端模式,刚好相反。我们常用的 X86 结构是小端模式,而 KEIL C51 则为大端模式。很多的ARM,DSP都为小端模式。有些ARM处理器还可以由硬件来选择是大端模式还是小端模式。

分享一道有关的题目:请简述大端字节序和小端字节序的概念,设计一个小程序来判断当前机器的字节序。

//这是我自己编写的简单的方法,通过判断内存中地址前进的步长来判断是哪一种存储模式
int main(void)
{
	int a = 1;
	char* p = &a;
	printf("%p\n", p);
	printf("%p\n", p + 1);
	printf("%d\n", *p);
	printf("%d\n", *(p + 1));
	return 0;
}

//这个是优化完的代码
#include <stdio.h>
int check_sys()
{
	int i = 1;
	return (*(char *)&i);
}
int main()
{
	int ret = check_sys();
	if (ret == 1)
	{
		printf("小端\n");
	}
	else
	{
		printf("大端\n");
	}
	return 0;
}

 一些跟数据存储相关的练习题

1.
    #include <stdio.h>
    int main()
    {
        char a= -1;
        //10000000 00000000 00000000 00000001 - 原码
        //11111111 11111111 11111111 11111110 - 反码
        //11111111 11111111 11111111 11111111 - 补码
        //11111111 截断 - a
        
        //printf("a=%d",a);
        //有符号数
        //11111111 11111111 11111111 11111111 整形提升 - 补码
        //10000000 00000000 00000000 00000000 - 反码
        //10000000 00000000 00000000 00000001 - 原码
        //-1
        
        signed char b=-1;
        //10000000 00000000 00000000 00000001 - 原码
        //11111111 11111111 11111111 11111110 - 反码
        //11111111 11111111 11111111 11111111 - 补码
        //11111111 截断 - b
       
        //printf("b=%d",b);
        //有符号数
        //11111111 11111111 11111111 11111111 整形提升 - 补码
        //10000000 00000000 00000000 00000000 - 反码
        //10000000 00000000 00000000 00000001 - 原码
        //-1
        
        unsigned char c=-1;
        //10000000 00000000 00000000 00000001 - 原码
        //11111111 11111111 11111111 11111110 - 反码
        //11111111 11111111 11111111 11111111 - 补码
        //11111111 截断 - c
        //无符号数
        //00000000 00000000 00000000 11111111 整形提升 - 补码

        // %d 打印有符号的整形 打印的是原码
        printf("a=%d,b=%d,c=%d",a,b,c);//a = -1;b = -1;c = 255
        return 0;
    }
2.
    #include <stdio.h>
    int main()
    {
        char a = -128;
        //一个有符号的char类型的范围时-128~127
        //1000000 00000000 00000000 10000000 - 原码
        //1111111 11111111 11111111 01111111 - 反码
        //1111111 11111111 11111111 10000000 - 补码
        //1000000 - 截断
        //1111111 11111111 11111111 10000000 - 整形提升 - 补码
        printf("%u\n",a);//打印的结果为无符号数 - 4294967168
        return 0;
    }

3.
//结果与2一致
    #include <stdio.h>
    int main()
    {
        char a = 128;
        printf("%u\n",a);
        return 0;
    }
4.
    int i= -20;
    //10000000 00000000 00000000 00010100 - 原码
    //11111111 11111111 11111111 11101011
    //11111111 11111111 11111111 11101100 - 补码

    unsigned int j = 10;
    //00000000 00000000 00000000 00001010
    
    //11111111 11111111 11111111 11101100
    //00000000 00000000 00000000 00001010
    //11111111 11111111 11111111 11110110 i + j - 补码

    //11111111 11111111 11111111 11110110 i + j - 补码
    //10000000 00000000 00000000 00001001 
    //10000000 00000000 00000000 00001010 - 输出结果 - 原码
    printf("%d\n", i+j);
5.
    int main(void)
    {
    	unsigned int i;
	    for (i = 9; i >= 0; i--)
	    {
	    	printf("%u\n", i);
    	}

    	return 0;
    }
//程序当i的值到0之前的数值都是正常打印9 8 7 6 5 4 3 2 1 0
//当达到0时,进行下一步操作 0-1 ,我们将其看做 0 + (-1)
//结果为:
//00000000 00000000 00000000 00000000 - 0

//10000000 00000000 00000000 00000001 - -1 - 原码
//11111111 11111111 11111111 11111110
//11111111 11111111 11111111 11111111 - 补码

//00000000 00000000 00000000 00000000 - 0
//11111111 11111111 11111111 11111111 - 补码
//11111111 11111111 11111111 11111111 - 结果 - 打印无符号数
//4294967295

int main()
{
	char a[1000];
	int i;
	for (i = 0; i < 1000; i++)
	{
		a[i] = -1 - i;
	}
	printf("%d", strlen(a));
	return 0;
}
//stelen函数当遇到'\0'时会停止统计字符串的长度
//char类型的数据的范围是[-128~127]
//因此我们可以猜测a的长度应该为255

-128 + (-1)
//10000000 00000000 00000000 10000000 - 原码
//11111111 11111111 11111111 01111111
//11111111 11111111 11111111 10000000 - 补码
//10000000 截断 - a[i]
//11111111 11111111 11111111 10000000 整形提升

//11111111 11111111 11111111 11111111 - -1 - 补码

//11111111 11111111 11111111 10000000 - 补码 - -128
//11111111 11111111 11111111 11111111 - 补码 - -1
//00000000 00000000 00000000 01111111 - 补码 - 计算结果

//01111111 - a[i] - 截断

//00000000 00000000 00000000 01111111 - 补码 - 整形提升
//01111111 11111111 11111111 10000000
//00000000 00000000 00000000 01111111 - 原码 - 127
7.
    #include <stdio.h>
    unsigned char i = 0;
    int main()
    {
    for(i = 0;i<=255;i++)
    {
        printf("hello world\n");
    }
    return 0;
    }
//255 + 1
//00000000 00000000 00000000 11111111 - 255
//00000000 00000000 00000000 00000001 - 1

//00000000 00000000 00000001 00000000

//00000000 - 截断

//00000000 00000000 00000000 00000000 - 整形提升

关于浮点型在内存中的存储相关的内容,将在最近一段时间内继续补充完成。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值