06- c语言指针 (C语言)

news2024/11/15 11:35:47

一 指针的引入

1、一般把内存中的一个字节称为一个内存单元。
2、为了正确地访问这些内存单元,必须为每个内存单元编上号。根据一个内存单元的编号即可准确地找到该内存单元。内存单元的编号也叫做地址,通常也把这个地址称为指针
3、如果在程序中定义了一个变量,在对程序进行编译或运行时,系统就会给这个变量分配内存单元,并确定它的内存地址(编号)。
4、变量的地址就是变量的指针,存放变量地址的变量是指针变量
5、内存单元的指针内存单元的内容是两个不同的概念。可以用一个通俗的例子来说明它们之间的关系。我们到银行去存取款时,银行工作人员将根据我们的帐号去找我们的存款单,找到之后在存单上写入存款、取款的金额。在这里,帐号就是存单的指针,存款数是存单的内容。对于一个内存单元来说,单元的地址即为指针,其中存放的数据才是该单元的内容。

二 指针变量的定义和使用

2.1 指针变量定义语法

数据类型  *指针变量名,注意:
1、数据类型为C语言支持的 所有数据类型
2、指针变量名遵循C语言变量的 命名规则

举例:

int *p; //定义了一个指针变量p,简称指针p,p是变量, int *是类型
char* p2;

我们也认为 指针是一种数据类型

2.2 指针变量的赋值

1)指针变量的值代表这个指针指向了以这个值为首地址的那块 内存空间

2)指针变量赋值 为其他变量的地址:&:取地址运算符

//指针变量 = &变量名;
&:取地址运算符
int a = 10;
int *p = &a;

3)指针变量 操作指向的内存空间
可以通过 指针变量访问 和修改所指向的内存空间中的内容

*:指针运算符(或称“间接访问” 运算)。
int a = 10;
int *p;
p = &a;
printf("*p: %d\n", *p);
*p = 100;
printf("*p: %d\n", *p);

4)指针变量同普通变量一样,定义后如果不进行初始化指针变量的值是不确定的

5)野指针
因为指针变量的值是不确定的,所以这个指针我们称之为“野指针”。
野指针的危害:因为指针指向的空间是不确定的,所以指针可能会操作到非法的内存空间,导致程序运行奔溃。 

int a = 100;
int *p;
*p = 1000;
/*因为p没有初始化/赋值,所以p的值是不确定的,如果此时p的值恰好等于a的地址(p == &a), 那么*p=1000将a的值修改为如果p的值恰好是内存上一块只读的内存空间,*p = 1000将导致程序异常退出,你可能会看到程序运行报错(段错误/核心内容*/

6)空指针
为了标志指针变量没有指向任何变量(空闲可用),C语言中,可以把NULL赋值给此指针,这样就标志此指针为空指针。

int *p = NULL;

NULL是一个值为0的宏常量:

#define NULL ((void *)0)

注意:空指针的作用是防止指针变量变成野指针。如果用*访问空指针所指向的内存空间也会程序报错。

7)笔试题:嵌入式系统经常具有要求程序员去 访问某特定的内存位置 的特点。在某工程中,要求设置一绝对地址为 0x67a9 的整型变量的值为 0xaa66

//方法1:
int *ptr;
ptr = (int *)0x67a9; //在内存地址编号的前面加上(int *)将地址编号这个无符号整型数据强制转换为
//(int*)指针类型,这样赋值符号左值和右值的数据类型一致
*ptr = 0xaa66;
//方法2:
*(int *)(0x67a9) = 0xaa55;

注意:在实际工作中我们一般 很少会将一个确定的内存地址赋值给一个指针变量,因为程序员一般不知道哪个内存地址是可用的!!!

2.3 不同类型指针变量之间的区别

1、int *p1 和 char *p2 的相同点是什么?

int x = 100;
int *p1 = &x;
char y = 'A';
char *p2 = &y;

相同点:

  • 都是指针变量
  • 都是用来保存一个内存地址编号
  • 占用的内存空间大小一样
int *p1;
char *p2;
printf("%d %d\n", sizeof(p1), sizeof(p2));

我们发现p1和p2占用的内存空间为4/8,在32位机器上结果为4, 64位机器上结果为8。
思考:

为什么指针变量占用的内存空间是 4 或者 8个字节呢?
因为指针变量保存的是一个内存地址的编号!
32位机器内存地址编号最大值为 2^32-1,可以用一个4字节的变量保存。
64位机器内存地址编号最大值为 2^64-1,可以用一个8字节的变量保存。

2、int *p1 和 char *p2 的不同点是什么?
首先我们应该知道:内存中存储的只是二进制而已。
之所以有 int float char 等数据类型是程序员希望将存储在内存中的二进制当作某种数据类型来处理而已。

int x = 65;
printf("%c\n", x);
  • int *p1的作用就是指针变量p1将他所指向的内存空间中的二进制当作 int类型 来处理。
  • char *p2的作用就是指针变量p2将他所指向的内存空间中的二进制当作 char类型 来处理。

3、p1++ 和 p2++的区别

#include <iostream>

int main() {
    int x = 10;
    int *p1 = &x;
    char y = 'A';
    char *p2 = &y;
    printf("p1: %p, p2: %p\n", p1, p2);
    p1++;
    p2++;
    printf("p1: %p, p2: %p\n", p1, p2);
    return 0;
}

p1自增后和自增前值 相差4
p2自增后和自增前值 相差1
指针变量+n,不是指针往后偏移n个字节,而是指针变量往后偏移n个数据类型,例如:p1+3,
表示指针p1往后偏移3个int类型的数据,指针变量p1的值+12 (3*sizeof(int)) 。

三 指针和数组

3.1 数组的指针

1、一个变量有一个地址,一个数组包含若干元素,每个数组元素都在内存中占用存储单元,它们都有相应的地址。所谓 数组的指针是指数组的起始地址
2、数组名表示数组的首地址,因此数组名也是一种指针
3、通过数组名访问数组中元素。

int ch[] = {1,2,3,4};
//假如我们想访问ch中的第3个元素:ch[3] == 4
//我们也可以通过指针法引用数组中的元素:比如 *(ch + 3)
//那么,如果想访问第n个元素呢?
*(ch + n) 注意:n<=sizeof(ch)/sizeof(ch[0])-1

4、练习:假如有数组int a[4], 编写代码实现如下功能:

  • 通过从键盘上输入数字对数组a的每一个元素进行赋值
  • 打印出数组a中每一个元素的地址
  • 通过指针法将数组a中的每一个元素的值打印出来
#include <iostream>

int main() {
    int a[4];
    int i;
    for (i = 0; i < 4; i++)
        scanf("%d", &a[i]);
    for (i = 0; i < 4; i++) {
        printf("%p %d\\n", &a[i], *(a+i));
    }
    }

5、通过指针变量间接访问数组

#include <iostream>

int main() {
    int a[4] = {1,2,3,4};
    int *p;
    p = a;
    *(p + 2) = 100;
    char ch[] = {'a', 'b', 'c'};
    char *p2;
    p2 = ch;
    *(p2+1) = 'A';
    }
#include <iostream>

int main() {
    int ch[] = {1, 2, 3, 4};
    printf("%d\n", ch[4]);
    printf("%p %p\n", &ch[3], &ch[4]);
    //数组名:数组的首地址
    printf("ch: %p\n", ch);
    //数组中的第0个元素的地址:数组的首地址
    printf("&ch[0]: %p\n", &ch[0]);
    printf("%d %d\n", ch[3], *(ch+3));
    int a[4];
    int i;
    for (i = 0; i < 4; i++)
    {
        scanf("%d", &a[i]); //a+i
        getchar();
    }
    //打印数组中每个元素的地址
    for (i = 0; i < 4; i++)
        printf("%p\n", &a[i]);
    //通过指针法将数组a中的每一个元素的值打印出来
    for (i = 0; i < 4; i++)
        printf("%d\n", *(a+i));
    int *p;
    p = a; //指针p指向了数组a

    //指针指向了一个数组,可以将指针当数组看待
    for (i = 0; i < 4; i++)
        printf("%d\n", p[i]); //通过下标发访问数组中的元素
        // printf("%d\n", *(p+i));
    }

6、数组指针越界

#include <iostream>

int main() {
    int b[4] = {10, 20, 30, 40};
    int a[4];
    a[4] = 100;
    printf("a[4]: %d\n", a[4]);
    printf("b[0]: %d\n", b[0]);
    printf("a: %u, b: %u\n", &a, &b); //打印数组a和b的首地址
    }

