C++从入门到精通 第五章(指针与引用)

 写在前面:

  1. 本系列专栏主要介绍C++的相关知识,思路以下面的参考链接教程为主,大部分笔记也出自该教程,笔者的原创部分主要在示例代码的注释部分。
  2. 除了参考下面的链接教程以外,笔者还参考了其它的一些C++教材(比如计算机二级教材和C语言教材),笔者认为重要的部分大多都会用粗体标注(未被标注出的部分可能全是重点,可根据相关部分的示例代码量和注释量判断,或者根据实际经验判断)。
  3. 如有错漏欢迎指出。

参考教程:黑马程序员匠心之作|C++教程从0到1入门编程,学习编程不再难_哔哩哔哩_bilibili

一、内存分区模型

1、内存四区

(1)C++程序在执行时,将内存大方向划分为4个区域

①代码区:存放函数体的二进制代码,由操作系统进行管理的。

②全局区:存放全局变量和静态变量以及常量

③栈区:由编译器自动分配释放存放函数的参数值局部变量等

④堆区:由程序员分配和释放,若程序员不释放,程序结束时由操作系统回收。

(2)内存四区的意义:不同区域存放的数据将被赋予不同的生命周期,使编程更灵活。

2、程序运行前的情况

(1)在程序编译后,生成了exe可执行程序,未执行该程序前内存分为代码区和全局区。

(2)代码区存放CPU执行的机器指令

①代码区是共享的,共享的目的是对于频繁被执行的程序,只需要在内存中有一份代码即可。

②代码区是只读的,使其只读的原因是防止程序意外地修改了它的指令。

(3)全局区存放全局变量和静态变量

①全局区还包含了常量区,字符串常量和其常量也存放在此。

②该区域的数据在程序结束后由操作系统释放。

#include<iostream>
using namespace std;
//全局变量(函数体外声明的变量,在所有函数体中都可以使用)
int g_a = 10;
int g_b = 10;
//const修饰的全局变量(全局常量)
const int c_g_a = 10;
const int c_g_b = 10;int main() {//创建普通局部变量int a = 10;int b = 10;//静态变量(在普通变量前加static)static int s_a = 10;static int s_b = 10;//常量(字符串常量,const修饰的全局变量,const修饰的局部变量)cout << "字符串常量的地址为:" << (int)&"Hello world" << endl; //在全局区const int c_l_a = 10;  //const修饰的局部变量const int c_l_b = 10;cout << "局部变量a的地址为:" << (int)&a << endl;  //局部变量都不在全局区cout << "局部变量b的地址为:" << (int)&b << endl;cout << "全局变量g_a的地址为:" << (int)&g_a << endl;  //在全局区cout << "全局变量g_b的地址为:" << (int)&g_b << endl;  cout << "静态变量s_a的地址为:" << (int)&s_a << endl;  //在全局区cout << "静态变量s_b的地址为:" << (int)&s_b << endl;cout << "全局常量c_g_a的地址为:" << (int)&c_g_a << endl;  //在全局区cout << "全局常量c_g_b的地址为:" << (int)&c_g_b << endl;cout << "const修饰的局部常量c_l_a的地址为:" << (int)&c_l_a << endl;  //局部变量都不在全局区cout << "const修饰的局部常量c_l_b的地址为:" << (int)&c_l_b << endl;system("pause");return 0;
}

3、程序运行后的情况

(1)程序运行后内存会再划分出堆区和栈区。

(2)栈区由编译器自动分配释放,用于存放函数的参数值局部变量等子函数不要返回局部变量的地址,子函数返回时栈区开辟的数据由编译器自动释放,局部变量也随之被“删除”,返回其地址基本没有意义;函数将会在下一章详细介绍,这里有个印象即可)。

(3)堆区由程序员分配释放,若程序员不释放,程序结束时由操作系统回收(在C++中主要利用new在堆区开辟内存,利用delete释放内存)。

(4)举例:

①例1:

#include<iostream>
using namespace std;//栈区数据注意事项——不要返回局部变量的地址
//栈区的数据由编译器管理开辟和释放int* func(int b)  //形参数据也会放在栈区
{int a = 10;  //局部变量存放在栈区,栈区的数据在函数执行完后自动释放return &a;   //(强烈不推荐)返回局部变量的地址
}int main() {int b = 0;int * p = func(b);  //接受func函数的返回值cout << *p << endl;    //第一次可以打印正确的数据,因为编译器做了保留cout << *p << endl;    //第二次这个数据就不再保留了,会输出乱码system("pause");return 0;
}

