C语言-3

news2025/1/19 2:40:50

定义指针

/*
    指针的概念:
        1.为了方便访问内存中的内容,给每一个内存单元,进行编号,
        那么我们称这个编号为地址,也就是指针。

        2.指针也是一种数据类型,指针变量有自己的内存,
        里面存储的是地址,也就是那些编号。

    四要素
        1.指针本身的类型    例如:float*    int*    ...
        2.指针指向的类型    例如:float        int        ...
        3.指针本身的内存
        4.指针指向的内存

    运算符
        *:
        1.定义指针时,通过 * 符号,来表示定义的是一个指针,并且是指针自身的类型的组成部分
        2.其他时候,表示解析引用(取内容:通过内存编号,读取内存中的内容)

        &:取(首)地址符,作用:取出(内存的)首地址
*/

# include <stdio.h>

int main()
{
    // 定义指针(指针:pointer)
    float* p_name;    // 指针本身的类型:float*    指针指向的类型:float
    int * p1;
    int *p2;        // * 符号偏不偏移不影响其功能

    return 0;
}

指针初始化与赋值

/*
    知识储备:
        // 初识化:定义的同时,给值
        int a = 0;

        // 赋值:先定义,再给值
        int b;
        b = 0;
*/

# include <stdio.h>

int main()
{    
    // 初始化
    int num = 6;
    int val = 8;
    
    // 初始化
    int* p1 = &num;    // 对于指针变量p1,如果进行给值,必须给地址(内存编号)

    // 赋值
    int* p2;
    p2 = &val;


    // 自行体会
    int* p3 = p1;
    

    // 直接存入地址(不推荐使用,因为你不知道自己随便写的地址里面是什么!!)
    int* p4 = (int*)123456;    // 将 整型123456 强转为 int*类型 的“地址”
    int* p5 = (int*)0XAB25;    // 计算机中的内存地址通常用16进制数表示


    // 直接使用地址:置空(即:"使用0地址,NULL:0X0")
    int* p6 = NULL;    // 等价于 int* p6 = (int*)0X0;
    // 目的:为了给暂无指向的指针,提供指向,保证安全,将内存中的0地址特殊化


    // 数组名就是数组的首地址
    int arr[3] = { 1, 2, 3 };
    // 数组类型:int [3]
    // 元素类型:int
    // arr 类型:int*
    int* p7 = arr;


    return 0;
}

探究内存

/*
    1.变量在内存中所占的字节数
    所有的指针变量,不论类型,在内存中所占的字节数都是一样的,都是4个字节(或者8个字节)
    (8个字节是因为时代的发展,部分好的计算机性能得到提升,一般都是4个字节)

    2.指针本身的内存,以及指针指向的内存
    指针本身的内存:4个字节(指针变量只需要存储,所指向的变量的首地址)
    指针指向的内存:看你所指向的类型,视情况而定
*/

# include <stdio.h>

int main()
{    
    double num = 12.0;
    double* p1 = &num;
    printf("%f \n", num);
    printf("%f \n", *p1);    // 利用指针,取得 num 的值

    return 0;
}

在这里插入图片描述

指针的偏移

在这里插入图片描述

  • 大端模式与小端模式:前往学习

内存区域划分-基础

/*
内存区域的划分
四个:常量区,栈区,堆区,静态全局区
五个:常量区,栈区,堆区,静态全局区,代码区

1.代码区:存代码
2.常量区:存常量
3.静态全局区:静态(static)变量,全局变量
4.栈区:普通局部变量
5.堆区:由程序员手动申请,手动释放
*/

# include <stdio.h>

int a;                // 普通“全局”变量(初识值默认为零)
// 作用域:当前项目
// 生命周期:程序开始到结束

static int b;        // 静态“全局”变量(初识值默认为零)
// 作用域:当前文件
// 生命周期:程序开始到结束

int main()
{    
    int c;            // 普通局部变量(无初始值)
    // 作用域:当前语块
    // 生命周期:当前语块

    static int d;    // “静态”局部变量(初识值默认为零)
    // 作用域:当前语块
    // 生命周期:程序开始到结束

    return 0;
}
# include <stdio.h>

void func()
{
    static int num;        // 只会定义一次

    printf("%d \n", num);
    num++;
    printf("%d \n", num);

}

int main()
{    
    func();
    func();
    func();

    return 0;
} 

