【C++】C++入门必备知识详细讲解

news2024/10/7 4:26:40

C++入门必备知识

  • 一、命名空间
    • 1. namespace
    • 2. namespace 的使用场景
  • 二、了解 C++ 中的输入和输出
  • 三、缺省参数
  • 四、函数重载
    • 1. 函数重载的概念
    • 2. C++支持函数重载的原理
  • 五、引用
    • 1. 引用的概念
    • 2. 引用特性
    • 3. 常引用
    • 4. 引用的使用场景
      • (1)做参数(传引用传参)
      • (2)做返回值(传引用返回)
      • (3)引用和指针的区别
  • 六、内联函数
    • 1. #define定义宏
    • 2. 内联函数的概念
    • 2. 内联函数的特性
  • 七、auto关键字
  • 八、基于范围的for循环(C++11)
  • 九、指针空值 nullptr

首先我们先要知道,C++是在C的基础之上,容纳进去了面向对象编程思想,并增加了许多有用的库。本章将会带大家了解,C++是补充C语言语法的不足,以及C++是如何对C语言设计不合理的地方进行优化的。

一、命名空间

1. namespace

在C/C++中,变量、函数等等都是大量存在的,这些变量、函数和类的名称将都存在于全局作用域中,可能会导致很多冲突。使用命名空间的目的是对标识符的名称进行本地化,以避免命名冲突,namespace 关键字的出现就是针对这种问题的。

例如,我们想定义一个变量 sqrt ,直接定义在全局变量然后编译是可以通过的,例如下图:

在这里插入图片描述
但是,我们知道 sqrt 其实是一个库函数,它包含在 math.h 的头文件中,假设我们加上 math.h 的头文件,还能编译过吗?答案是不能,因为它们重名了,如果包含了 math.h 的头文件,编译不会通过,会报下图中的错误:
在这里插入图片描述

那么有没有好的解决方案呢,答案是有的,C++中就增加了 namespace 这样的关键字解决这样的问题。例如我们可以将我们需要定义的变量放入 namespace 的命名空间中,然后在使用让编译器在指定的命名空间中寻找;如果不指定编译器,编译器优先会在全局域中寻找变量;namespace 的使用:

		#include <stdio.h>
		#include <math.h>
		
		// 命名空间的名字
		namespace Young
		{
			int sqrt = 10;
		}
		
		int main()
		{
			printf("%d\n", Young::sqrt);
			return 0;
		}

上述代码的使用就是让编译器在指定的命名空间 Young 中去寻找变量 sqrt 然后使用这个变量,这样就不会与库函数中的 sqrt 函数有命名冲突了;Young 是一个可以自己命名的命名空间的名字,可以取任意名字,不一定是 Young.

printf("%d\n", Young::sqrt);中,sqrt 前面的 :: 符号,叫做域作用限定符,意思是让编译器使用域作用限定符前面的命名空间中定义的东西。

2. namespace 的使用场景

除了上面我们使用 namespace 在命名空间中定义变量外,还可以定义函数、结构体等;除此之外,还可以嵌套使用。例如以下代码:

		namespace Young
		{
			//变量
			int sqrt = 10;
		
			// 函数
			int Add(int a, int b)
			{
				return a + b;
			}
		
			// 结构体
			struct ListNode
			{
				int data;
				struct ListNode* next;
			};
		
			// 嵌套使用 
			namespace Y
			{
				int a = 10;
			}
		
		}
		
		int main()
		{
			int ret = Young::Add(1, 2);
			printf("%d\n", ret);
		
			struct Young::ListNode node;
		
			printf("%d\n", Young::Y::a);
		
			return 0;
		}

上述代码中主函数部分,结构体中的域作用限定符是要在 ListNode 前使用,而不是在 struct 前使用;嵌套使用 namespace 是从右往左看,到指定的命名空间中去寻找;

虽然这种方法可以有效避免命名冲突问题,但是每次用的时候都要在前面加上域作用限定符,是不是很麻烦呢?确实是,但是还有一种方法可以解决,将命名空间展开;以上面的命名空间为例,例如以下代码:

		// 将命名空间展开
		using namespace Young;
		using namespace Y;
		
		int main()
		{
			int ret = Add(1, 2);
			printf("%d\n", ret);
		
			struct ListNode node;
		
			printf("%d\n",a);
		
			return 0;
		}