②例2:

#include<iostream>
using namespace std;//在堆区开辟数据int * func()  
{//利用new关键字可以将数据开辟到堆区int * p = new int(10);  //指针本质也是局部变量,放在栈上,但指针保存的数据放在堆区return p;
}int main() {int * p = func();cout << *p << endl;cout << *p << endl;cout << *p << endl;system("pause");return 0;
}

二、指针

1、指针概述

(1)内存编号是从0开始记录的,一般用十六进制数字表示,可以通过指针间接访问内存,也可以利用指针变量保存地址。(下图中a是变量名,p是指针变量名)

(2)指针变量声明的一般形式:

        <数据类型> * <变量名>;

①数据类型是指针所指对象的类型,在C++中指针可以指向任何C++类型。

②变量名即指针变量名。

(3)普通变量存放的是数据,指针变量存放的是地址

(4)指针类型与所指对象之间的关系:

int *px;

指向整型变量的指针

char *pc;

指向字符型变量的指针

char *apc[10];

由指向字符的指针构成的数组,即指针数组

char( *pac)[10];

指向字符数组的指针,即数组指针

int *fpi( );

返回值为指向整型量的指针的函数,即指针函数

int( *pfi)( );

指向返回值为整型量的函数的指针,即函数指针

int( *p[4][3])( );

指针数组,数组中每个元素为指向返回值为整型量的函数的指针

int *( *pfpi)( );

指向函数的指针,该函数的返回值为指向整型量的指针

2、指针和地址

(1)在使用任何指针变量之前必须先给它赋一个指向合法具体对象的地址值,否则该指针是野指针,程序运行时会出现问题。使一个指针指向一个具体对象的方法有:

①使用new运算符(或malloc和alloc等函数)给指针分配一个具体空间。

②将另一个同类型的指针赋给它以获得值。

③通过&运算符指向某个对象。

(2)指针使用两种特殊的运算符——“*”和“&”。

①一元(单目)运算符&用于返回其操作对象的内存地址,其操作对象通常为一个变量名。

②一元(单目)运算符*用于返回其操作数所指对象的值(或者说访问所指对象),其操作对象必须是一个指针,这个操作称为解引用。

(3)举例:

#include<iostream>
using namespace std;int main() {//1、定义一个指针int a = 10;int * p;  //定义指针p = &a;   //让指针记录变量a的地址cout << "a的地址为:" << &a << endl;cout << "指针p为:" << p << endl;//2、使用指针//可以通过解引用的方式来找到指针指向的内存(指针前加*)*p = 1000;cout << "a为:" << a << endl;cout << "*p为:" << *p << endl;system("pause");return 0;
}

3、指针所占内存空间

所有指针类型在32位操作系统下是4个字节

#include<iostream>
using namespace std;int main() {//指针所占内存空间int a = 10;int *p = &a;cout << "sizeof (int *) = " << sizeof(p) << endl;cout << "sizeof (float *) = " << sizeof(float *) << endl;cout << "sizeof (char *) = " << sizeof(char *) << endl;cout << "sizeof (double *) = " << sizeof(double *) << endl;system("pause");return 0;
}

4、指针运算

(1)指针和整型量可以进行加减,若p为指针,n为整型量,则p+n和p-n是合法的,同样p++也是合法的,它们的结果同指针所指对象类型的占用字节数相关

①如若p为指向字符数组第一个元素的指针,则p+1为指向字符数组第二个元素的指针,实际增加1(1是char型数据所占的字节数)。

②如若p为指向整型数组第一个元素的指针,则p+1为指向整型数组第二个元素的指针,实际增加1个整型单位长(假如int类型长度为32位,则实际增加4个字节,可借助sizeof运算符计算某个类型的长度)。

#include<iostream>
using namespace std;int main() {//利用指针访问数组中的元素int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };int *p = &arr[0];   //int *p = arr; 等价,都是数组首地址cout << "第一个元素为:" << *p << endl; //利用指针访问第一个元素p++;                                    //让指针向后偏移四个字节cout << "第二个元素为:" << *p << endl; //利用指针访问第二个元素int *p2 = arr;for (int i = 0; i < 10; i++){cout << *p2 << "  ";p2++;}cout << endl;system("pause");return 0;
}