/*
    运行结果:
        0
        1
        1
        2
        2
        3
        请按任意键继续. . .
*/

空类型指针

/*
    void* 指针
    1.不能自增自减
    2.不能偏移
    3.不能读取内容
    但是!可以接收任何类型的指针而不需要强转类型
    可以利用这个特点,将 void* 指针当作通用的存放地址的“容器”
    e.g.
        int a = 6,b = 8.8;

        int* p1 = &a;
        double* p2 = &b;

        void* p0 = NULL;    // 当作存放“内存地址”的容器使用
        p0 = p1;
        p0 = p2;
        ...

*/

# include <stdio.h>

int main()
{    
    void* p0 = NULL;

    return 0;
}

简单开辟内存

/*
    简单开辟内存
        1.申请
        ---有两个函数能够实现申请内存的功能:
            A. malloc(参数:需要字节的总数);
            B. calloc(参数:每个需要的字节数,个数);
            返回值都是 void* 类型的指针
        2.使用
        3.释放
            free(参数:首地址)
            如果不释放的话,会导致“内存泄露”
        4.置空
            如果不置空的话,会出现“野指针”
*/

# include <stdio.h>

int main()
{    
    /* malloc */
    double* p = (double*)malloc(sizeof(double));    // 申请一个double类型大小的内存(8字节)
    *p = 3.14;    // 使用
    printf("%lf \n", *p);
    free(p);    // 通过 p 里面存储的首地址,找到相对应的内存,从这里开始释放,一直释放到,申请内存的时候,做了标记的地方
    p = NULL;    // 通过置空,让指针不再指向已经被释放掉的内存


    /* calloc */
    float* p1 = (float*)calloc(sizeof(float),1);
    *p1 = 3.14f;
    printf("%f \n", *p1);
    free(p1);
    p1 = NULL;


    printf("进阶运用 \n");


    // 进阶运用
    p = (double*)malloc(sizeof(double)*10);    // 申请10个 double 类型大小的连续的内存(补充:因为上面将p定为 double* 而且置空过了,所以可再度利用)
    for (int i = 0; i < 10; i++ )
    {    
        *(p + i) = 10 + i;    // 给值
        printf("%lf \n", *(p + i));    // 展示值
    }
    free(p);
    p = NULL;
    /*
        对于上面 for 循环部分的补充:
        p:里面存的是:申请的内存的首地址
        在一次申请中,申请的内存是连续的
        *(p + i) <===> p[i]        // 注意!它不是数组!
    */
    
    return 0;
}
# include <stdio.h>

int main()
{    
    // 指针布局
    int row = 3;
    int** pp = (int**)calloc(sizeof(int*), row);

    int len = 4;
    for (size_t i = 0; i < row; i++)    // size_t是什么?点我跳转学习
    {
        pp[i] = (int*)calloc(sizeof(int), len);
    }

    // 内容展示
    for (size_t i = 0; i < row; i++)
    {
        for (size_t j = 0; j < len; j++)
        {    
            pp[i][j] = i * 10 + j;    // 给值
            printf("%-5d", pp[i][j]);    // 展示值,注意!这里不是二维数组!(看不懂请回顾上页内容)
        }
        printf("\n");
    }

    // 释放内存
    for (size_t i = 0; i < row; i++)
    {
        free(pp[i]);
        pp[i] = NULL;
    }
    free(pp);
    pp = NULL;
    
    
    return 0;
}

自动扩容

# include <stdio.h>

int main()
{    
    int len = 5;    // 默认长度
    int* p = (int*)calloc(sizeof(int), len);
    
    int num = 1;
    for (size_t i = 0; num != 0; i++)    // 用户不输入0结束,就一直获取数据并复制到开辟的内存中
    {
        scanf("%d", &num);
        p[i] = num;        // 数据复制到开辟的内存中
    }
    for (size_t i = 0; p[i] != 0; i++)
    {
        printf("%-5d", p[i]);    // 展示数据
    }
    free(p);
    p = NULL;
    
    return 0;
}
/*
    扩容的本质是:
        将小内存中的所有内容拷贝到大内存中,然后,再继续对大内存进行别的操作
*/

# include <stdio.h>