上面的代码就将 Young 和 Y 两个命名空间中的内容展开,就不用再使用域作用限定符了;除此之外,我们还可以展开部分命名空间中的内容,例如,我只展开 Add 函数出来:

		// 展开部分
		using Young::Add;
		
		int main()
		{
			int ret = Add(1, 2);
			printf("%d\n", ret);
		
			struct Young::ListNode node;
		
			printf("%d\n", Young::Y::a);
		
			return 0;
		}

以上就是展开部分的命名空间,通常在做项目的时候,我们都不会将命名空间展开,因为展开就会变得不安全;但是在平常我们在写代码练习的时候,可以将命名空间展开,更有利于我们练习。

二、了解 C++ 中的输入和输出

首先我们先要知道,C++中引入了不同于C语言的输入和输出,在C语言中我们使用 scanf 和 printf 作为输入和输出,但是在C++中了 cout 标准输出对象(控制台)和 cin 标准输入对象(键盘);我们先看看它们的使用:

在这里插入图片描述

我们可以了解到,上述代码中的 cout 和 cin 分别叫做流插入运算符流提取运算符,关于这两个更多的我们在以后的学习中再介绍;其中 cout 和 cin 必须包含< iostream >头文件以及按命名空间使用方法使用 std ,其中 std 是C++标准库的命名空间名,C++将标准库的定义实现都放到这个命名空间中。所以我们可以展开 std 的命名空间:

		#include <iostream>
		using namespace std;
		
		int main()
		{
			int input;
			double d;
			// 自动识别类型
			cin >> input >> d;
		
			cout << input << endl << d << endl;
			return 0;
		}

除此之外,cin 和 cout 还可以自动识别变量的类型,如上述代码,它的输出如下图:

在这里插入图片描述

三、缺省参数

缺省参数是声明或定义函数时为函数的参数指定一个缺省值。在调用该函数时,如果没有指定实参则采用该形参的缺省值,否则使用指定的实参。先看看缺省参数的使用:

在这里插入图片描述
在上面的使用中,Add 函数就是用了缺省参数,在 Add 函数定义中,它指定了 a = 100,b = 200,意思就是,当调用 Add 函数时,如果没有参数传进来,就使用它自己定义的变量;传参时,就使用指定的实参,如下图:

在这里插入图片描述
当然也可以只传一部分参数,但是当出现多个参数时,参数必须从右往左依次来给出,不能间隔着给;例如:

		#include <iostream>
		using namespace std;
		
		int Add(int a = 100, int b = 200, int c = 300)
		{
			return a + b + c;
		}
		
		int main()
		{
			int a = 10, b = 20, c = 30;
		
			int ret = Add(a);
			cout << ret << endl;
			return 0;
		}

以上这段的代码输出结果就是 510 ,那么例如 int ret = Add(a,,c); 这种传参是不允许的。

那么我们可以给缺省参数分类,像上面代码中,Add()这种什么都不传的就叫做全缺省参数;像Add(a)或者Add(a,b)这种只传一部分的就叫做半缺省参数

最后,我们要注意缺省参数不能在函数声明定义中同时出现,如果在函数声明和函数中同时出现,我们只需要在声明中给缺省值即可。

四、函数重载

1. 函数重载的概念

函数重载:是函数的一种特殊情况,C++允许在同一作用域中声明几个功能类似的同名函数,这些同名函数的形参列表(参数个数 或 类型 或 类型顺序)不同,常用来处理实现功能类似数据类型不同的问题。我们先看使用:

		#include <iostream>
		using namespace std;
		
		void Add(int a ,double b)
		{
			// 打印数据方便观察
			cout << "void Add(int a ,double b)" << endl;
		}
		
		
		void Add(double a, int b)
		{
			// 打印数据方便观察
			cout << "void Add(double a, int b)" << endl;
		}
		
		
		int main()
		{
		
			Add(3, 3.14);
			Add(3.14, 3);
			
			return 0;
		}

运行的结果如下:

在这里插入图片描述
以上代码中,我们在函数中打印数据,是为了说明编译器调用了这个函数;我们定义了两个同名的函数,但是它们的参数类型不一样,而我们在使用这两个函数的时候,传的参数也不一样,所以它们会调用各自对应的函数;

2. C++支持函数重载的原理

C++支持函数重载的原理是因为C++有自己的函数名修饰规则
我们知道,.cpp文件或者.c文件在生成可执行程序之前,要经过预处理,编译,汇编,链接的过程,具体回顾往期博客:预处理和程序环境;

其中,C语言在编译过程中,符号汇总将所有.c文件的函数名汇总在一起,注意,是函数名,所以在C语言中,重名的函数名在编译过程中会有冲突,编译不通过;

但是,在C++中的函数名修饰规则中,C++不是用函数名汇总在一起,而是有它自己的修饰规则,具体的修饰规则在不同的编译器有不同的修饰规则,例如:

		void func(int i, double d)
		{}
		
		void func(double d, int i)
		{}

这两个函数,在 g++ 编译器的函数修饰后变成【_Z+函数长度+函数名+类型首字母】,如图:

在这里插入图片描述
在这里插入图片描述
所以它们在编译汇总的时候是可以区分开来的。

五、引用

1. 引用的概念

引用不是新定义一个变量,而是给已存在变量取了一个别名,编译器不会为引用变量开辟内存空间,它和它引用的变量共用同一块内存空间。先看一个简单的例子:

		#include <iostream>
		using namespace std;
		
		int main()
		{
			int a = 10;
			int& b = a;
			return 0;
		}

以上代码中int& b = a;就是在定义引用类型,b 就是 a 的别名,a 和 b 实际上都是指向同一个空间,a 的改变会影响 b ,b 的改变也会影响 a.

2. 引用特性

  1. 引用在定义时必须初始化

  2. 一个变量可以有多个引用

  3. 引用一旦引用一个实体,再不能引用其他实体

     	void Test()
     	{
     		int a = 10;
     		// int& ra;   // 该语句编译时会出错
     		int& ra = a;
     		int& rra = a;
     	}
    

int& ra; 会编译出错是因为在定义时没有初始化;上述代码中,rra 是 ra 的别名,也是 a 的别名,这三个变量用的都是同一个空间,它们之间的互相改变都会影响彼此。

3. 常引用

我们在使用引用时要遵守一条规则,就是在引用的过程中,权限可以平移,权限也可以缩小,但是权限不能放大。例如:

		int main()
		{
			const int a = 0;
			// 权限的放大,不允许
			//int& b = a;
		
			// 不算权限的放大,因为这里是赋值拷贝,b修改不影响a
			//int b = a; 
		
			// 权限的平移,允许
			const int& c = a;
		
			// 权限的缩小,允许
			int x = 0;
			const int& y = x;
		
			return 0;
		}

上述代码中,权限的放大是指,const int a = 0;const修饰的 a 变量具有常性,不可修改,是只读,但是int& b = a;代表 b 的值可修改,并且 b 的值修改会影响 a ,b 是可读可写的,但是 a 只有只读,所以这里是权限的放大;但是int b = a; 不算权限的放大,因为这里是赋值拷贝,b 的修改不影响 a.

权限的平移是指,大家都具有一样的权限,例如上述代码中的const int& c = a;此处的 c 和 a 都被 const 修饰了,大家都具有常性,所以是权限的平移,是可以的。

权限的缩小在上述代码中,int x = 0; const int& y = x;是指 x 是可读可写的,但 y 被 const 修饰了,只有只读,但是从可读可写转变成只读是允许的,这种就叫做权限的缩小。

那么我们看一下以下的语句属于什么呢?

		void test()
		{
			int i = 0;
			double& d = i;
		}

首先我们应该了解清楚,如果是int i = 0; double d = i;也是可以的,因为它们之间会发生整型提升;那么我们要清楚,这个整型提升的过程中,会发生拷贝的过程,d 取的是 i 的临时拷贝,如下图,而这个临时拷贝具有常性,不可被修改,所以这里是权限的放大,是不允许的。
在这里插入图片描述
所以正确的语句应该如下:

		void test()
		{
			int i = 0;
			const double& d = i;
		}

