C++ 指针详解

目录

一、指针概述

指针的定义

指针的大小

指针的解引用

野指针

指针未初始化

指针越界访问

指针运算

二级指针

指针与数组

二、字符指针

三、指针数组

四、数组指针

函数指针

函数指针数组

指向函数指针数组的指针

回调函数

指针与数组

一维数组

字符数组

二维数组


一、指针概述

指针的定义

数据是存放在内存中的,每一个内存空间都会有一个像房间号一样的编号,比如某某五星级酒店3109号房间,只有获得了这个编号才能找到这个房间,这个编号就是地址。而指针就是用来存放这个地址的变量。总结来说:指针是一个变量,是用来存放内存地址的变量,我们通常称谓指针变量。

指针的大小

是由计算机的物理性质决定的,一般的计算机分为32位机和64位机,32位机就是由32根地址线组成,每一根地址线都会传递高电平(1)或者低电平(0)。那么32根地址线就会产生2^32次方个地址,所以64位机的地址以此类推。8bite = 1字节——32位机的地址就由4个字节存储,62位机的地址就由8个字节的地址存储。

指针类型:数据的存储方式有 char、short、int、long、long long、float、double、struct、void
因此对应反指针类型也就有:char*、short*、int*、long*、long long*、float*、double*、struct*、void*

int main()
{//什么类型数据的地址就得放到对应类型的指针变量中char ch = 'w';char* pc = &ch;int num = 10;int* p = #return 0;
}

指针的解引用

指针的类型决定了,对指针解引用的时候有多大的权限(能取到多大的字节数)。比如: char* 的指针解引用就只能访问一个字节,而 int* 的指针的解引用就能访问四个字节。

野指针

指针指向的位置是不可知的(随机的、不正确的、没有明确限制的)

指针未初始化

int main()
{ //局部变量指针未初始化,默认为随机值int *p;//没有指向对应的地址空间,无法查找地址*p = 20;return 0;
}

指针越界访问