int main()
{    
    // 长度
    int len = 5;
    // 首次申请内存
    int* p = (int*)calloc(sizeof(int), len);
    int* temp = p;    // 成为p的分身,以防万一

    // 重复输入数据(并复制到内存中)
    int num = 1;
    int i = 0;
    while (scanf("%d", &num), num != 0)
    {
        if (i < len)    // 没满的情况下
        {
            temp[i++] = num;    // 存完一次,记录一下
        }
        else   // 满了的情况下
        {
            len += 5;
            p = (int*)calloc(sizeof(int), len);        // 重新申请更大的内存
            for (int j = 0; j < i; j++)
            {
                p[j] = temp[j];
            }
            free(temp);
            temp = NULL;
            temp = p;    // 继续成为当前p的分身
            temp[i++] = num;
        }
        
    }
    // 输出数据
    printf("--------------------\n");
    for (int j = 0; j != i; j++)
    {
        printf("%d \n", temp[j]);
    }

    free(p);
    p = NULL;
    temp = NULL;
    
    return 0;
}

内存区域划分-进阶

/*
    内存区域的划分

    1.代码区
        存储代码


    2.常量区
        存储常量


    3.全局区(静态全局区)
        存储: 1.静态变量    2.全局变量

        # include <stdio.h>

        int c;            // 普通全局变量
        static int d;    // 静态全局变量

        int main()
        {
        int a;            // 普通局部变量
        static int b;    // 静态局部变量

        int c;    // 注意这个c不是上面的c,它们只是名字看起来一样而已

        a = 10;        // 普通局部变量没有默认初始值,所以需要自己赋值
        printf("a = %d \n", a);
        printf("b = %d \n", b);
        printf("c = %d \n", c);
        printf("d = %d \n", d);
        // 通过以上 printf,可以总结规律:静态全局区,默认的初始值为0


        // 作用域和生命周期

        作用域            生命周期
        普通全局变量        当前项目        程序开始到程序结束
        静态全局变量        当前文件        程序开始到程序结束
        普通局部变量        当前语块        当前语块
        静态局部变量        当前语块        程序开始到程序结束

        return 0;
        }

        
    4.栈区
        存储:普通局部变量
        从定义时系统自动分配内存,离开当前语块系统就会自动回收内存 


    5.堆区
        由程序员手动申请和释放
*/

指针与函数

/*
    1.指针函数
        返回值类型是指针的函数
    2.函数指针
        指向函数的指针
*/
// 1.指针函数


// 下面的代码是一个错误的例子,你能发现它的错误吗?
# include <stdio.h>

int* test();    // 声明

int main()
{    
    int* temp = test();
    printf("%d \n", *temp);
    return 0;
} 

int* test()
{
    int num = 10;
    int* p = &num;
    return p;    // 返回了栈区变量的首地址(非常严重的问题!详情见下)
}

/*
    下面的内容是在栈区,当运行完毕,系统会回收其内存资源:
        int* test()
        {
            int num = 10;
            int* p = &num;
            return p;
        }
    当函数返回栈区变量 num 的内存地址之后,
    函数运行完毕,系统回收 num 内存,供以后"某某东西"使用
    所以,返回的地址不但没有作用,还会导致以后非法访问内存的问题出现

*/
// 2.函数指针

/*
    函数指针的定义
    返回类型说明符 (*函数指针变量名)(参数列表);
*/

# include <stdio.h>

int func();    // 声明

int main()
{    
    // 定义函数指针,并进行初始化
    int(*p)() = func;    // 即:定义了指针p,而且 p 等于 func(func里面存的是函数的首地址)
    func();
    p();
    return 0;
}

int func()
{
    printf("成功执行了 func 函数!\n");
    return 6;
}
// 函数指针,知识扩展1

# include <stdio.h>

int func();    // 声明

typedef int funcType();    // 将 int...() 取别名为 funcType

int main()
{    
    funcType* p = func;
    p();
    return 0;
}

int func()
{
    printf("成功执行了 func 函数!\n");
    return 6;
}
// 函数指针,知识扩展2

# include <stdio.h>

int func(int a, int b);    // 声明

typedef int(*pfunc)(int a, int b);

int main()
{    
    pfunc p = func;        // 这样也能定义函数指针
    int res = p(1,2);
    printf("%d \n", res);

    return 0;
}

int func(int a, int b)
{
    printf("成功执行了 func 函数!\n");
    return a + b;
}

指针与数组