(2)若p1、p2为指针,当二者指向同一类型时,可以进行赋值,如:

        p2 = p1;

注:该语句使得两指针指向同一空间,若其中一个指针所指空间被删除(释放),则另一个指针所指空间亦会被删除,两个指针均变成野指针。

(3)两个指向同一类型的指针,可进行==、>、<等关系运算,其实就是地址的比较,比如数组中第一个元素的地址会小于第二个元素的地址,以此类推(不同数组的元素地址相互比较基本上没有任何意义)。

#include<iostream>
using namespace std;int main() {//字符串逆序输出char *p, *q, temp;char s[210] = "zifuchuangnixushuchu";p = s;  //s可代表字符数组s的首地址,也就是第一个元素的地址q = s + strlen(s) - 1;  //q指向字符串s的最后一个字符while (p < q){temp = *p;*p = *q;*q = temp;p++;q--;}cout << s << endl;system("pause");return 0;
}

(4)两个指向同一数组成员的指针可进行相减,结果为两个指针之间相差的元素个数,假如p指向数组头,q指向数组尾,则q-p+1表示数组长度。(指针之间不能相加)

5、指针和数组的联系

(1)在C++中,数组的名字就是指向该数组第一个元素(下标为0)的指针,即该数组第一个元素的地址,也即该数组的首地址

(2)一般情况下,一个数组元素的下标访问a[i]等价于相应的指针访问*(a+i)。需要注意的是,数组名和指针(变量)是有区别的,前者是常量,即数组名是一个常量指针,而后者是指针变量。

6、空指针、野指针和无类型指针

(1)不指向任何数据的指针称为空指针,其地址值为0,地址0处不能用于存储数据。可以用指针常量NULL(其值为0)来初始化一个指针变量,使之成为空指针。

#include<iostream>
using namespace std;int main() {//空指针//指针变量p指向内存地址编号为0的空间int *p = NULL;//cout << *p << endl;  //内存编号0 ~255为系统占用内存,不允许用户访问system("pause");return 0;
}

(2)指向非法的内存空间(不能访问的空间)的指针称为野指针。定义指针却不初始化,可能就会出现野指针。

#include<iostream>
using namespace std;int main() {//野指针//指针变量p指向内存地址编号为0x1100的空间//int *p = (int *)0x1100;//cout << *p << endl;//在程序中尽量避免出现野指针system("pause");return 0;
}

(3)可以用void来定义一个指针变量,称为无类型指针或void指针,例如:

        void *pt = NULL;

①无类型指针不与任何特定的数据类型相关联,但却可用来指向任何类型的数据。

②任何类型的指针可以赋值给无类型指针,但反过来却不行。

③无类型指针在一些特殊场合会派上用场。

7、const指针

(1)const修饰指针(const放在*之前)——常量指针:

①常量指针所指向的数据不可改动。

②常量指针本身可以改为指向其它数据。

③定义常量指针时可以不用初始化。

(2)const修饰常量(const放在*之后)——指针常量:

①指针常量所指向的数据可以改动。

②指针常量本身不可以改为指向其它数据。

③定义指针常量时必须初始化。

(3)const既修饰指针,又修饰常量——常量指针常量(前两种指针的综合):

①常量指针常量所指向的数据不可以改动。

②常量指针常量本身不可以改为指向其它数据。

③定义常量指针常量时必须初始化。

#include<iostream>
using namespace std;int main() {//1、const修饰指针  常量指针-指针指向的值不可以改int a = 10;int b = 10;const int*p1 = &a;//*p1 = 20; 这句是错的 p1 = &b;//2、const修饰常量  指针常量-指针的指向不可以改int * const p2 = &a;//p2 = &b; 这句是错的*p2 = 20;//3、const修饰指针和常量const int * const p3 = &a;//*p3 = 100;  错的//p3 = &b;    错的system("pause");return 0;
}

8、new和delete操作符

(1)使用new获得动态内存空间:

运算符new用于申请动态存储空间(用的是堆区的空间),它的操作数为某种数据类型且可以带有初值表达式或元素个数。