int main()
{int arr[10] = {0};int *p = arr;for(int i=0; i<=11; i++){//当指针指向的范围超出数组arr的范围时,p就是野指针*(p++) = i;}return 0;
}

指针指向的空间释放,在堆里开辟内存的空间,如果被回收就不能在使用,因此指针就不能再指向那块空间的地址。

指针运算

指针的类型决定 + - 整数,指针变量所偏移的空间大小

int main()
{float arr[5];//指针+-整数;指针的关系运算for (float *p = &arr[0]; vp < &arr[5];){//后置++,先给数组赋值在偏移四个字节*p++ = 0;}return 0;}

指针 - 指针

//模拟实现strlen
int my_strlen(char *s)
{char *p = s;while(*p != '\0' )p++;return p-s;//两个指针之间的空间个数
}

指针的关系运算,允许指向数组元素的指针与指向数组最后一个元素后面的那个内存位置的指针比较,但是不允许与指向第一个元素之前的那个内存位置的指针进行比较。

#define N_VALUES 5
int main()
{float values[N_VALUES];float *vp;//不能让数组第一个元素的地址与数组第一个元素之前的地址进行比较for(vp = &values[N_VALUES-1]; vp >= &values[0];vp--){*vp = 0;}return 0;
}

二级指针

指针变量也是一个变量,每一个变量都会存放到内存中,也就会有地址。二级指针,就是用来存放一级指针变量地址的变量。因此,存放一级指针变量地址的指针为二级指针,存放二级指针变量地址的指针为三级指针,以此类推。

 

指针与数组

可见数组名和数组首元素的地址是相同的。 因为数组是一块连续的空间,所以可以用指针指向数组首元素的地址,通过指针的加减来访问数组的每一个空间。

arr可以理解为一个变量,即一个内存空间。

二、字符指针

将字符变量的地址放到字符指针中称为字符指针。一种比较特殊的字符指针指向的是常量池中字符串首字符的地址,常量池当中的字符串是不能修改的。而将字符串放到数组当中进行存储的值是可以修改的。

char a = ‘w’;char* pa = &a;char* p = "abcdef";

备注:const加在*前表示不能修改指针所指向空间的值,const加在*后表示不能修改指针的指向

字符串数组和常量字符串的区别:

int main()
{//用相同的常量字符串去初始化不同的数组的时候就会开辟出不同的内存块//str1,str2字符串数组存放的是对应字符的asc码值//值虽然相同但是他们是两个不同数组,开辟的地址也是不同的char str1[] = "hello bit.";char str2[] = "hello bit.";//字符指针str3,str4存放的是字符串常量池的字符,存放的都是字符'h'的地址const char* str3 = "hello bit.";const char* str4 = "hello bit.";if (str1 == str2)printf("str1 and str2 are same\n");elseprintf("str1 and str2 are not same\n");if (str3 == str4)printf("str3 and str4 are same\n");elseprintf("str3 and str4 are not same\n");return 0;
}

三、指针数组

整型数组 - 存放整型的数组
字符数组 - 存放字符的数组
指针数组 - 存放指针(地址)的数组
指针数组是存放指针的数组,每种类型的数据都可以创建数组,但是数组也可以是由指针组成。

存放字符指针的数组

参数pc是是一个二级指针,存储的是abcdef字符串的地址,每次挪1字节

存放数组首地址的指针数组

四、数组指针

字符指针——存放字符地址的指针——指向字符的指针 char*
整型指针——存放整型地址的指针——指向整型的指针 int*
浮点型的指针——存放浮点型地址的指针——指向浮点型的指针 float*  double*
数组指针——存放数组地址的指针——指向数组的指针

数组名和&数组名的区别:

数组指针的使用,使用一:两种打印数组的方式

数组指针的使用,使用二:二维数组的使用

//正常的接受二维数组的参数
void print1(int arr[3][4], int r, int c)
{int i = 0;for (i = 0; i < r; i++){int j = 0;for (j = 0; j < c; j++){printf("%d ", arr[i][j]);}printf("\n");}
}//用数组指针来接受二维数组,表示的是数组的第几行
void print2(int(*p)[4], int r, int c)
{int i = 0;for (i = 0; i < r; i++){int j = 0;for (j = 0; j < c; j++){			//printf("%d ", (*(p + i))[j]);printf("%d ", p[i][j]);}printf("\n");}
}int main()
{int arr[3][4] = { {1,2,3,4}, {2,3,4,5} , {3,4,5,6} };print1(arr, 3, 4);printf("\n");//数组名arr,表示首元素的地址//但是二维数组的首元素是二维数组的第一行//所以这里传递的arr,其实相当于第一行的地址,是一维数组的地址//可以数组指针来接收print2(arr, 3, 4);return 0;
}

 

函数指针

函数指针的定义与使用

类型名  (*指针变量名)(函数参数)  = &函数名(或者直接写函数名)
//函数指针
int Add(int x, int y)
{return x + y;
}int main()
{//pf 是一个存放函数地址的指针变量 -  函数指针int (*pf)(int, int) = &Add;//可以理解为&Add给pfint ret = (*pf)(2,3);//&函数名和函数名都是函数的地址int (*pf)(int, int) = Add;//int ret = Add(2, 3);int ret = pf(2, 3);printf("%d\n", ret);return 0;
}

函数名也可以理解为一个指针变量,指针变量就是一块内存,内存中记录一个地址;变量也是一块内存直接记录值。*a取a的值,&a取a的低地址,a标识一块内存。

函数指针数组

函数指针数组的定义

数组是一个存放相同类型数据的存储空间,前面有讲到指针数组,那要把函数的地址存到一个数组中,那这个数组就叫函数指针数组。

                    数据类型 ( * 数组名[数值] ) (函数参数);
如:

int ( * parr1 [ 10 ])();
int * parr2 [ 10 ]();

函数指针数组的使用(转移表)

void menu()
{printf("*******************************\n");printf("****** 1. add   2. sub    *****\n");printf("****** 3. mul   4. div    *****\n");printf("****** 0. exit            *****\n");printf("*******************************\n");
}int Add(int x, int y)
{return x + y;
}int Sub(int x, int y)
{return x - y;
}int Mul(int x, int y)
{return x * y;
}int Div(int x, int y)
{return x / y;
}//函数指针数组存放上述函数的地址
//转移表--->传一个数,通过这个数找到对应的数组下标来调用对应的函数
int (*pf[5])(int, int) = { NULL, Add, Sub, Mul, Div };int main()
{int input = 0;int x = 0;int y = 0;int ret = 0;do{menu();printf("请选择:>");scanf("%d", &input);if (input == 0){printf("退出计算器\n");break;}else if (input>=1 &&input<=4){printf("请输入两个操作数:>");scanf("%d %d", &x, &y);ret = pf[input](x, y);printf("%d\n", ret);}else{printf("选择错误\n");}} while (input);return 0;
}

指向函数指针数组的指针

指向函数指针数组的指针是一个 指针指向一个数组, 数组的元素都是 函数指针 

void test(const char* str)
{printf("%s\n", str);
}
int main()
{//函数指针pfunvoid (*pfun)(const char*) = test;//函数指针的数组pfunArrvoid (*pfunArr[5])(const char* str);pfunArr[0] = test;//指向函数指针数组pfunArr的指针ppfunArrvoid (*(*ppfunArr)[5])(const char*) = &pfunArr;return 0;
}

回调函数

回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。

void menu()
{printf("*******************************\n");printf("****** 1. add   2. sub    *****\n");printf("****** 3. mul   4. div    *****\n");printf("****** 0. exit            *****\n");printf("*******************************\n");
}int Add(int x, int y)
{return x + y;
}int Sub(int x, int y)
{return x - y;
}int Mul(int x, int y)
{return x * y;
}int Div(int x, int y)
{return x / y;
}void calc(int (*pf)(int, int))
{int x = 0;int y = 0;int ret = 0;printf("请输入两个操作数:>");scanf("%d %d", &x, &y);ret = pf(x, y);printf("%d\n", ret);
}int main()
{int input = 0;do {menu();printf("请选择:>");scanf("%d", &input);switch (input){case 1:calc(Add);break;case 2:calc(Sub);break;case 3:calc(Mul);break;case 4:calc(Div);break;case 0:printf("退出计算器\n");break;default:printf("选择错误\n");break;}} while (input);return 0;
}

指针与数组

一维数组

int main()
{//一维数组int a[] = { 1,2,3,4 };//4*4=16printf("%d\n", sizeof(a));//16printf("%d\n", sizeof(a + 0));//a+0 其实是数组第一个元素的地址,是地址就是4/8字节printf("%d\n", sizeof(*a));//*a是数组首元素,计算的是数组首元素的大小,单位是字节,4printf("%d\n", sizeof(a + 1));//a+1是第二个元素的地址,是地址大小就是4/8printf("%d\n", sizeof(a[1]));//a[1]是第二个元素,计算的是第二个元素的大小-4-单位是字节printf("%d\n", sizeof(&a));//&a是整个数组的地址,整个数组的地址也是地址,地址的大小就是4/8字节//&a---> 类型:int(*)[4]printf("%d\n", sizeof(*&a));//&a是数组的地址,*&a就是拿到了数组,*&a--> a,a就是数组名,sizeof(*&a)-->sizeof(a)//计算的是整个数组的大小,单位是字节-16printf("%d\n", sizeof(&a + 1));//&a是整个数组的地址,&a+1,跳过整个数组,指向数组后边的空间,是一个地址,大小是4/8字节printf("%d\n", sizeof(&a[0]));//&a[0]是首元素的地址,计算的是首元素地址的大小,4/8字节printf("%d\n", sizeof(&a[0] + 1));//&a[0] + 1是第二个元素的地址,地址的大小就是4/8字节return 0;
}

字符数组

int main()
{//字符数组char arr[] = { 'a','b','c','d','e','f' };//char*//char [6]printf("%d\n", strlen(arr));//随机值printf("%d\n", strlen(arr + 0));//随机值//printf("%d\n", strlen(*arr));//strlen('a')->strlen(97),非法访问-err//printf("%d\n", strlen(arr[1]));//'b'-98,和上面的代码类似,是非法访问 - errprintf("%d\n", strlen(&arr));//&arr虽然是数组的地址,但是也是从数组起始位置开始的,计算的还是随机值//char(*)[6]printf("%d\n", strlen(&arr + 1));//&arr是数组的地址,&arr+1是跳过整个数组的地址,求字符串长度也是随机值printf("%d\n", strlen(&arr[0] + 1));//&arr[0] + 1是第二个元素的地址,是'b'的地址,求字符串长度也是随机值printf("%d\n", sizeof(arr));//arr单独放在sizeof内部,计算的是整个数组的大小,单位是字节,6printf("%d\n", sizeof(arr + 0));//arr + 0是数组首元素的地址,4/8printf("%d\n", sizeof(*arr));//*arr是数组的首元素,计算的是首元素的大小-1字节printf("%d\n", sizeof(arr[1]));//arr[1]是第二个元素,大小1字节printf("%d\n", sizeof(&arr));//取出的数组的地址,数组的地址也是地址,是地址大小就是4/8printf("%d\n", sizeof(&arr + 1));//&arr+1是跳过整个,指向数组后边空间的地址,4/8printf("%d\n", sizeof(&arr[0] + 1));//&arr[0] + 1是数组第二个元素的地址,是地址4/8字节return 0;
}

int main()
{char arr[] = "abcdef";//数组是7个元素//[a b c d e f \0]printf("%d\n", strlen(arr));//6,arr是数组首元素的地址,strlen从首元素的地址开始统计\0之前出现的字符个数,是6printf("%d\n", strlen(arr + 0));//arr + 0是数组首元素的地址,同第一个,结果是6printf("%d\n", strlen(*arr));//*arr是'a',是97,传给strlen是一个非法的地址,造成非法访问printf("%d\n", strlen(arr[1]));//errprintf("%d\n", strlen(&arr));//6printf("%d\n", strlen(&arr + 1));//&arr + 1是跳过数组后的地址,统计字符串的长度是随机值printf("%d\n", strlen(&arr[0] + 1));//&arr[0]+1是b的地址,从第二个字符往后统计字符串的长度,大小是5printf("%d\n", sizeof(arr));//7 - 数组名单独放在sizeof内部,计算的是数组的总大小,单位是字节printf("%d\n", sizeof(arr + 0));//arr+0是首元素的地址,大小是4/8printf("%d\n", sizeof(*arr));//*arr是数组首元素,大小是1字节printf("%d\n", sizeof(arr[1]));//arr[1]是数组的第二个元素,大小是1字节printf("%d\n", sizeof(&arr));//&arr是数组的地址,数组的地址也是地址,是4/8字节printf("%d\n", sizeof(&arr + 1));//&arr + 1是跳过整个数组的地址,是4/8字节printf("%d\n", sizeof(&arr[0] + 1));//&arr[0] + 1是第二个元素的地址,是4/8字节return 0;
}

int main()
{const char* p = "abcdef";printf("%d\n", strlen(p));//6- 求字符串长度printf("%d\n", strlen(p + 1));//p + 1是b的地址,求字符串长度就是5printf("%d\n", strlen(*p));//err,*p是'a'printf("%d\n", strlen(p[0]));//err - 同上一个printf("%d\n", strlen(&p));//&p拿到的是p这个指针变量的起始地址,从这里开始求字符串长度完全是随机值printf("%d\n", strlen(&p + 1));//&p+1是跳过p变量的地址,从这里开始求字符串长度也是随机值printf("%d\n", strlen(&p[0] + 1));//&p[0] + 1是b的地址,从b的地址向后数字符串的长度是5printf("%d\n", sizeof(p));//p是指针变量,大小就是4/8字节printf("%d\n", sizeof(p + 1));//p + 1是b的地址,是地址,就是4/8个字节printf("%d\n", sizeof(*p));//*p是'a',sizeof(*p)计算的是字符的大小,是1字节printf("%d\n", sizeof(p[0]));//p[0]-->*(p+0) --> *p  就同上一个,1字节printf("%d\n", sizeof(&p));//&p是二级指针,是指针大小就是4/8printf("%d\n", sizeof(&p + 1)); //&p + 1是跳过p变量后的地址,4/8字节printf("%d\n", sizeof(&p[0] + 1));//p[0]就是‘a’,&p[0]就是a的地址,+1,就是b的地址,是地址就是4/8return 0;
}

 

二维数组

int main()
{//二维数组int a[3][4] = { 0 };//printf("%p\n", &a[0][0]);//printf("%p\n", a[0]+1);printf("%d\n", sizeof(a));//48 = 3*4*4printf("%d\n", sizeof(a[0][0]));//4printf("%d\n", sizeof(a[0]));//a[0]是第一行的数组名,数组名单独放在sizeof内部,计算的就是数组(第一行)的大小,16个字节printf("%d\n", sizeof(a[0] + 1));//a[0]作为第一行的数组名,没有单独放在sizeof内部,没有取地址,表示的就是数组首元素的地址//那就是a[0][0]的地址,a[0]+1就是第一行第二个元素的地址,是地址就是4/8个字节printf("%d\n", sizeof(*(a[0] + 1)));//*(a[0] + 1)是第一行第2个元素,计算的是元素的大小-4个字节printf("%d\n", sizeof(a + 1));//a是二维数组的数组名,数组名表示首元素的地址,就是第一行的地址,a+1就是第二行的地址//第二行的地址也是地址,是地址就是4/8   //a - int (*)[4]//a+1--> int(*)[4]printf("%d\n", sizeof(*(a + 1)));//a+1是第二行的地址,*(a+1)表示的就是第二行,*(a+1)--a[1]  //16printf("%d\n", sizeof(&a[0] + 1));//&a[0]是第一行的地址,&a[0]+1是第二行的地址,地址的大小就是4/8printf("%d\n", sizeof(*(&a[0] + 1)));//*(&a[0] + 1) 是对第二行的地址解引用,得到的就是第二行,计算的就是第二行的大小printf("%d\n", sizeof(*a));//a表示首元素的地址,就是第一行的地址,*a就是第一行,计算的就是第一行的大小//*a -- *(a+0)--a[0]printf("%d\n", sizeof(a[3]));//16字节 int[4]//如果数组存在第四行,a[3]就是第四行的数组名,数组名单独放在sizeof内部,计算的是第四行的大小int a = 10;printf("%d\n", sizeof(int));return 0;
}

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

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

相关文章

pbootcms建站

pbootcms建站 一、下载pbootcms二、安装1、进入宝塔面在网站栏&#xff0c;新建站点&#xff0c;将该址里面文件全部清再将下载的pbootcms上传至该地址。 三、修改关联数据库1、在根目录下/config打开database.php照如下修改这里我使用mysqli数据库。修改并使用自已创建的数据库…

全网最新最全的自动化测试:python+pytest接口自动化-接口测试基础

接口定义 一般我们所说的接口即API&#xff0c;那什么又是API呢&#xff0c;百度给的定义如下&#xff1a; API&#xff08;Application Programming Interface&#xff0c;应用程序接口&#xff09;是一些预先定义的接口&#xff08;如函数、HTTP接口&#xff09;&#xff0c…

最长连续序列代码中的细节解读

最长连续序列 一、题目概述 给定一个未排序的整数数组 nums &#xff0c;找出数字连续的最长序列&#xff08;不要求序列元素在原数组中连续&#xff09;的长度。 请你设计并实现时间复杂度为 O(n) 的算法解决此问题。 原题地址&#xff1a;https://leetcode.cn/problems/l…

C语言入门基础知识(一)

#C语言基础知识入门 说明C语言代码块结构 #include <stdio.h> //这个语句的功能是进行有关的预处理操作。include称为文件包命令&#xff0c;后面尖括号中内容称为头部文件或收文件。 #include "demo.h" //导入的是工程内部的头文件 int main() { //main 函…

【踩坑】解决maven的编译报错Cannot connect to the Maven process. Try again later

背景 新公司新项目, 同事拷给我maven的setting配置文件, 跑项目编译发现maven报 Cannot connect to the Maven process. Try again later. If the problem persists, check the Maven Importing JDK settings and restart IntelliJ IDEA 虽然好像不影响, 项目最终还是能跑起来…

2023-2024-1-高级语言程序设计-第2次月考编程题

注&#xff1a;此前已发布过的题解不再发布&#xff08;原题请在下面位置进行搜索&#xff09;。 7-1-2 排序(算法任意) 本题要求将给定的n个整数从大到小排序后输出&#xff08;可使用任意排序算法&#xff09;。 输入格式: 输入第一行给出一个不超过10的正整数n。第二行给…

笔记66:自注意力和位置编码

本地笔记地址&#xff1a;D:\work_file\&#xff08;4&#xff09;DeepLearning_Learning\03_个人笔记\3.循环神经网络\第10章&#xff1a;动手学深度学习~注意力机制 a a a a a a a a a a a a a a a a a a a

paddleocr文本检测改进变迁

数据增强&#xff1a; BDA(Base Data Augmentation)&#xff1a;色调变换&#xff0c;透明度变换&#xff0c;旋转&#xff0c;背景模糊&#xff0c;饱和度变换。 图像变换类&#xff1a;AutoAugment&#xff0c;RandAugment 图像裁剪类&#xff1a;CutOut、RandErasing、Hi…

万字长文带你搞定MMUTLBTWU

最近一直在学习内存管理&#xff0c;也知道MMU是管理内存的映射的逻辑IP&#xff0c;还知道里面有个TLB。 今天刚刚好看到了几篇前辈的文章&#xff0c;很是不错&#xff0c;于是这里来一起学习一下吧。 PART 一&#xff1a;MMU 架构篇 MMU&#xff08;Memory Management Uni…

人工智能-A*算法-八数码问题

一&#xff0c;A*算法设计思想 A*算法&#xff08;A-star&#xff09;是一种寻路算法&#xff0c;主要用于游戏、机器人等领域。 它的设计思想是将最短路径搜索问题转化为一个优化问题&#xff0c;通过计算每个节点的评分&#xff08;f(n) g(n) h(n)&#xff09;来寻找最优…

Win7 旗舰版打开Rustdesk软件提示无法启动程序 ,计算机中丢失api-ms-win-shcore-scaling-|1-1-1.dll

环境: Win7 旗舰版 64位 Rustdesk1.19自编译客户端 问题描述: Win7 旗舰版打开Rustdesk软件提示无法启动程序 ,计算机中丢失api-ms-win-shcore-scaling-|1-1-1.dll "api-ms-win-shcore-scaling-|1-1-1.dll" 是一个系统动态链接库文件,它是Windows操作系统的一…

Ubuntu20.04/Linux中常用软件的安装

文章目录 一、安裝与卸载微信二、安裝与卸载QQ三、安装Chrome浏览器并加入apt更新四、安裝VScode4.1 安装常用插件4.2 减小Ipch缓存&#xff1a; 五、安装代码对比工具Meld六、安裝WPS七、安装PDF阅读器Foxit Reader八、安装文献管理软件Zotero九、安装有道云笔记十、安装远程控…

python pyaudio对音频进行端点检测,检测出说话区间

python pyaudio对音频进行端点检测&#xff0c;检测出说话区间 主要采用过零率和语音能量来进行检测&#xff0c;并设置双阈值。 代码如下&#xff1a; # -*- coding: utf-8 -*- import wave import os import matplotlib.pyplot as plt import numpy as np# 判断是否变号 de…

MysqlCluster集群部署

1.引言 1.1目的 部署MysqlCluster集群环境 1.2 MySQL集群Cluster原理 1. 数据分片 MySQL集群Cluster将数据分成多个片段&#xff0c;每个片段存储在不同的服务器上。这样可以将数据负载分散到多个服务器上&#xff0c;提高系统的性能和可扩展性。 2. 数据同步 MySQL集群…

layui+ssm实现数据表格双击编辑更新数据

layui实现数据表格双击编辑数据更新 在使用layui加载后端数据请求时&#xff0c;对数据选项框进行双击即可实现数据的输入编辑更改 代码块 var form layui.form, table layui.table,layer parent.layer undefined ? layui.layer : parent.layer,laypage layui.laypag…

ElasticSearch学习笔记(一)

计算机软件的学习&#xff0c;最重要的是举一反三&#xff0c;只要大胆尝试&#xff0c;认真验证自己的想法就能收到事办功倍的效果。在开始之前可以看看别人的教程做个快速的入门&#xff0c;然后去官方网站看看官方的教程&#xff0c;有中文教程固然是好&#xff0c;没有中文…

备忘录不小心删了怎么办?如何找回我的备忘录?

如果你的记性不太好&#xff0c;或者每天需要记住、完成的事情很多&#xff0c;那么养成随手记事的好习惯是非常有必要的。因为手机是每个成年人都会随身携带的电子设备&#xff0c;所以直接在手机上记录事情比较简单、便捷。而手机备忘录、便签、笔记等工具类软件&#xff0c;…

简单的界面与数据分离的架构

草图绘制于2021年2月19日 当时用到了&#xff1a;qt的子项目、delegate、view和widget的关系&#xff0c;有感而写的小备忘&#xff0c;2022年底考的软件设计师里面的设计模式虽然可能早已包含&#xff0c;但自己也得有自己啊&#xff0c;要把自己哪怕不成熟的东西也记录下来&…

HDFS客户端及API操作实验

实验二 HDFS客户端及API操作 实验目的&#xff1a; 1.掌握HDFS的客户端操作&#xff0c;包括上传文件、下载文件、重命名、查看目录等&#xff1b; 2.掌握HDFS的Java API使用&#xff0c;能够利用Java API实现上传、下载等常用操作&#xff1b; 实验内容&#xff1a; HDF…

fastadmin权限树。树形下拉框

fastadmin 笔记 权限树 在构造方法中编写相应的代码 值得一提的是&#xff0c;你的表必须有 id 字段以及 pid 字段。 // 必须将结果集转换为数组$ruleList \think\Db::name("state_list")->field(createtime,updatetime, true)->order(id ASC)->select();…