/*
    1.指针数组
        ......

    2.数组指针
        ......
*/
// 1.指针数组


/*

# include <stdio.h>

int main()
{    
    // 定义并初始化"数组"
    int arr1[3] = { 1, 2, 3 };    // 数组 arr1[3] -> 里面存的都是 int 类型


    // 定义并初始化"指针数组"
    int* arr2[3] = { 地址1,地址2,地址三 };    // 数组 arr2[3] -> 里面存的都是 int* 类型
    arr2[2] = 新地址;

    return 0;
} 

*/
// 2.数组指针

/*
    定义"数组"指针:
        所指向的数组里面存的数据类型 (*数组指针名称)[所指向的数组的长度];
*/

# include <stdio.h>

int main()
{    
    int arr[3] = { 1, 2, 3 };    // 建立一个数组。
//    arr 里面存的是数组内存的首地址,而 [] 表示内存里面存的那一堆东西是数组,3 表示数组长度,int 表示数组里面存的数据是 int 类型

    int(*p)[3];    // 长度为 3 的数组指针
    p = arr;
    printf("%d \n", p[1]);

    return 0;
}
// 数组指针,知识扩展

# include <stdio.h>

typedef int(*pType)[3];    // 定义类型:int(*pType)[3],取别名为:pType

int main()
{    
    int arr[3] = { 1, 2, 3 };
    pType p;    // 变量 p 的类型为 pType,而属于这种类型的变量 p 必然满足 int(*pType)[3] 模板格式
    p = arr;    // arr里面储存的“数组的内存首地址”复制给变量 p
    printf("%d \n", (*p)[0]);    // 注意!不要写成 p[0],虽然 p 获得了 arr 里面存的首地址,但是 *p 才是代表数组整体

    return 0;
} 

/*
    同理:
    # include <stdio.h>

    typeof int pArr[3];

    int main()
    {
        int arr[3] = { 1, 2, 3 };
        pArr p;
        p = arr;
    }
*/

使用const修饰指针

/*
    const:常量,被它修饰的变量会具有常量的属性,使用 const 修饰指针包含三种情况
        1.常量指针(指向常量的指针)
            指向常量的指针 type const *p; 或者 const type *p;
            可以改变指向,但是不能用 *p 修改指向变量的值


        2.指针常量
            它是常量,本身不能改变,也就是不能改变指向
            因为指向不能改,所以必须初始化
            但是可以通过取内容修改指向的内存中的内容


        3.常量指针常量("常量指针"常量即:指针常量)
            指针本身是一个常量,指向的也是常量
            const int * const p = &a;
            不能改变指向,也不能改变指向的内存的内容
*/

# include <stdio.h>

int main()
{
    const int num = 0;    // 变量 num 使用 const 修饰了就不能被修改了
    
    // 1.常量指针
    int a = 0, b = 9;
    const int * p = &a;        // const -> int
    p = &b;    // 可以改变指向


    // 2.指针常量
    int c = 6;
    int* const p1 = &c;        // const -> p1
    *p1 = 10;    // 可以修改内容


    // 3.常量指针常量
    int d = 8;
    const int* const p = &d;    // const -> int 和 p


    return 0;

}

指针和结构体

/*
            指针与结构体:
    指针与结构体结合起来使用包含两种情况:
    一.指针成员
        结构体变量的成员中存在指针

    二.结构体指针
        指向结构体变量的指针
*/
// 指针成员

# include <stdio.h>

typedef struct
{
    int n;
    int m;
    int* p;    // 定义指针

}MyStruct;

int main()
{
    MyStruct mystr;
    mystr.n = 0;
    mystr.m = 0;
    mystr.p = NULL;    // 地址置空
    return 0;

}
// 结构体指针

# include <stdio.h>

typedef struct
{
    int n;
    int m;

}MyStruct;

int main()
{
    MyStruct mystr;
    mystr.n = 0;
    mystr.m = 0;
    
    MyStruct* p = NULL;    // 定义一个指针 p ,它的类型是 MyStruct*,即该指针指向的是有 MyStruct 类型的变量(首地址)
    p = &mystr;    // mystr 便符合条件,可以将首地址 &mystr 赋值给 p

    // 注意!通过指针访问“结构体中的元素”的时候,用 -> 符号,而不是用 . 符号
    p->n = 9;
    p->m = 8;
    return 0;

}

