IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 数据结构与算法 -> data_structural(数据结构)- 重置版 -> 正文阅读

[数据结构与算法]data_structural(数据结构)- 重置版

数据类型: 分为两类

1 .是 c 语言本身具有的类型 , 又称内置类型

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

?

2 .自定义类型(构造类型)

类型的意义:

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)

?

浮点型家族

float
double

?

构造类型

数组类型 array
结构体类型 struct
枚举类型 enum
联合类型 union

?

指针类型

整形指针 int* pi;
字符指针 char* pc;
浮点型指针float* pf;
无类型指针(万能指针) void* pv; 【只能接收,不能使用,非要使用的话,需要强制类型转化】

?

空类型

1 .void 表示空类型(无类型)

2 .应用于函数的返回类型,函数的参数,指针类型

实例

#include<stdio.h>
void test()// void 在这里 表示 函数 test 无返回值
{
    printf("hello!\n");
}

int main()
{ 
    test();
    return 0;
}

?

整形在内存中存储

原码 反码 补码

在计算机中的  整形(正,负)有符号数  有三种表示方法: 原码  反码  补码(以二进制码表示)
三种方法均有 符号位 和 数值位 两部分,符号位(二进制最高位)都是 0 表示正, 1 表示 负
而数值位三种表示方法各有不同

原码: 直接将 整形数据 按照 正负的形式 翻译成二进制就可以。

反码: 将原码的符号位不变,其他位依次按位取反就可以得到了

补码: 反码+1

注意 正整数的 原 反 补 三码都是一样的

?

实例(二进制位最高位,是符号位, 0 为正,1 为负)

vs 环境
#include<stdio.h>
int main()
{
    int a = 20; // 00 00 00 14    整数数据 的 表示 和 存储 都是 用 补码
    a 为正数,三码相同  
    //0000 0000 0000 0000 0000 0000 0001 0100  原码, a是正整数。所以三码相同
    //0000 0000 0000 0000 0000 0000 0001 0100  反码   原码的符号位不变,其他位按位取反
    //0000 0000 0000 0000 0000 0000 0001 0100  补码   反码加一,  整形数据存入(a)内存中的是补码
    //  0    0    0    0    0    0  1(16)   4  十六进制(逢16 进 1)
    // 存入内存之后,表示为 14 00 00 00 (小端存储模式:低位字节内容存储于低地址,高位字节内容存储于高位地址)

    int b = -10;// 0x ff ff ff f8   整数数据 的 表示 和 存储 都是 用 补码
    // 1000 0000 0000 0000 0000 0000 0000 1010 原码
    // 1111 1111 1111 1111 1111 1111 1111 0101 反码  原码的符号位不变,其他位按位取反
    // 1111 1111 1111 1111 1111 1111 1111 0110 补码  反码加一,  整形数据存入(b)内存中的是补码
    //  f     f    f    f   f     f    f    6  十六进制 0x ff ff ff f6
    //存入内存之后,表示为 f6 ff ff ff (小端存储模式:低位字节内容存储于低地址,高位字节内容存储于高位地址)

    return 0; 
}

在这里插入图片描述

在这里插入图片描述


?

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

例子

#include<stdio.h>
int main()
{
    1 - 1;
    // 上表达式 在 CPU 看来 就是 1 + ( -1 ) ,因为CPU只有加法器
    如果 我们 使用 原码,来进行计算,会如何?
    //00000000 00000000 00000000 00000001 // 1  原码
    //10000000 00000000 00000000 00000001 //  -1 原码
    //10000000 00000000 00000000 00000010 //  -2 原码相加,很明显 用 原码 进行运算是错的,1 - 1 != -2 ,

    //00000000 00000000 00000000 00000001 // 1  补码
    //11111111 11111111 11111111 11111111 // -1 补码
    //00000000 00000000 00000000 00000000/ /  0  两补码相加,很明显 用 补码 进行运算 是对的,1 - 1 == 0
    return 0;
}

?

整数 (整形数据 一律用补码来 表示 和 存储):

1 . 有符号数 :