new返回一个指向其操作数类型变量的指针

③使用new对某种类型变量进行动态分配的语法格式如下,其中类型表示要分配的变量类型(如int、char、float等),指针指向new开辟出的空间,指针的类型与要分配的变量类型对应(如int *、char *、float *等)。

        <指针> = new <类型>;

④如果需要在分配变量的同时为其赋初值,可以在类型之后加上初值表达式(放在圆括号内),语法格式如下:

        <指针> = new <类型>(初值表达式);

运算符new还可以用来对数组进行动态分配,这时需要在数据类型后面添加方括号[],并在其中指明所要分配的数组元素个数(这个元素个数可以是一个变量,而直接定义数组的话,元素个数只能是常量,这是二者最大的区别;new返回数组首元素的地址)。

        <指针> = new <类型> [<元素个数>];

当堆区空间不足时,系统可能无法再对new提出的堆内存分配请求给予满足,此时new会返回空指针NULL,表示动态存储分配操作失败,所以最好在每次执行动态存储分配之后都检查一下new返回的指针是否为空,借助if语句,如果返回的指针为空,则须采取必要的措施。

(2)使用delete释放动态内存空间:

当动态分配的内存空间在程序中使用完毕之后,必须显式地将它们释放,这样做的目的是把闲置不用的堆内存归还给系统,使其可以被系统重新分配。

②在C++程序中由new分配的动态内存空间必须通过delete运算符释放,使用delete对动态分配的单个变量进行释放的语法格式如下,其中指针表示指向单个变量的指针。

        delete <指针>;

③使用delete对动态分配的数组进行释放的语法格式如下,其中指针表示指向数组首元素的指针(它必须是由new返回的指向动态内存空间的地址),delete之后的方括号指明将要释放的内存空间中存储着数组元素。

        delete [] <指针>;

new和delete总是成对出现的

(3)举例:

#include<iostream>
using namespace std;
int * func()  
{//new返回的是该数据类型的指针int * p = new int(10);  //指针本质也是局部变量,放在栈上,但指针保存的数据放在堆区return p;
}
void test01()
{int * p = func();cout << *p << endl;
}
void test02()
{//在堆区创建含10个整型数据的数组int * arr = new int[10];  //这个10代表数组有十个元素for (int i = 0; i < 10; i++){arr[i] = i + 100;  //给10个元素赋值}for (int i = 0; i < 10; i++){cout << arr[i] << "  ";}delete[] arr;   //释放数组的时候要加中括号
}
int main() {int * p = func();test01();cout << *p << endl;cout << *p << endl;  //堆区的数据由程序员管理开辟以及释放delete p;//cout << *p << endl;  内存已经被释放,不能合法访问test02();system("pause");return 0;
}

9、指针作为函数参数

(1)利用指针作函数参数,可以修改实参的值。(函数将会在下一章进行详细介绍,这里有个印象即可)

#include<iostream>
using namespace std;//值传递
void swap1(int a, int b)
{int temp = 1;a = b;b = temp;
}//地址传递
void swap2(int *p1, int *p2)
{int temp = *p1;*p1 = *p2;*p2 = temp;
}int main() {//int a = 10;int b = 20;swap1(a, b);cout << a << "  " << b << endl;swap2(&a, &b);   //如果是地址传递,可以修饰实参cout << a << "  " << b << endl;system("pause");return 0;
}

(2)数组名可作为参数进行传递,当数组名传给函数时,实际上传递的是数组的首地址(即数组第一个元素的地址)。

#include<iostream>
using namespace std;void bubbleSort(int *p,int a)
{int temp = 0;for (int i = 0; i < a -1 ; i++){for (int j = 0; j < a - i - 1; j++){if (*p > *(p + 1)){temp = *p;*p = *(p + 1);*(p + 1) = temp;}p++;}p = p + i +1 -a;}
}int main() {//封装一个函数,利用冒泡排序,实现对整型数组的升序排序//例如数组:int arr[10] = { 4,3,6,9,1,2,10,8,7,5 };int arr[10] = { 4,3,6,9,1,2,10,8,7,5 };int a = sizeof(arr) / sizeof(arr[0]);  //数组元素个数bubbleSort(arr, a);int *p = arr;for (int i = 0; i < a; i++){cout << *p << "  ";p++;}cout << endl;system("pause");return 0;
}