3.2 指针数组

1、指针数组顾名思义就是:存放指针的数组,本质是数组,数组中的每个元素都是指针 

#include <stdio.h>

int main()
{
    int a = 10, b = 20, c = 30;
    int *p[3];
    p[0] = &a;
    p[1] = &b;
    p[2] = &c;
    return 0;
}

2、注意:int *p[3]; 等价于 (int *) p[3]; 因为[] 比 *的优先级要高先与p匹配。
3、思考:如何通过一个数组存储10个人的姓名?
char *name[10] = {"zhangsan", "lisi", "wangwu", "zhaoliu, "tianqi"};
name数组中保存了10个字符串常量的首地址(注意:没有保存字符串常量而是常量的首地址)。

3.3 指针变量的地址

1、我们在定义一个 指针变量 的时候,编译器会分配一块空间来存储这个指针变量的值,分配的这块内存空间肯定有一个地址编码啦,那么这个地址编码肯定就是这个指针变量的地址啦。

#include <stdio.h>

int main()
{
    int a = 10;
    int *p;
    p = &a;
    //将指针变量p的值以及变量a的地址打印出来(结果应该是两者相等)
    printf("p: %p, &a: %p\n", p, &a);
    //打印指针变量p的地址(存储指针变量p的内存空间的首地址)
    printf("&p: %p\n", &p);
    return 0;
}

2、强调:指针变量p的值保存的是另外一个变量a的地址0x300800,指针变量的地址是存储p这个指针变量的值的那块内存空间的首地址:0x3007F8,这块空间中保存的值是0x300800 

3.4 一级指针作为函数的形参

1、函数的形参为数组
如果函数的形参是数组,该形参的定义方法如下:

void func(int a[], int n)
{}

我们也可以将形参定义为 指针类型
 

void func(int *a, int n)
{}

在实际工作中我们通常使用第二种方法!

2、调用函数时需要传递字符串可将形参设计为char *类型

#include <stdio.h>

void func(char *p) //调用函数时将字符串的地址赋值给指针变量p
{
    printf("%c\n", p[0]);   //返回值为 'h'
}
int main()
{
    func("hello");
    return 0;
}

3、当形参为数组时,如果获取数组的长度呢?     //sizeof( )

为什么sizeof(a)和sizeof(b)的值都是8呢?
原因:编译器在编译的时候将a和b当做了指针来处理了!!! 

4、注意:如果函数的 形参为指针,在函数体中一般先对指针的值进行判断,判断指针的值是否为NULL

3.5 二级指针

1、用一个指针变量保存一个一级指针变量的地址,这个指针我们称之为二级指针
2、二级指针的定义 :

  • 数据类型 **变量名;

3、二级指针的应用 

void func_1(){
    //二级指针的使用
    int a = 10;
    int *p = &a;
    int **p2 = &p; //二级指针p2保存了一级指针p的地址(p2指向了p )
    int ***p3 = &p2; //三级指针
    //*p2 == p == &a
    printf("%p %p %p\n", *p2, p, &a);  //000000000061FDE4 000000000061FDE4 
    //**p2 == *p == *(&a) == a
    printf("%d %d %d %d\n", **p2, *p, *(&a), a);   //10 10 10 10

    **p2 = 100;
    printf("%d %d %d %d\n", **p2, *p, *(&a), a);  //100 100 100 100
    //*p3 == p2 = &p
    //**p3 == *p2 == p == &a
    //***p3 == **p2 == *p == *(&a) == a
    printf("***p3: %d\n", ***p3);   //***p3: 100
}

3.6 内存分配

1、在实际工作中,如果我们需要存储多个数据很多同学首先想到的是使用数组,但是因为数组的长度在定义完后是固定的所以往往不够灵活
2、我们可以根据需要存储的数据类型先定义一个指针变量,例如:int *p; 然后根据实际需求使用
malloc函数
分配空间。

3、malloc函数

#include <stdlib.h>
void *malloc(size_t size);

功能:malloc 函数像系统申请size个字节的内存空间,并且返回一个指针,这个指针指向被分配的内存空间的首地址,并且申请的内存空间是在“堆”上的。堆上的空间是需要手动申请,手动释放的!!否则就会造成内存泄漏。

int *p;
//假如我们需要存储10个int类型的数据
p = (int *)malloc(10*sizeof(int));

注意:分配的空间为 10*sizeof(int), 因为malloc申请分配的空间是以字节为单位的。

int *p;
//通过指针变量p操作一块空间,可以存储4个int数据
//向系统申请 4*sizeof(int)字节的内存空间
p = (int *)malloc(4*sizeof(int)); //在堆上申请了4*sizeof(int)字节的内存空间
//p的值:申请到的堆上的内存空间的首地址 (指针p指向申请到的堆上的空间)
printf("p: %p\n", p);
//通过指针变量p 来操作申请到的堆空间
p[0] = 100;
p[1] = 200;
*(p+2) = 300;
*(p+3) = 400;

4、内存释放:free函数 

#include <stdlib.h>
void free(void *ptr);

功能:释放ptr所指向的内存空间

注意:
free函数并不会修改指针变量的值!但是free执行完成以后指针所指向的原来的那块地址空间中的内容是不确定的!!
问题:
释放空间到底做了什么事情呢?
最重要的是:告诉系统这块内存空间可以给别人使用了!!!

  • malloc 所在的源文件: #include <stdlib.h>
  • strcpy 所在的源文件: #include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(int argc, char *argv[]){
    char *p;
    //malloc分配的空间是在堆上的,需要手动释放
    p = (char *)malloc(10);
    strcpy(p, "hello");
    printf("p所指向的空间的内容: %s\n", p); //结果是hello

    //将p所指向的地址空间的首地址打印出来(就是将指针变量p的值打印出来)
    printf("p的值: %p\n", p);
    free(p);
    //将p所指向的地址空间的首地址打印出来
    printf("p的值: %p\n", p);

    //仔细观察,下面这条打印语句的结果
    printf("p所指向的空间的内容: %s\n", p);  //结果不是hello

    strcpy(p, "world");
    //再仔细观察,下面这条打印语句的结果
    printf("p所指向的空间的内容: %s\n", p);  //结果是world

    return 0;
}