使用指针的注意事项

/*
    注意事项
        1.避免野指针
            推荐:每次定义指针都进行初始化(有指向就给指向,没指向就置空 )
        2.注意类型匹配
        3.防止内存泄漏
            只有堆区是自己申请,自己释放,其他地方都是系统分配,系统回收

        总结:指针能够直接操作内存,必须在自己明确用途的情况下使用,否则很可能会造成严重后果!
*/

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1441877.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

VMware17上安装centos7.9

一、下载安装包&#xff1a; 1、VMware安装 VMware 下载地址&#xff1a; https://www.vmware.com/cn/products/workstation-pro.html VMware下载后安装即可 安装教程可以参考VMware安装教程 2、CentOs7.9下载地址&#xff1a; http://mirrors.aliyun.com/centos/7.9.2009/iso…

HTML+CSS:全景轮播

效果演示 实现了一个简单的网页布局&#xff0c;其中包含了五个不同的盒子&#xff0c;每个盒子都有一个不同的背景图片&#xff0c;并且它们之间有一些间距。当鼠标悬停在某个盒子上时&#xff0c;它的背景图片会变暗&#xff0c;并且文字会变成白色。这些盒子和按钮都被放在一…

如何用Hexo搭建一个优雅的博客

引言 在数字化时代&#xff0c;拥有一个个人博客已经成为许多人展示自己技能、分享知识和与世界互动的重要方式。而在众多博客平台中&#xff0c;Hexo因其简洁、高效和易于定制的特点而备受青睐。本文将详细介绍如何从零开始搭建一个Hexo博客&#xff0c;让你的个人博客在互联…

Git版本与分支

目录 一、Git 二、配置SSH 1.什么是SSH Key 2.配置SSH Key 三、分支 1.为什么要使用分支 2.四个环境及特点 3.实践操作 1.创建分支 2.查看分支 3.切换分支 4.合并分支 5.删除分支 6.重命名分支 7.推送远程分支 8.拉取远程分支 9.克隆指定分支 四、版本 1.什…

Linux操作系统基础(三):虚拟机与Linux系统安装

文章目录 虚拟机与Linux系统安装 一、系统的安装方式 二、虚拟机概念 三、虚拟机的安装 四、Linux系统安装 1、解压人工智能虚拟机 2、找到解压目录中的node1.vmx 3、启动操作系统 虚拟机与Linux系统安装 一、系统的安装方式 Linux操作系统也有两种安装方式&#xf…

蓝桥杯每日一题------背包问题(一)

背包问题 阅读小提示&#xff1a;这篇文章稍微有点长&#xff0c;希望可以对背包问题进行系统详细的讲解&#xff0c;在看的过程中如果有任何疑问请在评论区里指出。因为篇幅过长也可以进行选择性阅读&#xff0c;读取自己想要的那一部分即可。 前言 背包问题可以看作动态规…

js手写Promise(上)

目录 构造函数resolve与reject状态改变状态改变后就无法再次改变 代码优化回调函数中抛出错误 thenonFulfilled和onRejected的调用时机异步then多个then 如果是不知道或者对Promise不熟悉的铁铁可以先看我这篇文章 Promise 构造函数 在最开始&#xff0c;我们先不去考虑Promi…

精简还是全能?如何在 Full 和 Lite 之间做出最佳选择!关于Configuration注解的Full模式与Lite模式(SpringBoot2)

&#x1f3c3;‍♂️ 微信公众号: 朕在debugger© 版权: 本文由【朕在debugger】原创、需要转载请联系博主&#x1f4d5; 如果文章对您有所帮助&#xff0c;欢迎关注、点赞、转发和订阅专栏&#xff01; 前言 关于 Configuration 注解&#xff0c;相信在座的各位 Javaer 都…

可达鸭二月月赛——基础赛第六场(周五)题解,这次四个题的题解都在这一篇文章内,满满干货,含有位运算的详细用法介绍。

姓名 王胤皓 T1 题解 T1 题面 T1 思路 样例输入就是骗人的&#xff0c;其实直接输出就可以了&#xff0c;输出 Hello 2024&#xff0c;注意&#xff0c;中间有一个空格&#xff01; T1 代码 #include<bits/stdc.h> using namespace std; #define ll long long int …

Swift 使用 Combine 管道和线程进行开发 从入门到精通八