三、引用

1、引用的概念

(1)引用是个别名,建立时须用另一个数据对象(如一个变量)的名字进行初始化,以指定该引用所代表的数据对象,此后对引用的任何操作实际上就是对所代表的数据对象的操作,简单说,引用就是给变量起别名,作用跟typedef有点相似。

(2)在类型名后跟引用运算符&以及引用名来创建一个引用(引用名就是一个变量名)。

#include<iostream>
using namespace std;int main() {//引用基本语法int a = 10;int &b = a;        //a的别名为bcout << a << endl;cout << b << endl;b = 100;           //用b改值,其实也是给a改值,a和b是同一个cout << a << endl;cout << b << endl;system("pause");return 0;
}

2、使用引用需要遵循的规则

(1)创建引用时,必须立即对其进行初始化(指针则可以在任何时候被初始化)。

(2)一旦一个引用被初始化为一个对象的引用,它就不能再被改变为对另一个对象的引用(指针则可以在任何时候改变为指向另一个对象)。

(3)不可能有NULL引用。必须确保引用是对具体合法的对象的引用(即引用应和一块合法的存储空间关联)

#include<iostream>
using namespace std;int main() {int a = 10;int b = 20;//引用必须初始化//int &c ;   这显然是不行的//引用一旦初始化后,就不可以更改了int &c = b;//int &c = a; 这样也是不行的,当然,像c = a这种是赋值操作,不是更改引用cout << a << endl;cout << b << endl;cout << c << endl;system("pause");return 0;
}

3、引用的本质

引用并不会占用额外的存储空间,其本质是在c++内部实现的一个指针常量。

#include<iostream>
using namespace std;//发现是引用,转换为 int* const ref = &a;
void func(int& ref) {ref = 100; // ref是引用,转换为*ref = 100
}
int main() {int a = 10;//自动转换为 int* const ref = &a; 指针常量是指针指向不可改,这也说明为什么引用不可更改int& ref = a;ref = 20; //内部发现ref是引用,自动帮我们转换为: *ref = 20;cout << "a:" << a << endl;cout << "ref:" << ref << endl;func(a);system("pause");return 0;
}

4、用引用传递函数参数

(1)函数传参时,可以利用引用的技术让形参修饰实参,这样可以简化指针修改实参。

(2)通过引用参数产生的效果同按地址传递是一样的,引用的语法更清楚简单。

#include<iostream>
using namespace std;
//1、值传递
void mySwap01(int a, int b)
{int temp = a;a = b;b = temp;
}
//2、地址传递
void mySwap02(int *a, int *b)
{int temp = *a;*a = *b;*b = temp;
}
//3、引用传递
void mySwap03(int &a, int &b)  //引用传递,形参也会修饰实参
{int temp = a;a = b;b = temp;
}
int main() {int a = 10;int b = 20;mySwap01(a, b);cout << "a=" << a << "  b=" << b << endl;mySwap02(&a, &b);cout << "a=" << a << "  b=" << b << endl;mySwap03(a, b);cout << "a=" << a << "  b=" << b << endl;system("pause");return 0;
}

(3)在函数形参列表中,可以加const修饰形参,防止形参改变实参。

#include<iostream>
using namespace std;//常量引用使用场景:用于修饰形参,防止误操作
void showValue(const int& val) 
{//val = 1000;   如果不加const,万一在函数体内val发生变化,由于是引用传递,主函数中的a也会发生变化cout << val << endl;
}
int main() {int a = 100;int &ref = a;   //引用必须引一块合法的内存空间const int & ref2 = 10;  //加上const之后,编译器将代码修改为int temp =10 ; const int & ref2 = temp ;//ref2 = 20;  加入const之后变为只读,不可修改showValue(a);system("pause");return 0;
}

5、引用做函数返回值

(1)引用是可以作为函数的返回值存在的,但注意不要返回局部变量引用

(2)引用做函数返回值时,函数的调用可以为左值。