free函数调用完以后的使用技巧: 

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(int argc, char *argv[]){
    char *p;
    //malloc分配的空间是在堆上的,需要手动释放
    p = (char *)malloc(10);
    strcpy(p, "hello");
    
    //释放申请的内容
    free(p);
    p = NULL;
    return 0;
}

5、如果之前分配的空间不够了怎么办呢?
我们可以使用 realloc函数: 

#include <stdlib.h>
void *realloc(void *ptr, size_t size);

功能:在堆上分配一块size所指定的新的内存空间,空间大小单位为字节,并且还会将ptr所指向的空间中的内容拷贝到新的内存空间中,最后返回新的内存空间的额首地址
示例代码: 

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main()
{
    char *p;
    p = (char *)malloc(10);
    strcpy(p, "hello"); //向分配的空间中拷贝字符串
    printf("p所指向空间的首地址: %p\n", p);
    printf("p所指向空间的内容: %s\n", p);
    p = (char *)realloc(p, 20); //重新分配新的空间
    printf("p所指向新的空间的首地址: %p\n", p);
    printf("p所指向新的空间的内容: %s\n", p);
    //注意:分配的新的空间的首地址有可能有之前分配的空间首地址一样,也有可能不一样
    strcat(p, " world"); //追加字符串
    printf("p所指向新的空间的内容: %s\n", p);
    return 0;
}

6、思考一种情景,char *dest, *src; 通过一个函数将src所指向的地址空间中的内容拷贝到dest所指向的地址空间中, 但是假设在调用函数前我们并不知道src的长度,这个时候我们需要将函数的形参设计为 二级指针

void test2(char **dest, char *src){
    //通过二级指针dest给形参一级指针dest分配内存空间!
    *dest = (char *)malloc(strlen(src) + 1);
    if (NULL == *dest || NULL == src)
        return ;
    strcpy(*dest, src);
}
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void func4(char **dst)
{
    //*dst == p
    *dst = (char *)malloc(10); //在堆上申请了10个字节
    strcpy(*dst, "hello");
}

int main()
{
    char *p; //指针指向某个函数调用结束后 在函数体中申请的堆空间的首地址
    /*
    * 既然我希望让p指向一块堆空间,其实就是希望对p进行赋值,赋值为在函数中申请的堆空间的首地址
    * 如何在函数中对p进行赋值呢?必须在调用函数的时候传递p的地址!!!!
    * */
    func4(&p);
    printf("%s\n", p);
    free(p);
    return 0;
}

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

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

