本人喜欢提炼自己的一套理解,希望读者一定也要形成自己的理解,这样后面指针再结合其他的东西也能轻而易举看懂!
这也是我日日夜夜的付出!大家学完还有任何疑惑私信或评论,看到会回,喜欢收藏点赞,感谢!
《个人原创【指针】学习笔记》,转载请注明出处!不胜感谢。
附件包括文章markdown文本及测试代码,需要自取(免费)。
目录
- 引言★★★
- 运算符与优先级指针与取址 典例1
- 指针与数组指针与函数
- 函数指针和指针函数 典例2
- 函数指针 典例3
- 指针函数与数组指针指针与内存分配
- 双重指针 典例4
- 双重指针
- 思考
引言★★★★★
重点在于实践:一定要去自己动手测试典例1代码!!!
int* ptr = # //举例,下面关系不大,重点在于先阐明一下一些要点!初学者容易迷惑。
下面很重要,可截图置顶待会参照,我稍后尽量注释提醒:
本质:
地址要用指针来存储,指针是用来存储地址的!!!
重难点:
指针指向的地址,也就是指针存储的地址!!!
就像整型数据要用整型变量来存储,整型变量是用来存储整型数据的!
地址:
地址值: ptr/∑
取出地址所存的值: *a/sum;
地址所存的值的类型 : int
典例:函数指针 void(* Fun)(void)
地址值: Fun
取出地址所存的值: * Fun
地址所存的值的类型: void (即为函数类型)
Tips:
指针(*)则右边是它指向的地址的标识符(也可以称为指针变量),左边为它指向的地址所存的值的类型;
函数(Function)则右边为参数,左边为返回值;
运算符与优先级
先记住运算符优先级,对于后面分析比较重要!
运算符
*
(解引用运算符):*
是解引用运算符,用于访问指针所指向的内存地址处存储的值。例如,*ptr
表示访问指针ptr
所指向的内存地址处的值。
()
(函数调用运算符):()
是函数调用运算符,用于调用函数并传递参数。例如,function_name(argument)
表示调用名为function_name
的函数并传递参数argument
。
[]
(下标运算符):[]
是下标运算符,用于访问数组中特定索引位置的元素。例如,array[index]
表示访问数组array
中索引为index
的元素。
&
(取地址运算符):&
是取地址运算符,用于获取变量的内存地址。例如,&variable
表示获取变量variable
的内存地址。
=
(赋值运算符):=
是赋值运算符,用于将右侧的值赋给左侧的变量。例如,variable = value
表示将值value
赋给变量variable
。
优先级
指针运算符 *
、()
、[]
、&
的优先级从高到低的顺序如下:
()
:括号运算符,用于指定表达式中的优先级。[]
:下标运算符,用于访问数组元素。.
和->
:成员访问运算符,用于访问结构体成员。*
:解引用运算符,用于访问指针所指向的值。&
:取地址运算符,用于获取变量的内存地址。
需要注意的是,括号运算符 `()` 具有最高的优先级,
所以在表达式中会首先执行括号中的内容。
接着是下标运算符 `[]` 和成员访问运算符 `.` 和 `->`,
然后是解引用运算符 `*`,最后是取地址运算符 `&`
指针与取址
记住:
地址要用指针来存储,指针是用来存储地址值的!
区分:
ptr地址值和取出地址所存的值 (*a)
指针的定义:
int num = 10;
/*声明一个整型变量,并赋值为10*/
int *ptr;
/*
简明:
定义一个整型指针ptr;
详细:
ptr是一个指针变量,它可以存一个地址,它的初始地址值为0;
*ptr为取出地址所存的值;
int 为这个指针地址将要存的值变量类型为int,否则要提前强制转换<见后文内存分配>;
不一致则需要强制转换 :int* ptr = (int*)malloc(sizeof(int));
*/
指针的赋值:
ptr = &num;
/*
简明:
指针ptr指向变量num的地址
详细:
把num的地址值赋值给指针指向指向的地址值,打印等式两边发现是一样的值,指代表的地址值相同;
也可以说:整型变量的地址存储到整型变量的指针,从此指针指向整型变量的地址,ptr可以代表一个地址值了!
*/
同时声明赋值
int *ptr = &num;
/*
简明:
声明一个整型指针ptr,同时将指针指向num的地址;
详细:
声明一个指针变量ptr,它可以存储一个地址,这个地址所存的值需为整型;
同时对整型变量num进行取址,取到的地址赋给指针指向的地址;
*/
典例1
搞懂了下面这个典例,恭喜你!已经入门啦!!!
/*
本质:
地址要用指针来存储,指针是用来存储地址值的!!!
重难点:
指针指向的地址,也就是指针存储的地址!!!
就像整型数据要用整型变量来存储,整型变量是用来存储整型数据的!
地址:
地址值: ptr/&sum;
取出地址所存的值: *a/sum;
地址所存的值的类型 : int
典例:函数指针 void(* Fun)(void)
地址值: Fun
取出地址所存的值: * Fun
地址所存的值的类型: void (即为函数类型)
*/
#include <stdio.h>
int main(void)
{
int sum = 10; //定义一个指针整型变量
/*第一种写法 */
//声明一个指向整型变量的指针变量 ptr,同时将指针 ptr 指向整型变量 sum 的地址
//int* ptr = ∑
/*第二种写法 */
//ptr是一个指针变量,它存了一个地址 *ptr为取出地址所存的值 int为地址所存的值的变量类型
int* ptr; //声明一个指针变量ptr,用于存储整型变量的地址;地址所存的值是整数类型的 ,指针存的也必须是一个整型变量的地址;
ptr = ∑ //ptr是一个指针变量,指针存的就是sum的地址(&sum),这行把sum地址值取出来地址赋给指针ptr。
printf("sum的地址(值): %d\n", &sum );
printf("ptr指向的地址(值): %d\n", ptr );
printf("ptr指向的地址所存的值: %d\n", *ptr );
printf("________________________\n\n");
//指针与数组
int arr[] = {0, 1, 2};
int *ptr1;
printf("开始指针ptr1的指向地址的值: %d\n", ptr1);
printf("数组首地址:%d\n", arr);
ptr1 = arr; //给指针ptr1赋值(指向数组首地址)后,指针ptr指向(存储)的地址为数组首地址
printf("现在指针ptr1的指向地址的值: %d\n", ptr1); //打印数组arr的首地址:ptr1与指向arr的首地址,自然打印的都是首地址的值
printf("数组首地址:%d\n", arr);
//地址引用
printf("*ptr1 为取出地址所存的值:%d\n", *(ptr1 + 0)); //*(ptr1 + 0)等价于*ptr1也等价于arr[0]
printf("*(ptr1 + 1) 为取出地址所存的值:%d\n", *(ptr1 + 1)); //*(ptr1 + 1)等价于arr[1]
//再次说明ptr与arr都能代表数组首地址
ptr1[0] = 3; //测试是否能够修改第一个元素值
printf("first:%d\n", arr[0]); //发现可以,说明 ptr1[0]等价于 arr[0];
arr[0] = 4;
printf("secend%d\n", arr[0]);
//留下一个思考
int* ptr3 = 10;
printf("%d,%d", *ptr3 ,ptr3);
/*
底层机制理解:
int arr[] = {0, 1, 2};
int *ptr= arr;
声明:
声明一个指向整型数组的指针变量,指这个指针变量指向向数组的首地址
解释:
arr的首地址赋给ptr,ptr是一个整型指针,用于存储一个地址,地址存的值为整数类型
所谓的指针就是把地址所代表的值取出来 即为 (*ptr)
int代表 这个值为整型,指针指向的内容是整型;
之前说“指针存的也必须是一个整型变量的地址 ”
地址存的值的变量类型 与 指针指向的地址需要存的值变量类型 不一致则需要强制转换 :
int* ptr = (int*)malloc(sizeof(int));
*/
}
指针与数组
先结合什么,它就是什么!!!
以上是一个理解指针的一个很优秀的切入点,原文:http://t.csdnimg.cn/fDCR6
注意看注释:优先级这里要用了!
普通的整型变量
int p;
指针变量
P 是一个的指针,指向的内容的类型为int 型
int *p;
/*
首先从P 处开始,先与*结合,所以说明P 是一个指针,
然后再与int 结合,说明指针所指向的内容的类型为int 型.
所以P是一个返回整型数据的指针;
*/
数组
P 是一个数组,里面都是整型数据;
int p[3];
/*
首先从P 处开始,先与[]结合,说明P 是一个数组,
然后与int 结合,说明数组里的元素是整型的,
所以P 是一个由整型数据组成的数组;
*/
指针数组
P 是一个指针所组成的数组,指针指向的内容的类型为int 型;
int *p[3];
/*
首先从P 处开始,先与[]结合,因为其优先级比*高,所以P 是一个数组,
然后再与*结合,说明数组里的元素是指针类型,
然后再与int 结合,说明指针所指向的内容的类型是整型的,
所以P 是一个由返回整型数据的指针所组成的数组
*/
数组指针
P 是一个指针,指针指向数组,数组元素为整型数据;
int (*p)[3];
/*
首先从P 处开始,先与*结合,说明P 是一个指针,
然后再与[]结合(与"()"这步可以忽略,只是为了改变优先级),说明指针所指向的内容是一个数组,
然后再与int 结合,说明数组里的元素是整型的.
所以P 是一个指向由整型数据组成的数组的指针
*/
指针与函数
二级指针
P 是一个大指针,大指针指向的元素是小指针,小指针指向的内容是整型数据;
int **p;
/*
首先从P 开始,先与*结合,说是P 是一个(大)指针,
然后再与*结合,说明(大)指针所指向的元素是(小)指针,
然后再与int 结合,说明(小)指针所指向的元素是整型数据.
由于二级指针以及更高级的指针极少用在复杂类型中,所以后面更复杂类型就不考虑多级指针了,最多只考虑一级指针.
*/
数组
P 是一个函数,函数的参数和返回值类型都为整型数据;
int p(int);
/*
从P 处起,先与()结合,说明P 是一个函数,
然后进入()里分析,说明该函数有一个整型变量的参数,
然后再与外面的int 结合,说明函数的返回值是一个整型数据.
*/
函数指针
P 是一个指针,指针指向函数,函数参数和返回类型都为整型数据
int (*p)(int);
/*
从P 处开始,先与指针结合,说明P 是一个指针,
然后与()结合,说明指针指向的是一个函数,
然后再与()里的int 结合,说明函数有一个int 型的参数,
再与最外层的int 结合,说明函数的返回类型是整型,
所以P 是一个指向有一个整型参数且返回类型为整型的函数的指针.
*/
指针函数
p是一个函数,参数为int类型,返回值为一个int类型的指针(int*)
int* p(int);
拓展:可以先跳过,不看这个类型,过于复杂;
int* (*p(int))[3];
p是一个函数,函数参数为int类型,返回值为指针,指针指向数组,数组元素为指针,指针指向整型数据
/*
从P 开始,先与()结合,说明P 是一个函数,
然后进入()里面,与int 结合,说明函数有一个整型变量参数,
然后再与外面的*结合,说明函数返回的是一个指针
然后到最外面一层,先与[]结合,说明返回的指针指向的是一个数组,
然后再与*结合,说明数组里的元素是指针,
然后再与int 结合,说明指针指向的内容是整型数据.
所以P 是一个参数为一个整数据且返回一个指向由整型指针变量组成的数组的指针变量的函数.
*/
int* (p(int))(int*);
p是一个函数,函数参数为int类型,返回值和参数都为一个指针,指针指向整型数据
函数指针和指针函数
注重运算符优先级即可:
指针函数重点在于返回值是一个指针变量;函数指针重点在于需预制一个函数,让这个指针去指向它;
其实我们早已应用于实际开发,只是总结一下从理论上把它梳理明白!(笔者注)
函数
函数:
int fun(int x,int y);
返回值:
一个函数,然后返回值是一个 int 类型,是一个数值。
指针函数
指针函数:和普通函数对比不过就是其返回了一个指针(即地址值)而已。
定义:
一个返回指针的函数,其本质是一个函数,而该函数的返回值是一个指针。
声明格式:
类型标识符 *函数名(参数表)
原型:
int* fun(int x,int y);
一个fun函数,返回值为<int*>,它是一个int类型的指针,指向了return的值的地址;
返回值:
一个 int 类型的指针。
指针函数写法
笔者喜欢第三种,个人推荐:*靠近返回值类型的话可能更容易理解其定义
int *fun(int x,int y);
int * fun(int x,int y);
int* fun(int x,int y); //个人推荐:*靠近返回值类型,更容易理解其定义!
函数指针
函数指针:函数指针就是指向函数的指针,需提前定义一个函数,让该指针把他取走;
本质是一个指针变量,该指针指向这个函数,去典例中加强理解;
定义:
一个指针变量,该指针指向这个函数。总结来说,函数指针就是指向函数的指针。
声明格式:
类型说明符 (*函数名) (参数)
原型:
int (*fun)(int x,int y);
返回值:
本质是一个指针,自然没有返回值!!!把他当指针使用!只是有函数的功能,比如创建一个线程;
函数指针写法
函数指针是需要把一个函数的地址赋值给它,
取地址运算符&为非必需,因为一个函数标识符就表示了它的地址;
如果是函数调用,还必须包含一个圆括号括起来的参数表。
两种写法:
fun = &Function; //个人推荐:有 & 更容易理解其定义,区别开来!
fun = Function;
//这里提一下,双重指针能用到:指针的地址就是跟这里一样的啦
int* ptr1;
int* ptr2;
ptr1 = &ptr2; //&可省略
典例2 函数指针
展示了如何定义函数指针、赋值函数地址给函数指针以及通过函数指针调用函数时需要包含圆括号括起来的参数表
//特色: funcPtr = add;
#include <stdio.h>
// 定义一个函数,用于计算两个整数的和
int add(int a, int b) {
return a + b;
}
int main() {
// 声明一个函数指针,指向返回类型为int,接受两个int参数的函数
int (*funcPtr)(int, int);
// 将函数add的地址赋值给函数指针funcPtr
funcPtr = &add; // 可以不需要使用取地址运算符&
// 通过函数指针调用函数add,并传入参数,需要包含圆括号括起来的参数表
int result = funcPtr(3, 4); // 函数调用需要圆括号括起来的参数表
printf("Result: %d\n", result);
return 0;
}
典例3 指针函数与数组指针
我的困惑:发誓搞明白指针,源于此!
典型的多线程编程;
网上有很多解释:入口函数、数字签名,都是一种不错的快速上手的理解方法,但是个人认为…不够细节/底层;
//特色:创建后对应函数返回值传入就行,戏剧在于函数指针指向的函数是一个指针函数的地址
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <unistd.h>
//这是一个指针函数,返回值为int类型的指针,参数为void类型的指针;
static void *new_thread_start(void *arg)
{
printf("新线程: 进程 ID<%d> 线程 ID<%lu>\n", getpid(), pthread_self());
return (void *)0;
}
int main(void)
{
pthread_t tid;
int ret;
/*
creat第三个参数为 void *(*start_routine) (void *) 是一个函数指针;
因为start_routine是一个函数指针的标识符,需要一个函数的地址来传进去,
new_thread_start就是提前创建好的函数地址的标识符!至于它是啥函数,我指针不管(实际是一个指针函数)
第三个传参隐式含义: start_routine = &new_thread_start; "&"可省略,下面没写!!!
*/
ret = pthread_create(&tid, NULL, new_thread_start, NULL);
if (ret)
{
fprintf(stderr, "Error: %s\n", strerror(ret));
exit(-1);
}
printf("主线程: 进程 ID<%d> 线程 ID<%lu>\n", getpid(), pthread_self());
sleep(1);
exit(0);
}
最重要的来了!
creat第三个参数为 void *(*start_routine) (void *) 是一个函数指针;
因为start_routine是一个函数指针的标识符,需要一个函数的地址来传进去(所谓的入口函数);
new_thread_start就是提前创建好的函数地址的标识符!至于它是啥函数,我指针不管(实际是一个指针函数)
第三个传参隐式含义: start_routine = &new_thread_start; "&"可省略,程序没写易混淆!!!
void *(*start_routine) (void *)
是一个函数指针的声明,用来指向一个函数,该函数接受一个 void*
类型的参数并返回一个 void*
类型的指针。这种函数指针通常用于多线程编程中,作为线程的入口函数。
具体解释如下:
void*
表示指向任意类型的指针。 注意:★这行非常重要★(*start_routine)
表示这是一个指针,指向一个函数。(void *)
表示该函数接受一个void*
类型的参数。void *
表示该函数返回一个void*
类型的指针。
入口函数与签名:快速上手应用的理解方式。
void *(*start_routine) (void *) 与 void *new_thread_start(void *);
入口函数:
start_routine是一个函数指针的标识符,需要一个函数的地址来传进去(即为入口函数:new_thread_start);
数字签名:
void *()(void *)是一致的,是一个签名;
(对比括号里面!)start_routine为一个函数指针,需要指向一个函数的地址;
把指针函数new_thread_start的地址填入即可;
指针与内存分配
详见笔者另外一篇【堆区内存管理】:https://blog.csdn.net/Thmos_vader/article/details/141091368
动态分配一个 int
类型大小的内存空间,并使用 ptr
指针来管理这块内存空间;
int* ptr = (int*)malloc(sizeof(int));
/*
左边:
定义一个int类型的指针叫ptr,它指向等式右边所代表的地址;
右边:
一个int类型的指针动态分配的内存空间;
malloc(sizeof(int)) 分配了一块内存空间,并返回了指向这块内存空间的指针,
然后使用类型转换 (int*) 将这个指针转换为 int* 类型的指针;
由此笔者根据以前经验,直觉感觉到何时需要申请内存!不就是我亲爱的双重指针嘛!!!!!
函数传入的是一个指针!传入指针则需分配内存,之前内存分配文章形成闭环!!!
好好回顾为什么我要研究这么晚,就是因为这个问题!!
双重指针
定义
双重指针(double pointer)是指一个指针变量存储的是另一个指针变量的地址。
在C语言中,指针 变量用于存储内存地址,而双重指针则是指向指针变量的指针,也被称为指向指针的指针。
作用
双重指针的主要场景:函数间传递指针的地址,以便在函数内部修改指针指向的内容。
通过传递指针的地址,函数可以直接修改原指针指向的内容(多级指针),而不仅仅是修改指针的副本。这在需要动态分配内存、修改指针指向等情况下非常有用。
这种技术在处理动态内存分配、多级指针等情况下非常有用。
函数可以修改指向多个 struct ts_sample_mt 结构体的指针的值,从而实现对多个触摸点数据的存储和处理。
s_read_mt() 函数中的参数struct ts_sample_mt **samp就是一个二级指针,它指向一个指针变量,该指针变量又指向struct ts_sample_mt结构体类型的数据。
典例4双重指针
重点在于一小块的地方,就是我们学习的指针,挑出来理解就行,程序真正实现什么功能可以先不管。
单点触摸:指针与结构体
#include "tslib.h"
int ts_read(struct tsdev *ts, struct ts_sample *samp, int nr)
/*
作用:
用于从触摸屏设备中读取触摸样本(Touch sample)数据。
参数:
ts: 指向触摸屏设备的指针。
samp: 指向存储触摸样本数据(坐标、压力、分辨率)的结构体指针。
nr: 要读取的触摸样本数据的数量。
返回值:
返回实际读取的触摸样本数据的数量。
*/
/*示例代码 18.4.1 tslib 单点触摸程序*/
#include <stdio.h>
#include <stdlib.h>
#include <tslib.h> //包含 tslib.h 头文件
int main(int argc, char *argv[])
{
struct tsdev *ts = NULL;
struct ts_sample samp;
int pressure = 0;//用于保存上一次的按压力,初始为 0,表示松开
/* 打开并配置触摸屏设备 */
ts = ts_setup(NULL, 0);
if (NULL == ts)
{
fprintf(stderr, "ts_setup error");
exit(EXIT_FAILURE);
}
/* 读数据 */
for ( ; ; )
{
if (0 > ts_read(ts, &samp, 1))
{
fprintf(stderr, "ts_read error");
ts_close(ts);
exit(EXIT_FAILURE);
}
if (samp.pressure)
{//按压力>0
if (pressure) //若上一次的按压力>0
printf("移动(%d, %d)\n", samp.x, samp.y);
else
printf("按下(%d, %d)\n", samp.x, samp.y);
}
else
printf("松开\n");//打印坐标
pressure = samp.pressure;
}
ts_close(ts);
exit(EXIT_SUCCESS);
}
*struct ts_sample samp
/*
一个 struct ts_sample *类型的指针,指向一个 struct ts_sample 对象,
struct ts_sample 数据结构描述了触摸点的信息;
调用 ts_read()函数获取到的数据会存放在 samp 指针所指向的内存中。
*/
多点触摸:双重指针与结构体
int ts_read_mt(struct tsdev *ts, struct ts_sample_mt **samp, int max_slots, int nr)
/*
参数:
ts:指向一个触摸屏设备句柄的指针。
samp:一个指向指针的指针,用于存储多点触摸数据。
max_slots:表示触摸屏支持的最大触摸点数。
nr:表示要读取的触摸样本数据的数量。
*/
/*示例代码 18.4.2 tslib 多点触摸应用程序*/
#include <stdio.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#include <linux/input.h>
#include <tslib.h>
int main(int argc, char *argv[])
{
struct tsdev *ts = NULL;
struct ts_sample_mt *mt_ptr = NULL;
struct input_absinfo slot;
int max_slots;
unsigned int pressure[12] = {0}; //用于保存每一个触摸点上一次的按压力,初始为 0,表示松开
int i;
/* 打开并配置触摸屏设备 */
ts = ts_setup(NULL, 0);
if (NULL == ts)
{
fprintf(stderr, "ts_setup error");
exit(EXIT_FAILURE);
}
/* 获取触摸屏支持的最大触摸点数 */
if (0 > ioctl(ts_fd(ts), EVIOCGABS(ABS_MT_SLOT), &slot))
{
perror("ioctl error");
ts_close(ts);
exit(EXIT_FAILURE);
}
max_slots = slot.maximum + 1 - slot.minimum;
printf("max_slots: %d\n", max_slots);
/* 内存分配 */
mt_ptr = calloc(max_slots, sizeof(struct ts_sample_mt));
/* 读数据 */
for ( ; ; )
{
if (0 > ts_read_mt(ts, &mt_ptr, max_slots, 1))
{
perror("ts_read_mt error");
ts_close(ts);
free(mt_ptr);
exit(EXIT_FAILURE);
}
for (i = 0; i < max_slots; i++)
{
if (mt_ptr[i].valid)
{//有效表示有更新!
if (mt_ptr[i].pressure)
{ //如果按压力>0
if (pressure[mt_ptr[i].slot])//如果上一次的按压力>0
printf("slot<%d>, 移动(%d, %d)\n", mt_ptr[i].slot, mt_ptr[i].x, mt_ptr[i].y);
else
printf("slot<%d>, 按下(%d, %d)\n", mt_ptr[i].slot, mt_ptr[i].x, mt_ptr[i].y);
}
else
printf("slot<%d>, 松开\n", mt_ptr[i].slot);
pressure[mt_ptr[i].slot] = mt_ptr[i].pressure;
}
}
}
/* 关闭设备、释放内存、退出 */
ts_close(ts);
free(mt_ptr);
exit(EXIT_SUCCESS);
}
函数原型:
int ts_read_mt(struct tsdev *ts, struct ts_sample_mt **samp, int max_slots, int nr)
第三个参数是一个双重指针(struct ts_sample_mt* *samp):
指针samp指向一个结构体指针,外部传参需传入一个指向结构体指针的指针的地址(见下);
动态分配内存与双重指针详细操作解释
提前定义:
struct ts_sample_mt *mt_ptr = NULL;
结构体指针mt_ptr内存分配:
mt_ptr = calloc(max_slots, sizeof(struct ts_sample_mt));
传入一个结构体的地址samp,
ts_read_mt(ts, &mt_ptr, max_slots, 1))
释放内存:
free(mt_ptr);
思考
我的一切学习根源于此!!!因此花了一夜 10点到次日4点来研究透指针这玩意,哥们先睡了,明天再和室友讨论讨论。
详见笔者另外一篇【堆区内存管理】:https://blog.csdn.net/Thmos_vader/article/details/141091368
为什么 ts_read 无需申请内存来处理samp,而ts_read_mt需要?
因为需要动态分配内存;
双重指针详情:指针samp指向一个结构体指针,外部传参需传入一个指向<结构体指针>的指针的地址(见下);
而这个结构体指针指向的内容正是结构体,它是屏幕触摸点的内容,是需要修改的动态的!!!
如果是因为双重指针,那么之前不采用移植库的read_mt需要而read_ts不需要?
因为之前不是双重指针,无需动态分配。
ps:
好吧,到目前为止,中间睡觉吃饭(6.5+2h)打了2.5h的PUBG外,都在搞这些,终于弄透了,文档已成!
纪念一下吧!–2024/2/12/1/17 敏学二栋