将 d 的属性也变成不可修改,那么它们之间就是权限的平移关系了。

4. 引用的使用场景

(1)做参数(传引用传参)

我们常见的传引用传参就是交换函数了,写一个我们常用的交换函数如下:

		#include <iostream>
		using namespace std;
		
		void Swap(int* p1, int* p2)
		{
			int tmp = *p1;
			*p1 = *p2;
			*p2 = tmp;
		}
		
		int main()
		{
			int a = 10, b = 20;
		
			Swap(&a, &b);
			
			return 0;
		}

在这个交换函数中,我们需要传 a 的地址和 b 的地址过去,才能改变 a 和 b 的值;在C++中,我们可以使用引用完成同样的交换,代码如下:

		void Swap(int& p1, int& p2)
		{
			int tmp = p1;
			p1 = p2;
			p2 = tmp;
		}
		
		int main()
		{
			int a = 10, b = 20;
		
			Swap(a, b);
		
			return 0;
		}

使用了引用后,代码整体看起来就很舒服,不用像指针那样传地址和解引用;同时传引用传参还能提高传参的效率,因为每一次传址或者传值都是一次拷贝,每传一次就要多拷贝一次,效率很低;而引用则不需要拷贝,因为形参是实参的别名,就不用进行拷贝。

除此之外,传引用传参最舒服的地方还是在我们以前学过的单链表中,如往期博客 单链表 中,无论是头插还是尾插等等操作,都需要传二级指针才能改变链表的整体结构,而C++引入了引用之后,就不需要传二级指针了,如下代码:

		void SLTPushBack(SLTNode*& phead, SLTDateType x)
		{
		    // ...
		    if (phead == NULL)
		    {
		        phead = newnode;
		    }
		    else
		    {
		        //...
		    }
		}
		
		int main()
		{
		    SLTNode* plist = NULL;
		
		    SLTPushBack(plist, 1);
		    SLTPushBack(plist, 2);
		    SLTPushBack(plist, 3);
		
		    return 0;
		}

(2)做返回值(传引用返回)

在使用传引用返回时需要注意,不像传引用传参一样,传引用返回如果出了函数作用域对象还在的话才可以用,如果出了函数作用域对象不在就不能用;如以下代码:

		int& func()
		{
			int n = 0;
			n = 10;
		
			return n;
		}
		
		int main()
		{
			int ret = func();
			
			return 0;
		}

在这段代码中,函数 func 内定义了一个变量 n,但是它的生命周期只在这个函数内,出了函数作用域它的空间就会被销毁,画图更好地理解:

在这里插入图片描述
如上图,func 销毁后,n 随之也会销毁,将空间归还给操作系统,但是在 main 函数中,ret 实际上是相当于访问已经销毁的 n ,这严格来说相当于野指针问题了,也就是越界访问。

但是在不同的编译器中,得出的结果却不一样,在 vs2019 中,是可以得到 n 的值,如下图:
在这里插入图片描述

而在 gcc/g++ 的编译器中,却报错了,如下图:
在这里插入图片描述
原因是因为,这取决于栈帧销毁之后,编译器是否会对已经销毁的空间初始化,如果对已经销毁的空间进行初始化,而继续对它进行访问,就是越界,像 gcc/g++ 这样的编译器,很明显在空间回收时会对空间进行初始化,所以造成越界;而 vs2019 则没有严格的检查。

拓展:那如果将代码改成如下,还能编译通过吗?

		int& func()
		{
			int n = 0;
			n = 10;
		
			return n;
		}
		
		int main()
		{
			int& ret = func();
			cout << ret << endl;
			cout << ret << endl;
		
			return 0;
		
		}

这里将 ret 的接收改成了引用,也就是说,ret 是返回的 n 的别名,我们看执行结果:

在这里插入图片描述
第二次执行是随机值,为什么呢?原因是因为 ret 是 n 的别名,它们公用同一个空间,在执行 cout 语句时,也会发生一系列函数栈帧的创建,所以新的空间会覆盖之前的 func 所在的空间,也就是说,n 的空间被覆盖了,也就是 ret 的空间被覆盖了,所以 n 的值也就变成了随机值;第一次是 10 的原因是原来的空间并没有被覆盖。