相关文章

设计模式之策略模式笔记

设计模式之策略模式笔记 说明Strategy(策略)目录策略模式示例类图抽象策略类策略A类策略B类策略C类促销员类测试类 说明 记录下学习设计模式-策略模式的写法。JDK使用版本为1.8版本。 Strategy(策略) 意图:定义一系列的算法&#xff0c;把它们一个个封装起来&#xff0c;并且…

【数据分享】全国县市2000-2021年综合经济数据(地区生产总值\一二三产业增加值等)

《中国县域统计年鉴》是一部全面反映我国县域社会经济发展状况的资料性年鉴&#xff0c;收录了上一年度全国2000多个县域单位的基本情况、综合经济、农业、工业、教育、卫生、社会保障等方面的资料。 本篇文章我们给大家带来从2001年—2022年《中国县域统计年鉴》整理的数据—…

MySQL:事务

事务 在介绍事务之前&#xff0c;我们先来了解一个案例&#xff1a; 在一个买票的软件中&#xff0c;当客户端A检查还有一张票时&#xff0c;将票卖点&#xff0c;但是还没有更新数据库&#xff0c;客户端B检查了票数&#xff0c;发现大于0&#xff0c;于是又卖掉了一张票。然…

ROS:通信机制

目录 一、通信机制简介二、话题通信机制2.1话题通信简介2.2话题通信实操&#xff08;C&#xff09;2.2.1分析2.2.2发布方代码2.2.3订阅方代码2.2.4配置CMakeLists.txt2.2.5执行2.2.6注意 2.3话题通信实操&#xff08;python&#xff09;2.3.1分析2.3.2发布方代码2.3.3订阅方代码…

【计算机网络】第二章 物理层(上)

文章目录 2.1 物理层的基本概念2.2 物理层下面的传输媒体2.3 传输方式2.4 编码与调制2.4.1 介绍2.4.2 常用编码2.4.3 编码习题2.4.4 基本调制方法2.4.5 混合调制 2.1 物理层的基本概念 物理层考虑的是怎样在连接各种计算机的传输媒体上传输数据比特流。 物理层为数据链路层屏蔽…

4.0、Java_IO流 - 流的概念细分

4.0、Java_IO流 - 流的概念细分 按照流的方向分类&#xff1a; 输入流&#xff1a;数据流从数据源到程序&#xff08;以 InputStream 、Reader 结尾的流&#xff09;&#xff1b; 输出流&#xff1a;数据流从程序到目的地&#xff08;以 OutputStream 、Writer 结尾的流&#x…

【雕爷学编程】Arduino动手做(120)---游戏摇杆扩展板

37款传感器与执行器的提法&#xff0c;在网络上广泛流传&#xff0c;其实Arduino能够兼容的传感器模块肯定是不止这37种的。鉴于本人手头积累了一些传感器和执行器模块&#xff0c;依照实践出真知&#xff08;一定要动手做&#xff09;的理念&#xff0c;以学习和交流为目的&am…

2023-06-23:redis中什么是缓存击穿?该如何解决?

2023-06-23&#xff1a;redis中什么是缓存击穿&#xff1f;该如何解决&#xff1f; 答案2023-06-23&#xff1a; 缓存击穿是指一个缓存中的热点数据非常频繁地被大量并发请求访问&#xff0c;当该热点数据失效的瞬间&#xff0c;持续的大并发请求无法通过缓存获取到数据&…

Triton教程 --- Triton 响应缓存

Triton教程 — Triton 响应缓存 Triton系列教程: 快速开始利用Triton部署你自己的模型Triton架构模型仓库存储代理模型设置优化动态批处理速率限制器模型管理自定义算子解耦后端和模型 概述 在本文档中&#xff0c;推理请求是模型名称、模型版本和输入张量&#xff08;名称、…

ChatGPT从入门到精通,深入认识ChatGPT

ChatGPT从入门到精通&#xff0c;一站式掌握办公自动化/爬虫/数据分析和可视化图表制作 全面AI时代就在转角 道路已经铺好了 “局外人”or“先行者” 就在此刻 等你决定1、ChatGPT从入门到精通&#xff0c;一站式掌握办公自动化/爬虫/数据分析和可视( 点击观看完整版本 )https…