#include<iostream>
using namespace std;
//引用做函数的返回值
//不要返回局部变量的引用
//函数的调用可以作为左值
int& test01()
{int a = 10;  //局部变量,存放在栈区return  a;
}
int& test02()
{static int a = 10;   //静态变量,存放在全局区(或者说堆区),程序结束后再由系统释放  return  a;
}int main() {int &ref1 = test01();cout << "ref1= " << ref1 << endl;//cout << "ref= " << ref << endl;  //此时a的内存已经释放,不能访问了int &ref2 = test02();cout << "ref2= " << ref2 << endl;cout << "ref2= " << ref2 << endl;test02() = 1000;        //函数的调用可以作为左值,test02返回的是a的引用,这里相当于a = 1000cout << "ref2= " << ref2 << endl;cout << "ref2= " << ref2 << endl;system("pause");return 0;
}

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

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

相关文章

怎么把ppt压缩到10m以内?立刻学会~

在分享PPT文件时&#xff0c;文件大小通常会成为一个重要考虑因素。过大的文件不仅会增加传输和下载时间&#xff0c;还可能遇到一些网络限制。本文将介绍如何将PPT文件压缩到10M以内的方法&#xff0c;并详细介绍使用压缩工具的解决方案。 使用压缩工具的解决方法 工具一&…

http协议基础与Apache的简单介绍

一、相关介绍&#xff1a; 互联网&#xff1a;是网络的网络&#xff0c;是所有类型网络的母集因特网&#xff1a;世界上最大的互联网网络。即因特网概念从属于互联网概念。习惯上&#xff0c;大家把连接在因特网上的计算机都成为主机。万维网&#xff1a;WWW&#xff08;world…

Excel面试题及答案(1)

1.辅助列添加,快速填充方式填充隔行的编号;定位条件定位到空值后,右击---插入整行 2.利用通配符计算A3:A9含有车间的单元格个数(保留计算公式)。 3.利用身份证号提取 “性别”、“年月日”、“年龄” 性别:利用mid()方法,添加了一列辅助列,根据提取身份证后面第2位…

【Python笔记-设计模式】工厂模式

一、说明 (一) 解决问题 提供了一种方式&#xff0c;在不指定具体类将要创建的情况下&#xff0c;将类的实例化操作延迟到子类中完成。可以实现客户端代码与具体类实现之间的解耦&#xff0c;使得系统更加灵活、可扩展和可维护。 (二) 使用场景 希望复用现有对象来节省系统…

Leetcoder Day17| 二叉树 part06

语言&#xff1a;Java/C 654.最大二叉树 给定一个不含重复元素的整数数组。一个以此数组构建的最大二叉树定义如下&#xff1a; 二叉树的根是数组中的最大元素。左子树是通过数组中最大值左边部分构造出的最大二叉树。右子树是通过数组中最大值右边部分构造出的最大二叉树。 …

力扣随笔之两数之和 Ⅱ -输入有序数组(中等167)

思路&#xff1a;在递增数组中找出满足相加之和等于目标数 定义左右两个指针&#xff08;下标&#xff09;从数组两边开始遍历&#xff0c;若左右指针所指数字之和大于目标数&#xff0c;则将右指针自减&#xff0c;若左右指针所指数字之和小于目标数&#xff0c;则左指针自加&…

petalinux_zynq7 驱动DAC以及ADC模块之五:nodejs+vue3实现web网页波形显示

前文&#xff1a; petalinux_zynq7 C语言驱动DAC以及ADC模块之一&#xff1a;建立IPhttps://blog.csdn.net/qq_27158179/article/details/136234296petalinux_zynq7 C语言驱动DAC以及ADC模块之二&#xff1a;petalinuxhttps://blog.csdn.net/qq_27158179/article/details/1362…

【Unity】MySql +Navicat 安装教程

问题描述 在使用Unity开发的时候&#xff0c;有的时候我们是需要使用Mysql数据库的&#xff0c;本教程使用的MySql 和Navicat均为免安装版 ❶mysql安装 1.下载mysql解压至任意目录&#xff0c;此处以“C:\mysql-5.6.39-winx64”为例. mysql百度云连接&#xff1a; 链接&…

mybatis 集成neo4j实现

文章目录 前言一、引入jar包依赖二、配置 application.properties三、Mybatis Neo4j分页插件四、Mybatis Neo4j自定义转换器handler五、MybatisNeo4j代码示例总结 前言 MyBatis是一个基于Java语言的持久层框架&#xff0c;它通过XML描述符或注解将对象与存储过程或SQL语句进行…