所以就引入了另一个话题,如果 n 的空间没有被覆盖,它是不是还是 10 呢?那么我们将代码修改成以下代码:

		int& func()
		{
			int a[1000];
			int n = 0;
			n = 10;
		
			return n;
		}
		
		int main()
		{
			int& ret = func();
			
			cout << ret << endl;
			cout << ret << endl;
		
			return 0;
		}

在 func 函数内,我们增加了一个长度为 1000 的数组,我们先看运行结果:

在这里插入图片描述

这个时候又变成了 10 ,这是因为函数的栈帧中空间是向下创建的,所以在 func 函数内,先创建 1000 个空间,然后再为 n 创建空间,n 这个时候的位置是处于下方的;如果 func 销毁后,如果有新的空间覆盖,这要取决于这个空间是否比原来 func 的空间要大,如果这个空间很大,覆盖了 n ,那么 n 就会变成随机值,否则,n 还是原来的值。

那么传引用返回有什么应用场景呢?我们常见的传引用返回可以用作修改返回对象,例如在单链表中,查找函数和修改函数可以合并在一起写,使用传引用返回,这样就既可以查找到想要查找的数据,又能修改想要修改的值。例如以下代码:

		int& SLFindOrModify(struct SeqList& ps, int i)
		{
			assert(i < ps.size);
			// ...
			return (ps.a[i]);
		}
		
		int main()
		{
			// 定义对象
			struct SeqList s;
			
			// 查找 10 这个数据,并将它修改成 20
			SLFindOrModify(s, 10) = 20;
		
			return 0;
		}

(3)引用和指针的区别

现在我们都学过指针和引用了,我们可以发现,其实引用和指针很相似,在很多用法上指针可以代替引用,引用也可以代替指针,那么它们之间又有什么区别呢?我们一一分析:

引用和指针的不同点:

  1. 引用概念上定义一个变量的别名,指针存储一个变量地址。
  2. 引用在定义时必须初始化,指针没有要求
  3. 引用在初始化时引用一个实体后,就不能再引用其他实体,而指针可以在任何时候指向任何
    一个同类型实体
  4. 没有NULL引用,但有NULL指针
  5. 在sizeof中含义不同:引用结果为引用类型的大小,但指针始终是地址空间所占字节个数(32
    位平台下占4个字节)
  6. 引用自加即引用的实体增加1,指针自加即指针向后偏移一个类型的大小
  7. 有多级指针,但是没有多级引用
  8. 访问实体方式不同,指针需要显式解引用,引用编译器自己处理
  9. 引用比指针使用起来相对更安全

六、内联函数

1. #define定义宏

我们以前学过 #define定义宏,如往期博客 #define定义宏 中,宏给我们带来很多好处,如针对频繁调用的小函数,不需要建立栈帧,提高了效率;如以下代码:

		#define ADD(a,b) ((a)+(b))
		
		int main()
		{
			int ret = ADD(10, 20);
		
			cout << ret << endl;
			return 0;
		}

以上的宏定义了两个数的相加,注意,这里宏定义的((a)+(b))不能写成(a+b),因为考虑到运算符优先级问题,如ADD(1 | 2 + 1 & 2)这种表达式,加号优先级更高,会先执行加的操作,再执行 | 和 & ,并不是我们想要的结果。

上面的宏定义在预处理阶段是直接展开替换,所以没有建立栈帧,很好地提高了效率。

但是宏给我们带来好处的同时,必然会带来不便,如使用宏定义会容易出错,就如上面两数相加的宏,少一个括号都不行,所以宏的语法坑很多。

最后总结一下宏的优缺点:

优点:

  1. 没有类型的严格限制。
  2. 没有函数栈帧的建立,提高效率。

缺点:

  1. 不方便调试宏。(因为预编译阶段进行了替换)
  2. 导致代码可读性差。
  3. 没有类型安全的检查 。
  4. 容易出错,语法坑多。

2. 内联函数的概念

所以C++引入了内联函数,以 inline 修饰的函数叫做内联函数,编译时C++编译器会在调用内联函数的地方展开,没有函数调用建立栈帧的开销,内联函数提升程序运行的效率。