TreeMap数据结构及源码解析.跟学黑马

TreeMap数据结构及源码解析 1.TreeMap的特点2.TreeMap的数据结构2.1二叉查找树2.1.1二叉查找树的定义2.1.2二叉查找树的查找操作 2.2平衡二叉树2.2.1平衡二叉树的定义2.2.2平衡二叉树的旋转 2.3红黑树2.3.1红黑树的定义 2.TreeMap的源码分析2.1get()获取方法分析2.2put()添加方…

企业级开发项目和自学项目到底有什么区别

前言 好久不见了各位&#xff01;最近几个月都未更新&#xff0c;是因为从春招开始就在投简历面试实习岗位&#xff0c;然后入职&#xff0c;最后成功成为了一个半成品后端练习生&#xff0c;想说的话有太多太多 下面就站在一个在校实习生的身份&#xff0c;结合自己最近几个月…

科普 | 眼图

本文简要说明眼图相关的知识&#xff0c;参考是德科技的文章 1。 科普 | 眼图 基本知识串扰眼图眼图的产生原理及作用创建眼图 - 眼图波形的采样过程眼图的产生原理及作用眼图可以看出哪些性能指标&#xff1f;如何评判眼图质量&#xff1f;眼图测试模板眼图与存储深度实时的眼…

短视频seo源码开发部署技术解析

短视频seo开发需要哪些技术 应用程序优化技术&#xff1a;包括应用程序的各种元素&#xff08;如标题、描述、关键字、图标等&#xff09;的优化和设置&#xff0c;以及应用程序内部链接和导航的合理布局和设置。 视频内容优化技术&#xff1a;包括视频标题、描述、标签、封面…

人工智能数据集处理——数据获取

目录 1、从csv和txt文件中读取数据 pandas中可使用read_csv读取csv或txt文件中的数据 使用read_csv()函数读取phones.csv文件中的数据,并指定编码格式为gbk 使用head()方法指定获取phones.csv文件中前3行的数据 使用read_csv() 函数读取 itheima_books.txt文件中的数据,并指…

【Redis】2、Redis 的 Java 客户端(Jedis 和 SpringDataRedis)

目录 零、Redis 的 Java 客户端有哪些&#xff1f;二、Jedis 客户端(1) 引依赖(2) 连接 Redis 服务并测试(3) Redis 连接池 三、SpringDataRedis 介绍四、SpringBoot 中集成 SpringDataRedis(1) 引入依赖(2) 配置文件中书写相关配置(3) RedisTemplate 的默认序列化方式(4) 自定…

高校学生考勤系统

摘 要 在Internet高速发展的今天&#xff0c;我们生活的各个领域都涉及到计算机的应用&#xff0c;其中包括高校学生考勤系统的网络应用&#xff0c;在外国高校学生考勤系统已经是很普遍的方式&#xff0c;不过国内的高校学生考勤可能还处于起步阶段。高校学生考勤系统具有管理…

Linux网络-数据链路层,MAC帧解析,ARP协议

目录 数据链路层VS网络层 以太网概念 以太网的帧格式&#xff08;报文格式&#xff09;&#xff08;也可以称之为MAC帧&#xff09; MAC地址的概念 MAC帧格式 局域网通信原理 MTU MTU说明 MTU对IP协议的影响 MTU对UDP协议的影响 MTU对TCP协议的影响 ARP协议 ARP协…

【算法题解】41. 二叉树的中序遍历

这是一道 简单 题 https://leetcode.cn/problems/binary-tree-inorder-traversal/ 题目 给定一个二叉树的根节点 root &#xff0c;返回 它的 中序 遍历 。 示例 1&#xff1a; 输入&#xff1a;root [1,null,2,3] 输出&#xff1a;[1,3,2] 示例 2&#xff1a; 输入&#…

[易语言][部署]使用易语言部署paddleocr的onnx模型api接口推理直接调用

易语言如何部署paddleocr模型&#xff0c;如今paddleocr模型广泛被使用各种编程&#xff0c;为了能在易语言上使用因此开发一个通用接口&#xff0c;开发基本思路如下&#xff1a; 可见我们并没有使用什么通信协议或者命令行之类的方法&#xff0c;这种直接封装接口比其他方法更…