正数 ( 原 反 补 三码相同 )
负数 ( 原码 -> 原码 符号位 不变,其余 按位取反 -> 反码 -> 加1 -> 补码[ )

?

2 . 无符号整数:

原 反 补 三码相同

?

大小端的概念

在这里插入图片描述
那可不可以用其它方式 存储呢?例如 11 33 22 44, 44 22 33 11等等…
答案是可以的。 只要能想到的存储方式都可以(但是请注意:怎么放进去的,怎么拿出来)


为了方便存入和读取数据
所以选取 的是 正着存(小端), 倒着存(大端)

另外十六进制数 11 22 33 44 每两个占一字节 也可以说 按照 字节的顺序 存储方式

即:
大端(储存)模式,是指 数据的 低位 保存在 内存 的 高地址 中,而数据的高位,保存在内存的低地址中
又称:大端字节序(储存) 模式


小端(储存)模式:是指 数据的 低位 保存在 内存 的 低地址 中,而数据的高位,保存在内存的高地址中
又称:小端字节序(储存)模式


?

大小端模式意义:

因为在计算机系统中,我们是以字节为单位的,每个地址单元都对应着一个字节,一个字节 8 bit。
但是C语言中除了 8 bit 的 char 之外,还有 16 bit 的 short,32 bit 的 long 型(要看具体的编译器)。
对于位数大于 8 位 的 处理器,例如 16位 或 32 位的处理器,由于寄存器宽度大于一个字节,那么必然存在着一个 多个字节安排 的 问题。
因此 大端 存储模式 和 小端 存储模式,就有存在的必要了

实例


#include<stdio.h>
int main()
{
    int a = 20; // 内存情况:14 00 00 00 (小端)
        //0000 0000 0000 0000 0000 0000 0001 0100  原码, a是正整数。所以三码相同
        //0000 0000 0000 0000 0000 0000 0001 0100  反码
        //0000 0000 0000 0000 0000 0000 0001 0100  补码  存入a的是补码
        //  0    0    0    0    0    0  1(16)   4  十六进制  0x 00 00 00 14

    return 0;
}

?

案例1(写一段代码 告诉我们当前机器的字节序是什么):

写法 1

vs 环境
#include<stdio.h>
int main()
{
    int a = 1;// 01 00 00 00 (小端存储模式)
    char* p = (char*)&a; // 将 a是整形,取出其地址的类型也是 int。想要存入 字符指针变量 p 中,需要 将 a 的 地址转换成 字符类型(地址的值是不变的)
    if (*p == 1) 
  因为 指针变量p 是 字符类型的,对其解引用,只能访问,p 存的地址 所指向的 空间中 一个字节 的 内容。(访问 是从 低~高位地址开始访问的,
  所以第一个访问到 低位第一个数据( 1 byte 的内容【01】,因为需要和 1  进行比较,所以需要整形提升,根据最高位进行补位【最高位为0,补零,直至32bit位,即 00 00 00 01 】,其值转换成十进制数,肯定是等于1的)
    
    {            只要等于1,就说明 该编译器的存储模式为 小端存储模式(小端字节序存储模式)
        printf("小端\n");//所以输出这条语句
    }
    else
    {    如果不等于 1,那肯定就是 大端存储模式了
        printf("大端\n");
    }
    return 0;
}


?

写法2

#include<stdio.h>
int check_sys()
{
     写法1
        int a = 1;                
        return *(char*)&a;       
     写法2
      //char* p = (char*)&a; 
      //return *p;
      
          返回 1 小端
         返回 0  大端
}
指针类型决定了指针解引用操作符一次能访问多少个字节,char* p ; *p可以访问一个字节 ,int*p;*p可以访问 4 个字节

int main()
{

    int ret = check_sys();
    返回 1 小端
    返回 0 大端
    if (ret == 1)
    {
        printf("小端\n");
    }
    else
    {
        printf("大端\n");
    }
    return 0;
}

在这里插入图片描述


?

例题 1

vs 环境
#include<stdio.h>
int main()
{
    char a = -1;
    100000000000000000000000 00000001  -1 的原码
    111111111111111111111111 11111110   -1 的反码
    111111111111111111111111 11111111  -1 的 补码
    // a 为 有符号字符类型数据;存的是补码第8位(数据截断,截取低位数据 是因为 是小端存储模式);‘’
        //打印 a 之前需要整形提升按照符号位补充 根据最高位补位,最高位是 1 补位 就是 32个1,就是 -1 的 补码,
    //打印的是原码,所以需要转换成原码, 即 : 10000000 00000000 00000000 00000001  
    // a 输出的是 -1

 
    signed char b = -1; //有符号;存的补码8个1(截断);//打印之前需要整形提升(算术逻辑)按照符号位补充 ,
        //即 32 个 1.打印的是原码 10000000 00000000 00000000 00000001 即 -1
        

    unsigned char c = -1;// 无符号数 说明无符号数进行整形提升直接补0, 而 -1 的补码位8个1(截断 );
    //将其打印之前需要整形提升:00000000 00000000 00000000 11111111将其转化为十进制 即 255
    
    printf("a = %d,b = %d,c = %d ", a, b, c);// -1,-1,255
   

    
    return 0;
}

?

例题 2

#include<stdio.h>
int main()
{
    char a = -128;
    //10000000 00000000 00000000 1000 0000; 原码
    //11111111 11111111 11111111 0111 1111  反码
    //11111111 11111111 11111111 1000 0000  补码
    // 1000 0000 截断

    printf("%u\n",a);//打印之前,整形提升(有符号数,根据最高位【1】进行补位):11111111 11111111 11111111 1000 0000  
    // %u :打印无符号,所以三码相同,直接 将其 二进制转换为 十进制数,进行输出(且 结果恒大于0【非负数】)
    // 输出为 4294967168
    return 0;
}

?

例题 3

 有符号和无符号相加
#include<stdio.h>
int main()
{
    int i = -20;
    //10000000 00000000 00000000 0001 0100  原码
    //11111111 11111111 11111110 1110 1011  反码
    //11111111 11111111 11111111 1110 1100 补码 
    unsigned int j = 10;// 无符号数 三码相同
    //00000000 00000000 00000000 0000 1010 原 反 补 码 三码相同
    
    //00000000 00000000 00000000 0000 1010   j 的 补码
                    +
    //11111111 11111111 11111111 1110 1100   i 的 补码 
                    ==
    //11111111 11111111 11111111 1111 0110   结果(补码)
    //10000000 00000000 00000000 0000 1001 结果(反码)【补码除了符号位,其余位取反】
    //10000000 00000000 00000000 0000 1010 结果(原码) 【补码+1】
     上下两者方法皆可
      11111111 11111111 11111111 1111 0101  反码  ==  补码-110000000 00000000 00000000 0000 1010  原码  ==  反码【除符号位,其余位 按位取反】
     
    printf("%d\n",i+j);// 打印是打印原码
    10000000 00000000 00000000 0000 1010 转化成时间至数 就是 10
    return 0;
}

?

例题 4

 
#include<stdio.h>
int main()
{
    unsigned int i;
    for (i = 9; i >= 0; i--)
    {
        printf("%u\n", i);// 死循环 i为无符号数,所以 i 永远 不可能 小于 0
    }
    return 0;
}

?

例题 5

#include<stdio.h>
int main()
{
    char a[1000];
    int i;
    for (i = 0; i < 1000; i++)
    {
        a[i] = -1 - i;
    }
    printf("%d", strlen(a));
  因为数据是char类型,有符号范围 -128~127,但是有很多数比 255 要大的数据,造成值大了放不下
    所以要把这1000个数 转化为 -128~127,才能放进 字符数组当中
就是说 第128个元素是 -128,第129个元素是 -127以此类推,第 256个元素 就是0,第257个元素是1,按照这种模式,不停的循环赋值,直到赋完1000个元素的值

    strlen 计算 元素个数,照理来说 是 一千个元素,但是由于 char 的取值特性,它的值当中有一个 10,'\0'的是ASCII码值 就是 0
    ,所以 他计算的值 不肯能是 1000 个
     详情见下方附图 ,它是顺时针转, 等它转到0 == '\0'时,停下。即 第256个元素 0 == '\0' 会停止计数(strlen 遇到'\0',停止计数,'\0'不被计入),即输出 255【strlen 只计算到255个元素就停止计数了】
    return 0;
}

附图

在这里插入图片描述


?

例题 6

#include<stdio.h>
unsigned char i = 0;// 无符号char 范围 0~255
int main()
{
    for (i = 0; i <= 255;i++)//恒满足,所以死循环, 与 案例5的性质 是一样的 第256个元素为 0,然后开始递增,循此往复。
    {
        printf("hello world!\n");
    }
    return 0;
}

在这里插入图片描述


?

浮点型在内存中的存储

程序一:

#include<stdio.h>
int  main()
{
    double d = 1E10;//  1E10  : 1 乘以 10 的 10 次方
    printf("%lf\n", d); //10000000000.000000
    return 0;
}

?

程序二:

#include<stdio.h>
int  main()
{
    int n = 9;
    float* pfloat = (float*)&n;//强制转化类型
    printf("n的值为:%d\n",n);//n的值为:9
    printf("pfloat的值:%f\n",*pfloat);//pfloat的值:0.000000  小数点后面默认6个0
    整形存入,整形拿出,没问题;浮点型拿出就有问题

    *pfloat = 9.0;
    printf("n的值为:%d\n", n);// n的值为:1091567616
    printf("pfloat的值:%f\n", *pfloat);//pfloat的值:9.000000
    浮点型存入,浮点型拿出,没问题;整形拿出就有问题

    由上表达式 得出结论: 整形与浮点型 在内存中存储的方式不同

    return 0;
}
n 和 *pfloat 是同一个数,为什么浮点数和整数的解读结果会差别这么大?
 所以一定要搞懂浮点数在计算机内部的表示方法。

根据国际标准 IEEE(电气和电子工程协会) 754(IEEE 754这是一个标准,规定浮点型在内存中如何存储),任何一个二进制浮点数 V 可以表示下面的形式:

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


?

例子

在这里插入图片描述


?

IEEE 754规定: 对于 32位 的浮点数,最高的 1 位 是 符号位 S,接着的8位是指数E,剩下的 23 位为 有效数字M

在这里插入图片描述


?

IEEE 754 对 有效数字 M 和 指数 E ,还有一些特别规定,前面说过, 1 <= M < 2,也就是说,M可以写成1.xxxxxx的形式(其中 xxxxxx 表示小数部分)


IEEE 754规定,在计算机内部保存 M 时,默认这个数的第一位总是 1 ,因此可以被舍去

只保存后面的 xxxxxx 部分:比如 保存 1.01,只保存 01。等到读取的时候,再把第一位的 1 加上去。
这样 可以提升一位的精度,对于 32位 来说,等于可以保存 24 位 有效数字

指数E

首先,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,即 1000 1001

?

再来看一个小点

 小数 0.5  = 0.1 (二进制表示形式) 0 -> 2^0 (1*0 == 0);  1 -> 2 ^ (-1) 等于 (1 * 1/2)  = 0.5

在这里插入图片描述
因为 有效数字(M),要大于等于1,小于2【1 <= M < 2】,
所以: 1 <= M < 2 所以 0.1 的 小数点 右移一位,即 1.0 ^ (-1)

故 (-1) ^ S * M * 2 ^ E == (-1) * 0 * 1.0 * 2 ^ (-1)

 S  == 0
 M == 1.0   
 E == -1 
存入内存时 E  ,必须 真实值(-1)加上一个中间数(127)。  对于 8 位的 E,这个 中间数 是 127
所以 -1 + 127 = 126  也就是真正存入 E 的值(存储值)是 126 (转化二进制) 0111 1110,
如果想知道 E 的 真实值 就减去 中间值127。 即  126 - 127 = -1

?

例题
#include<stdio.h>
int main()
{
    float f = 5.5;
    // 将 浮点数 5.5 转换成 二进制浮点数, 即: 101.1,又因为 1 <= M < 2,小数点向左移动 2 位,故 1.011 * 2^2 
        因为 5.5 > 0,所以 S == 0
    表达式最终为  (-1) ^ 0 *1.011 * 2^2
      S == 0
      M == 1.011  // IEEE 754规定,在计算机内部保存 M 时,默认这个数的第一位总是 1 ,因此可以被舍去,只保存小数点后面的 011 部分
       E == 2  -> 2+127 = 129 -> 10000001

                             单精度浮点数存储模型

            S           EEEEEEEE             MMMMMMMM MMMMMMMM MMMM MMM
       S ( 1 bit)      E( 8 bit)                  M ( 23 bit )
          0            10000001              01100000 00000000 0000 000// 011 后面直接补零
          
     0100 0000 1011 0000 0000 0000 0000 0000
       4   0   b(11)  0    0    0    0   00x 40 b0 00 00  十六进制数
     内存形式 00 00 b0 40(小端)
    return 0;
}

?

指数 E 从内存中取出还可以分三种情况:

1. E 不全为0 或 不全为1
这时,浮点数就采用下面的规则表示,即指数 E 的计算值(二进制数转化十进制数) 减去 127(32位)/ 1023(64位) 得到真实值。
再将有效数字 M 前加上一位的 1 ,比如 0.5( 1/2 )的二进制形式为  1. 0* 2^(-1),由于规定正数部分必须为 1.

即将小数点右移1位,即为 1.0 * 2^(-1),其 阶码(E) 为 -1 + 127 = 126,转化为二进制数为 0111 1110
而 1.0 去掉整数部分还剩一个  0 ,(后补)补齐 0 到 23 位 00000000 00000000 0000 000

则其二进制表示形式为
 0 01111110 00000000000000000000000

?

2 . E全为0 —0 【 00000000 (E)】 01100000000000000000000 等价于 (正负) ± 0.011 * 2 ^(-126) 正负无限接近 0 的数字【正负 是因为 (-1)的S方】
 当 E 全为 0 时,浮点数的指数 E 等于   1-127【-126】   (或者   1-1023 【-1022】  )即为  E 的 存储值,
 有效数字 M   不在加上    第一位  的   1(因为加上了,等于没加,一个 1点几 的数,乘以 2 的 126次方之一【2^-126】,它是一个无限接近0的数)
直接  0.011 * 2^(-126),还原出来的数字,是无限接近于 0 的数字,加上正负,就是正负无穷接近于 0 的数字

而是还原为 0. xx xx xx 的小数(保留 小数点后六位),这样做是为了表示 +-(正负) 0,以及接近于 0 的很小的数字。

?

3 . E 全为 1 ---- 0 { 111111111 } 01100000000000000000000
                     E 的存储值为 255,【E+ 127 = 255】 
                     E 的真实值  255 - 127 == 128
                       (正负+-)1,xxx  *  2 ^ 128  
 这时,如果 有效数字M 全为 0(1,0 * 2^128),表示+-(正负)无穷大的数字(正负 取决于 符号位 【(-1)^S 】 )

?

实例
#include<stdio.h>
int  main()
{
    int n = 9;
    // 00000000 00000000 00000000 0001001  补码
    float* pfloat = (float*)&n;//强制转化类型
    printf("n的值为:%d\n",n);//n的值为:9
    printf("pfloat的值:%f\n",*pfloat);//pfloat的值:0.000000  小数点后面默认6个0
   
                           单精度浮点数存储模型

            S           EEEEEEEE             MMMMMMMM MMMMMMMM MMMM MMM
       S ( 1 bit)      E( 8 bit)                  M ( 23 bit )
 9 的 补码为  0          00000000               0000000000000000001001  
符合上述情况 2  E全为零
    // 即 0.000000 0000000000001001 * 2 ^ -126   是一个正负无限接近于 0 的数字,浮点型数据 默认 保留 小数点后面 6 位

    *pfloat = 9.0;
    // 1001.0
    //-1 ^ 0 * 1.001 * 2 ^ 3   E= 3 + 127 =130【10000010】
    // 0 10000010 001 00000000000000000000
    //正数 2 ^ 24 +  2 ^ 20 + 2 ^ 30  == 1,091,567,616
    printf("n的值为:%d\n", n);// n的值为:1091567616
    printf("pfloat的值:%f\n", *pfloat);//pfloat的值:9.000000
    return 0;
 }

浮点型与 整形 互相转换流程

在这里插入图片描述

本文结束

  数据结构与算法 最新文章
【力扣106】 从中序与后续遍历序列构造二叉
leetcode 322 零钱兑换
哈希的应用:海量数据处理
动态规划|最短Hamilton路径
华为机试_HJ41 称砝码【中等】【menset】【
【C与数据结构】——寒假提高每日练习Day1
基础算法——堆排序
2023王道数据结构线性表--单链表课后习题部
LeetCode 之 反转链表的一部分
【题解】lintcode必刷50题<有效的括号序列
上一篇文章      下一篇文章      查看所有文章
加:2021-10-04 13:05:02  更:2021-10-04 13:05:08 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/17 12:32:23-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码