例如以下的两数相加的内联函数:

		inline int Add(int a, int b)
		{
			return a + b;
		}
		
		int main()
		{
			int ret = Add(10, 20);
		
			cout << ret << endl;
			return 0;
		}

以上代码中,两数相加的内联函数既没有建立函数栈帧,性能有很好的体现,也没有因为运算符问题需要添加很多括号,所以内联函数是综合了宏和函数的优缺点来设计的。

2. 内联函数的特性

(1) inline是一种以空间换时间的做法,如果编译器将函数当成内联函数处理,在编译阶段,会用函数体替换函数调用,缺陷:可能会使目标文件变大,优势:少了调用开销,提高程序运行效率。

(2) inline对于编译器而言只是一个建议,不同编译器关于inline实现机制可能不同,一般建议:将函数规模较小(即函数不是很长,具体没有准确的说法,取决于编译器内部实现)、不是递归、且频繁调用的函数采用inline修饰,否则编译器会忽略inline特性。

也就是说,假设你使用了 inline,编译器也不一定会视这个函数为内联函数,因为如果这个函数的规模很大,代码量大,会造成代码膨胀,所以综合性能方面考虑,我们如果使用内联函数,尽量要简化代码。

(3) inline 不建议声明和定义分离,分离会导致链接错误。因为 inline 被展开,就没有函数地址了,链接就会找不到。

例如我定义了一个 Test.h 的头文件,里面包含 Add 函数的声明:

		inline int Add(int a, int b);

再定义一个 Test.cpp 文件,里面包含 Add 函数的实现:

		#include "Test.h"
		int Add(int a, int b)
		{
			return a + b;
		}

然后在 main.cpp 函数中调用 Add 函数:

		#include "Test.h"

		int main()
		{
			int ret = Add(10, 20);
		
			cout << ret << endl;
			return 0;
		}

最后编译出错了,如下图:

在这里插入图片描述
这是因为什么呢?原因是因为头文件 #include "Test.h" 会在预处理阶段在 main.cpp 文件中展开,展开之后会有函数 Add 的声明,而 Add 函数前加了内联 inline,编译器会认为它就是一个内联函数,认为它就会直接展开,所以在编译阶段没有给它一个有效的地址,也就没有进入符号表;而在 main 函数中调用了 Add 函数,它在符号表中并没有找到自己对应函数的地址,所以会出现链接错误。

七、auto关键字

在 C++11 中,auto 的含义是,auto 声明的变量必须由编译器在编译时期推导而得。也就是说,auto 是一个根据变量自动推导类型的关键字。

例如:

八、基于范围的for循环(C++11)

当我们需要遍历一个数组时,通常使用以下方式:

		int main()
		{
			int arr[] = { 1,2,3,4,5,6,7,8 };
		
			for (int i = 0; i < sizeof(arr) / sizeof(arr[0]); i++)
			{
				cout << arr[i] << " ";
			}
		
			return 0;
		}

对于一个有范围的集合而言,由程序员来说明循环的范围是多余的,有时候还会容易犯错误。因此 C++11 中引入了基于范围的 for 循环。for 循环后的括号由冒号“ :”分为两部分:第一部分是范围内用于迭代的变量,第二部分则表示被迭代的范围。使用范围 for 我们可以结合上面所学的 auto 关键字结合使用,例如以下代码:

在这里插入图片描述
如果我们需要改变数组中的值,是否像以下代码那样使用呢?

在这里插入图片描述

很明显,答案是不可以的,因为 e 只是数组中的数据的临时拷贝,改变临时拷贝的值不影响数组中原来的值,所以我们要加上引用:

		int main()
		{
			int arr[] = { 1,2,3,4,5,6,7,8 };
		
			for (auto& e : arr)
			{
				e *= 2;
			}
		
			for (auto e : arr)
			{
				cout << e << " ";
			}
			return 0;
		}

加上引用后,e 就是数组中的数据的别名,改变 e 也就是改变数组中的内容。

九、指针空值 nullptr

在早期设计 NULL 空指针时,NULL 实际上就是 0,所以导致有些地方使用 NULL 会造成不明确的函数调用,例如:

在这里插入图片描述
在以上代码中,func 构成函数重载,我们期望的 NULL 是调用 void func(int*) 函数,但是它却调用了另外一个,所以这造成了不明确的函数调用。

所以在 C++11 中,引入了 nullptr,它的类型是无类型指针(void*),这很好地避免了以上的情况,例如下图,nullptr 是调用了具有指针类型的函数:

在这里插入图片描述

最后,C++ 入门的全部内容已经全部分享完啦,感觉对自己有帮助的小伙伴赶紧点赞收藏吧~感谢支持!

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

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

相关文章

No.2(3)——双指针算法实现平方数组排序

双指针算法指的是&#xff0c;从数组的两侧开辟指针变量进行查找&#xff0c;这类问题往往通过暴力&#xff08;双循环&#xff09;可以解出&#xff0c;而采用双指针相当于用空间换取时间&#xff0c;省略双层循环中重复的部分。 对于一个含有负数的有序数组&#xff0c;要求保…

Kubernetes Volume及其类型(NFS、SAN) - PV - PVC - PV与PVC与Pod的关系

目录 volume 卷 官方文档&#xff1a;卷 | Kubernetes 一、emptyDir&#xff08;临时卷&#xff09; 二、hostPath卷 type字段参数 hostPath 实验&#xff1a; 三、第3方提供的存储卷&#xff08;百度云、阿里云、亚马逊云、谷歌云等&#xff09; 四、local卷 五、NF…

Geany配置python虚拟环境(Anaconda)

Geany配置python运行环境步骤&#xff1a; 以我的conda为例 我的conda中python虚拟环境base环境python位于路径 D:\Anaconda3 打开Geany -> 生成 -> 设置生成命令 在弹出的窗口内输入以下命令 # Compile选项后修改 "D:\Anaconda3\python.exe" -m py_compi…

准备WebUI自动化测试面试?这30个问题你必须掌握(一)

本文共有8600字&#xff0c;包含了前十五个问题&#xff0c;如需要后十五个问题&#xff0c;可查看文末链接~ 1. 什么是WebUI自动化测试&#xff1f; WebUI自动化测试是指使用自动化测试工具和技术来模拟用户在Web用户界面&#xff08;UI&#xff09;上执行操作&#xff0c;并…

条件概率我知道,但什么是条件期望?--草稿

目录 1 目标问题&#xff1a; 什么是条件期望 2 条件期望&#xff0c;全期望公式 1 目标问题&#xff1a; 什么是条件期望 这次先不说目标 先引用一个小学数学题 1班平均分是93,2班平均分是95,两个班的平均分怎么算 错误算法&#xff0c;(9395)/294 除非两个班的学生数量一…

园区电能监测系统

园区电能监测系统是一种能够对园区内电能使用情况进行实时监测和管理的系统&#xff0c;可以帮助企业更好地控制能源消耗&#xff0c;提高能源利用效率&#xff0c;从而降低能源成本&#xff0c;对于推进节能减排和可持续发展具有重要意义。 园区电能监测系统通常由多个子系统组…

RocketMQ 5.0 快速入门

RocketMQ 5.0 Apache RocketMQ 自诞生以来&#xff0c;因其架构简单、业务功能丰富、具备极强可扩展性等特点被众多企业开发者以及云厂商广泛采用。历经十余年的大规模场景打磨&#xff0c;RocketMQ 已经成为业内共识的金融级可靠业务消息首选方案&#xff0c;被广泛应用于互联…

大二web作业精仿王者荣耀(html+css)

经过漫长的期末考试季节&#xff0c;我成功地完成了一个王者荣耀的仿写项目&#xff0c;并且非常高兴地与大家分享。 作品展示 作业-王者荣耀 作品目录 获取源码 1&#xff0c;复制该网站 https://download.csdn.net/download/qq_42431718/87946610 2&#xff0c;点击上方下…

Lazygit贴合 neovim

功能性要比gitui 好用&#xff0c;vim 的键位习惯 > 嵌入式数据库 &#xff0c;python 的性能够用了 … … ,分析差异&#xff0c;选择 备份和升级

ROS:action通信

