C语言进阶--数据的存储

1.数据类型介绍

内置类型

char  //字符数据类型  1字节
short  //短整型      2字节
int  //整型          4字节
long  //长整型       4/8字节
long long  //更长的整型  8字节   (C99中引入的)
float  //单精度浮点数    4字节
double  //双精度浮点数   8字节


sizeof(long) >= sizeof(int)
4(32位平台)/8(64位平台)  >=  4   字节

类型的意义

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

1.1类型的基本归类

整型家族

char    (字符的本质是ASCII码值,属于整型,所以划分到整型家族)  
        (char 类型 到底是signed char 还是unsigned char ,在C语言标准中是未定义的,取决于编译器的实现) —— char 比较特殊
    unsigned char
    signed char

short
    unsigned short [int]
    signed short [int]

int     (int a; ————> 这里的int类型默认指的是signed int) 
    unsigned int
    signed int
    
long
    unsigned long [int]
    signed long [int]
    
long long
    unsigned long long [int] 
    signed long long [int]

生活中有些数据是没有负数的,比如身高,体重,长度等。 ——> unsigned

但有些数据是有负数的,比如温度。——> signed

浮点型家族

float
double

只要是表示小数就可以使用浮点型。

float的精度低,存储的数值范围较小;double的精度高,存储的数值范围更大。

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

数组类型
结构体类型 struct
枚举类型 enum
联合类型 union    
//数组类型
int arr1[5]; //数组类型:int [5]
int arr2[8]; //数组类型:int [8]
char arr3[5]; //数组类型:char [5]

指针类型

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

空类型

void 表示空类型(无类型)
通常应用于函数的返回类型、函数的参数、指针类型    
void test(void) //第一个void 表示函数没有返回值;第二个void 表示函数不需要传任何参数
{
    
}

2.整型在内存中的存储

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

//数值有不同的表示形式,2进制,8进制,10进制,16进制
//eg:21
//0b10101 —— 025 —— 21 —— 0x15

2.1原码、反码、补码

整数的2进制表示也有3种表示形式:原码、反码、补码。

1.正的整数,原码、反码、补码相同。

2.负的整数,原码、反码、补码是需要计算的。

原码(直接通过正负的形式写出的二进制序列)

反码(原码的符号位不变,其它位按位取反)

补码(反码+1)

int main()
{
    int a = 20;
    //00000000 00000000 00000000 00010100 - 原码(二进制)
    //0x00 00 00 14 - 原码、反码、补码(十六进制)
    //00000000 00000000 00000000 00010100 - 反码(二进制)
    //00000000 00000000 00000000 00010100 - 补码(二进制)
    
    int b = -10;
    //10000000 00000000 00000000 00001010 - 原码(二进制)
    //0x80 00 00 0a - 原码(十六进制)
    //11111111 11111111 11111111 11110101 - 反码(二进制)
    //0xff ff ff f5 - 反码(十六进制)
    //11111111 11111111 11111111 11110110 - 补码(二进制)   
    //0xff ff ff f6 - 补码(十六进制)
    
    return 0;
}

在这里插入图片描述
整数在内存中存放的是补码的二进制序列。

为什么呢?

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

2.2大小端介绍

在这里插入图片描述
大端【字节序】存储:把一个数据的高位字节序的内容放在低地址处,把低位字节序的内容放在高地址处。

小端【字节序】存储:把一个数据的高位字节序的内容放在高地址处,把低位字节序的内容放在低地址处。

我们常用的X86结构是小端模式,而KEIL C51为大端模式。很多的ARM,DSP都为小端模式。有些ARM处理器还可以由硬件来选择大端模式还是小端模式。

//判断大小端
#include <stdio.h>