有名管道的大小

管道&#xff1a;有名管道、无名管道 通信&#xff1a; 单工通信&#xff1a;固定的读端和写端 -- 广播 半双工通信&#xff1a;同一时刻&#xff0c;只有有一方写&#xff0c;另外一方读:对讲机 全双工通信&#xff1a;随时两方都能读写 -- 电话 特点&#xff1a; 管道属…

小红书x-s算法及补环境 单旋转验证码

前言 大家好呀!新的一年,先祝大家新年快乐咯.祝大家逆向,风控都一把过咯. 新年第一篇文章,后续会持续更新哦! 春晚见证了中国经济的新风口,今年春晚互联网企业赞助商就两家,小红书和京东.小红书类似国外的ins,有预感未来小红书会大火,所以写了这篇文章,有需要的加我,联系方式…

统计图扇形图绘制方法

统计图扇形图绘制方法 常用的统计图有条形图、柱形图、折线图、曲线图、饼图、环形图、扇形图。 前几类图比较容易绘制&#xff0c;饼图环形图绘制较难。 还有一种扇形图的绘制也较难&#xff0c;扇形图的各个变类&#xff0c;饼图、环形图、半圆图、玫瑰图等都是统计图扇形的变…

线程的同步(synchronized的原理和用法,解决线程同步时的通信问题)

线程的同步锁&#xff08;synchronized&#xff09; 为什么会出现线程的同步锁&#xff1f; 因为JVM虚拟机是抢占调度模型&#xff0c;当多个线程在同时访问一个资源时会发生两个线程争抢一个资源&#xff0c;在一个线程没有执行结束时&#xff0c;另一个线程抢到资源&#x…

动态规划--持续更新篇

将数字变成0的操作次数 1.题目 2.思路 在numberOfSteps函数中&#xff0c;首先设置f[0]为0&#xff0c;因为0已经是0了&#xff0c;不需要任何步骤。然后&#xff0c;使用一个for循环从1迭代到输入的整数num。对于每个整数i&#xff0c;如果i是奇数&#xff0c;则将f[i]设置为…

igolang学习3,golang 项目中配置gin的web框架

1.go 初始化 mod文件 go mod init gin-ranking 2.gin的crm框架 go get -u github.com/gin-gonic/gin 3.go.mod爆红解决

uniapp开发微信小程序跳转到另一个小程序中

注意&#xff1a;一开始我的云上务工模块是单独的tabbar界面&#xff0c;但是小程序跳转好像不能直接点击tabbar进行&#xff0c;所以我将这里改成了点击首页中的按钮进行跳转 点击这里进行小程序跳转 目录 基础讲解 uniapp小程序跳转的两个方法 调用说明&#xff08;半屏跳转…

文件上传漏洞 (upload部分通关教程)

一、抓包修改后缀名绕过前端验证&#xff0c;实现上传木马&#xff08;第一关&#xff09; 只适合只有前端验证的网站 一句话木马&#xff1a; <?php eval($_POST[a]); ?> 保存到文件中&#xff1a; 使用第一关直接上传&#xff0c;报错&#xff1a; 按照网站要求…

国内Twitter账号注册要注意什么?注册多个Twitter账号如何防止被封?

在这个数字化快速发展的时代&#xff0c;Twitter作为一个全球性的社交媒体平台&#xff0c;对于个人品牌塑造乃至跨境电商均有着不可忽视的影响力。然而&#xff0c;在中国大陆地区&#xff0c;对于Twitter账号注册以及如何安全地注册多个Twitter账号这样的话题&#xff0c;往往…

《初阶数据结构》尾声

目录 前言&#xff1a; 《快速排序&#xff08;非递归&#xff09;》: 《归并排序》&#xff1a; 《归并排序&#xff08;非递归&#xff09;》&#xff1a; 《计数排序》&#xff1a; 对于快速排序的优化&#xff1a; 分析&#xff1a; 总结&#xff1a; 前言&#xff1a…

vue3 toRefs之后的变量修改方法

上效果 修改值需要带上解构之前的对象名obj&#xff0c; changeName:()>{ // toRefs 解决后变量修改值方法&#xff1a; 解构前变量.字段新值 obj.name FEIFEI; } } 案例源码 <!DOCTYPE html> <html> <head><me…