目录 一、前言二、概念三、作用四、实际案例4.1需求4.2action通信自定义action文件4.2.1定义action文件4.2.2编辑配置文件4.2.3编译 4.3action通信自定义action文件调用(C)4.3.1流程4.3.2vscode配置4.3.3服务端4.3.4客户端4.3.5编译配置文件4.3.6执行 4.4action通信自定义actio…

python_day11_practice

将文本数据插入数据库 两文本文件为day10面向对象练习案例 将data_define.py文件复制过来&#xff08;导入失败&#xff0c;疑惑&#xff09; 新建数据库&#xff0c;建表orders -- CREATE DATABASE py_sql charset utf8;use py_sql;create table orders(order_date date,…

企业内部FAQ系统的搭建重要性是什么?

企业内部FAQ系统&#xff08;Frequently Asked Questions&#xff0c;常见问题解答系统&#xff09;的搭建对于企业来说具有重要的意义。它可以帮助企业有效地管理和解决员工和客户的常见问题&#xff0c;提高工作效率和服务质量。 企业内部FAQ系统搭建的重要性&#xff1a; …

k8s集群内网与办公网络打通

k8s网络 k8s节点ip段&#xff1a;192.168.1.0/24 k8s pod ip段&#xff1a; 10.233.64.0/18 k8s svc ip段&#xff1a; 10.233.0.0/18办公网络 办公电脑ip段&#xff1a;192.168.2.0/24 交换机ip&#xff1a; 192.168.8.252说明 192.168.2.0/24网段访问192.168.1.0/24网段经…

埋点数据完备性校验及结果分析

一、数据校验功能入口 入口:数据管理——数据质量——数据校验 二、操作步骤 2.1 统计时间区间 根据自身需要进行选择。一般选择埋点严重bug fix后的时间,避免脏数据过多影响分析结果。 2.2 数据抽样 根据自身需要进行选择。全量数据量较大,分析起来会复杂一些,但是结…

Win11中的Swapfile.sys

除了 pagefile.sys 和 hiberfil.sys 文件外&#xff0c;在系统根目录会多出一个 swapfile.sys 虚拟内存文件。Windows 10/8 系统为什么会同时使用 SWAP 交换文件和 Page 页面文件呢&#xff1f; 其实 swapfile.sys 文件目前只被用来交换 Universal App (其实就是Metro App)的个…

pyqt 使用pixmap展示图片时候出现失真(图片偏移)

像上图上面的情况&#xff0c; 都是经过放大、旋转等操作&#xff0c;展示图片的时候出现失真的情况 一般都是显卡的问题 需要在qimage转pixmap时&#xff0c;添加部分参数 修改办法&#xff1a; 原本是&#xff1a; pixMap QImage(self.pic_image, width, height, QImage…

JDK、JRE、JVM三者之间的关系以及区别

一、关系 JDK JRE Java 开发工具包 [Java,Javac,Javadoc,Javap等] JRE JVM Java 的核心类库 二、JDK,JRE与JVM介绍 1、JDK JDK是用于Java程序开发的最小环境&#xff0c;包含&#xff1a;Java程序设计语言&#xff0c;Java虚拟机&#xff08;JVM&#xff09;&#xff0…

喜讯!旭帆科技成功入驻“科大硅谷”!

2023年7月&#xff0c;安徽旭帆信息科技有限公司&#xff08;以下简称“旭帆科技”&#xff09;成功入驻“科大硅谷”&#xff0c;成为合肥城市发展新引擎、科创生态集群企业队伍中的一员。 “科大硅谷”项目建设总投资约75.82亿&#xff0c;共计17.37平方公里&#xff0c;是聚…

MQTT 订阅选项的使用

在 MQTT 发布/订阅模式介绍这篇博客中&#xff0c;我们已经了解到&#xff0c;我们需要先向服务端发起订阅&#xff0c;才能从服务端接收对应的消息。如果说订阅时指定的主题过滤器决定了服务端将向我们转发哪些主题下的消息&#xff0c;那么订阅选项则是允许我们进一步定制服务…

Python学习(十三)

安装包的方法&#xff1a; #python数据和json数据的相互转换 import json #准备列表&#xff0c;列表的每一个元素都是字典&#xff0c;将其转换为JSON data [{"name":"大大","age":21},{"name":"小小","age":21…