int main()
{
    int a = 1;
    if (*(char*)&a == 1)
    {
        printf("小端\n");
    }
    else
    {
        printf("大端\n");
    }
    
    return 0;
}
//判断大小端(封装成函数)
#include <stdio.h>
int check_sys()
{
    int a = 1;
    if (*(char*)&a == 1)
    {
        return 1;
    }
    else
    {
        return 0;
    }
}
int main()
{
    int ret = check_sys();
    if (ret == 1)
        printf("小端\n");
    else
        printf("大端\n");
    
    return 0;
}
//判断大小端(封装成函数,简洁版)
#include <stdio.h>
int check_sys()
{
    int a = 1;
    return *(char*)&a;
}
int main()
{
    int ret = check_sys();
    if (ret == 1)
        printf("小端\n");
    else
        printf("大端\n");
    
    return 0;
}

3.浮点型在内存中的存储

常见的浮点数:

3.14159

1E10(1.0*e的10次方,科学计数法)

浮点数家族:float、double、long double(C99中引入)类型。

浮点数表示的范围:float.h中定义。

整数表示的范围:limits.h中定义。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

3.1浮点数存储序列

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

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

  • (-1)^S【-1的S次方】表示符号位。当S=0,V为正数;当S=1,V为负数。
  • M表示有效数字。大于等于1,小于2.
  • 2^E【2的E次方】表示指数位。

IEEE 754规定:

对于32位的浮点数,最高的1位是符号位S,接着的8位是指数E,剩下的32位是有效数字M。

在这里插入图片描述
对于64位的浮点数,最高的1位是符号位S,接着的11位是指数E,剩下的52位是有效数字M。

在这里插入图片描述
IEEE 754对有效数字M和指数E,还有一些特别的规定:

前面说过,1<=M<2,也就是说,M可以写成1.xxxx的形式,其中xxxx表示小数部分。

IEEE 754规定,在计算机内部保存M时,默认这个数的第一位总是1,因此可以被舍去,只保存后面的xxxx部分。比如保存1.01时,只保存01,等到读取的时候,再把第一位的1加上去。这样做的目的:节省1位有效数字。

至于指数E,情况比较复杂。

首先,E为一个无符号整数(unsigned int),这意味着,如果E为8位,它的取值范围是0-255;如果E为11位,它的取值范围是0-2047。但是,我们知道,科学计数法中的E是可以出现负数的。所以IEEE 754规定,存入内存时E的真实值必须再加上一个中间数。对于8位的E,这个中间数是127;对于11位的E,这个中间数是1023。

int main()
{
    float f = 5.5f;
    //101.1 - 二进制
    //1.011*2^2 - 科学计数法
    //S=0,M=1.011,E=2(存储时+中间值127=129)
    //0 10000001 011(后面不足23bit直接补0)00000000000000000000
    //01000000101100000000000000000000
    //0100 0000 1011 0000 0000 0000 0000 0000(写出16进制)
    //0x40 b0 00 00
    return 0;
}

在这里插入图片描述
然后,指数E从内存这种取出还可以分成三种情况:

E不全为0或不全为1

这时,浮点数就采用下面的规则表示:即指数E的计算值减去127(或1023),得到真实值,再将有效数字M前加上第一位的1。

比如:

0 10000001 01100000000000000000000
(-1)^0 * 1.01100000000000000000000 * 2^2129-127=2

E全为0

这时,浮点数的指数E等于1-127(或者1-1023),即位真实值。有效数字M也不再加上第一位的1,而是还原为0.xxxx的小数。这样做是为了表示正负0,以及接近于0的很小的数字。

E全为1

这时,如果有效数字M全为0,表示正负无穷大(正负取决于符号位S)。

作业1:输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得数组中所有的奇数位于数组的前半部分,所有偶数位于数组的后半部分。

//输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得数组中所有的奇数位于数组的前半部分,所有偶数位于数组的后半部分。

#include <stdio.h>