Combine 系列 Swift Combine 从入门到精通一Swift Combine 发布者订阅者操作者 从入门到精通二Swift Combine 管道 从入门到精通三Swift Combine 发布者publisher的生命周期 从入门到精通四Swift Combine 操作符operations和Subjects发布者的生命周期 从入门到精通五Swift Com…

ANSI Escape Sequence 下落的方块

ANSI Escape Sequence 下落的方块 1. ANSI Escape 的用途 无意中发现 B站有人讲解&#xff0c; 完全基于终端实现俄罗斯方块。 基本想法是借助于 ANSI Escape Sequence 实现方方块的绘制、 下落动态效果等。对于只了解 ansi escape sequence 用于 log 的颜色打印的人来说&…

(每日持续更新)信息系统项目管理(第四版)(高级项目管理)考试重点整理第10章 项目进度管理(四)

博主2023年11月通过了信息系统项目管理的考试&#xff0c;考试过程中发现考试的内容全部是教材中的内容&#xff0c;非常符合我学习的思路&#xff0c;因此博主想通过该平台把自己学习过程中的经验和教材博主认为重要的知识点分享给大家&#xff0c;希望更多的人能够通过考试&a…

【Java EE】----SpringBoot的日志文件

1.SpringBoot使用日志 先得到日志对象通过日志对象提供的方法进行打印 2.打印日志的信息 3.日志级别 作用&#xff1a; 可以筛选出重要的信息不同环境实现不同日志级别的需求 ⽇志的级别分为&#xff1a;&#xff08;1-6级别从低到高&#xff09; trace&#xff1a;微量&#…

高级数据结构与算法 | 布谷鸟过滤器(Cuckoo Filter):原理、实现、LSM Tree 优化

文章目录 Cuckoo Filter基本介绍布隆过滤器局限变体 布谷鸟哈希布谷鸟过滤器 实现数据结构优化项Victim Cache备用位置计算半排序桶 插入查找删除 应用场景&#xff1a;LSM 优化 Cuckoo Filter 基本介绍 如果对布隆过滤器不太了解&#xff0c;可以看看往期博客&#xff1a;海量…

CentOS 7安装Nodejs

说明&#xff1a;本文介绍如何在云服务器上CentOS 7操作系统上安装Nodejs。以及安装过程中遇到的问题。 下载压缩包&解压 首先&#xff0c;先去官网下载Linux版本的Node。 将下载下来的压缩包&#xff0c;上传到云服务器上&#xff0c;解压。配置环境变量。 &#xff08…

VScode为什么选择了Electron,而不是QT?

选择Electron而不是QT可能是基于以下几个原因&#xff1a; Web技术的普及和开发者生态系统&#xff1a;Web技术如HTML、CSS和JavaScript在开发者中非常普及&#xff0c;开发者生态系统庞大且活跃。使用Electron可以利用这些熟悉的Web技术和丰富的开发者社区资源。跨平台支持&am…

蓝桥杯(Web大学组)2022国赛真题:水果消消乐

思路&#xff1a; 记录点击次数&#xff0c;点击次数为1时&#xff0c;记录点击下标&#xff08;用于隐藏or消除&#xff09;、点击种类&#xff0c;点击次数为2时&#xff0c;判断该下标所对应种类与第一次是否相同 相同&#xff1a;两个都visibility:hidden &#xff08;占…

黄金交易策略(EA):三个仓位的设计是确保可以不停息做单

完整EA&#xff1a;Nerve Knife.ex4黄金交易策略_黄金趋势ea-CSDN博客

MacOS 查AirPods 电量技巧:可实现低电量提醒、自动弹窗

要怎么透过macOS 来查询AirPods 电量呢&#xff1f;当AirPods 和Mac 配对后&#xff0c;有的朋友想通过Mac来查询AirPods有多少电量&#xff0c;这个里有几个技巧&#xff0c;下面我们来介绍一下。 透过Mac 查AirPods 电量技巧 技巧1. 利用状态列上音量功能查询 如要使用此功能…

在django中集成markdown文本框

首先需要下载开源组件&#xff1a;http://editor.md.ipandao.com/&#xff0c;可能需要挂梯子。 百度网盘&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1D9o3P8EQDqSqfhAw10kYkw 提取码&#xff1a;eric 1.在html代码中生成一个div&#xff0c;ideditor <div c…