void move_odd_even(int arr[], int sz)
{
    int left = 0;
    int right = sz - 1;
    while (left < right)
    {
        //从左向右找一个偶数,遇到奇数停下来
    	while ((left < right)&& (arr[left] % 2 == 1)) //可能存在越界,加上条件(left < right)
    	{
        	left++;
   	 	}
   	 	//从右向左找一个奇数,遇到偶数停下来
    	while ((left < right)&& (arr[right] % 2 == 0))
    	{
        	right--;
    	}
    	//交换奇数和偶数
    	if (left < right)
    	{
        	int tmp = arr[left];
        	arr[left] = arr[right];
        	arr[right] = tmp;
        	left++;
        	right--;
    	}
    }
}
int main()
{
    int arr[10] = {0};
    //输入
    int i = 0;
    int sz = sizeof(arr) / sizeof(arr[0]);
    for (i = 0; i < sz; i++)
    {
        //scanf("%d", &arr[i]); //等价于下面代码
        scanf("%d", arr+i);
    }
    //调整
    move_odd_even(arr, sz);
    //输出
    for (i = 0; i < sz; i++)
    {
        printf("%d ", arr[i]); 
    }
    return 0;
}

作业2:有序序列合并

#include <stdio.h>
int main()
{
    int n = 0;
    int m = 0;
    scanf("%d %d", &n, &m);
    int arr1[n]; //C99支持的变长数组
    int arr2[m];
    //输入n个整数
    int i = 0;
    for (i = 0; i < n; i++)
    {
        scanf("%d", &arr1[i]);
    }
    //输入m个整数
    for (i = 0; i < m; i++)
    {
        scanf("%d", &arr2[i]);
    }
    //合并打印
    int j = 0; //n数组下标
    int k = 0; //m数组下标
    while (j<n && k<m)
    {
        if (arr1[j] < arr2[k])
        {
            printf("%d ", arr1[j]);
            j++;
        }
        else
        {
            printf("%d ", arr2[k]);
            k++;
        }
    }
    if (j<n)
    {
        for (; j < n; j++)
        {
            printf("%d ", arr1[j]);
        }
    }
    else //k<m
    {
        for (; k < m; k++)
        {
            printf("%d ", arr2[k]);
        }
    }
    return 0;
}
//如果要存储在一个新数组里面
#include <stdio.h>
int main()
{
    int n = 0;
    int m = 0;
    scanf("%d %d", &n, &m);
    int arr1[n]; //C99支持的变长数组
    int arr2[m];
    
    int arr3[m+n];
        
    //输入n个整数
    int i = 0;
    for (i = 0; i < n; i++)
    {
        scanf("%d", &arr1[i]);
    }
    //输入m个整数
    for (i = 0; i < m; i++)
    {
        scanf("%d", &arr2[i]);
    }
    //合并打印
    int j = 0; //arr1数组下标
    int k = 0; //arr2数组下标
    
    int r = 0; //arr3数组下标
    
    while (j<n && k<m)
    {
        if (arr1[j] < arr2[k])
        {
            arr3[r++] = arr1[j];
            //printf("%d ", arr1[j]);
            j++;
        }
        else
        {
            arr3[r++] = arr2[k];
            //printf("%d ", arr2[k]);
            k++;
        }
    }
    if (j<n)
    {
        for (; j < n; j++)
        {
            arr3[r++] = arr1[j];
            //printf("%d ", arr1[j]);
        }
    }
    else //k<m
    {
        for (; k < m; k++)
        {
            arr3[r] = arr2[k];
            //printf("%d ", arr2[k]);
        }
    }
    
    //打印
    for (i = 0; i < m+n; i++)
    {
        printf("%d ", arr3[i]);
    }
    
    return 0;
}

总结

今天就暂且更新至此吧,期待下周再会。如有错误还请不吝赐教。希望对您学习有所帮助,翻页前留下你的支持,以防下次失踪了嗷。

作者更新不易,免费关注别手软。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

小汐睡着了

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

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

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

打赏作者

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

抵扣说明:

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

余额充值