C++深入学习part_1

Linux下编译C++程序

安装g++命令:sudo apt install g++

编译命令:$ g++ *.cc 或者 *.cpp -o fileName;

hellworld

在这里插入图片描述

编译程序可以看到:
在这里插入图片描述

namespace命名空间

首先,命名空间的提出是为了防止变量重名冲突而设置的。
浅浅试一下:
在这里插入图片描述
现在我们进行编译的时候会发现报错:
在这里插入图片描述
从报错提示可以看到,他希望我们使用wd::display()的方式来调用该函数。(::称为作用域限定符)
这是因为display函数是定义在namespace命名空间里的,所以想要使用其内置成员的时候我们需要加上其对应的空间名:
在这里插入图片描述
再来编译:
在这里插入图片描述
完美运行通过。

命名空间还可以嵌套使用:
在这里插入图片描述
完美运行:
在这里插入图片描述
这是命名空间的一种使用方式,还有一种则是如下:
在这里插入图片描述
使用了using编译指令之后就可以不用再带对应的命名空间名了,因为上图中using编译指令会将std该空间的所有实体全部引入。

注意:第二种方式使用时必须知道该空间中有哪些实体,如果不知道,这样的写法就依然存在可能造成冲突的风险。

什么意思?
我们来试一下,这里使用std标准命名空间测试:
在这里插入图片描述
可以看见我们的cout函数具有二义性,因为std中也有一个该函数,编译会报错:
在这里插入图片描述
ambiguous:二义性。

所以初学时大型项目里面最好不要使用using编译指令,因为有可能造成冲突(你并不知道std中有多少函数)。

推荐使用using声明机制,即:using std::cout; 它只引入这一个实体。
另外在命名空间中直接定义的实体,不要直接缩进。

匿名命名空间

其实就是不带空间名就是匿名的命名空间,匿名命名空间可以直接使用其内部定义的实体。

#include <iostream>
using namespace std;
//匿名命名空间
namespace {int number = 4;}int main(void){cout << number << endl;return 0;
}

我们看存在的一种情况:

#include <iostream>
using namespace std;
//匿名命名空间
namespace {int number = 4;}int number = 5;int main(void){cout << number << endl;return 0;
}

此时不出意料肯定会报错,因为具有二义性:
在这里插入图片描述
所以我们为了强调我们用的是哪个number,就需要使用匿名空间的作用域操作符:

#include <iostream>
using namespace std;
//匿名命名空间
namespace {int number = 4;//而这个变量只能在本模块内部使用}int number = 5; //该全局变量可以跨模块使用,即可以在另一个.cpp文件中使用
//所谓模块:一个*.c/*.cc/*.cpp的文件就可以叫做一个模块//同理:
static int s_number = 5; //也只能在本模块内部使用int main(void){//使用作用域操作符来使用匿名命名空间cout << ::number << endl;return 0;
}

运行就没有问题了,因为我们强调了使用命名空间中的number:
在这里插入图片描述

跨模块调用extern关键字

当我们要跨模块调用另一个cpp文件中的变量或者函数时,需要使用extern关键字。
我们在hello.cpp文件中写上g_number = 100;

#include <iostream>
using namespace std;//等待被namespace1.cpp调用的变量
int g_number = 100;//这里要注意嗷,我们上面的所谓跨模块调用意思是这些模块本身就属于同一个项目
//而一个项目只能有一个main函数,所以这里我们注释掉hello.cpp中的main函数
//int main() {//      cout << "hello world" << endl;//    return 0;
//}

然后我们在namespace1.cpp文件中通过extern关键字来引用它:

#include <iostream>
using namespace std;
//匿名命名空间
namespace {int number = 4;//而这个变量只能在本模块内部使用}int number = 5; //全局变量可以跨模块使用,即可以在另一个.cpp文件中使用//使用hello.cpp文件中的g_number变量
extern int g_number;int main(void){//使用作用域操作符来使用匿名命名空间cout << ::number << endl;//打印g_numbercout << g_number << endl;return 0;
}

编译运行:
在这里插入图片描述

另外,在同一个模块中可以定义多次命名空间;在不同的模块中也可以定义多次命名空间:

#include <iostream>using namespace std;//第一次定义命名空间wd
namespace wd{void show(); //这里是第一次声明实体show()
}int main(){//调用wd中的show()wd::show();return 0;
}//这里我们第二次定义命名空间wd
namespace wd{//第二次定义实体showvoid show();
}//这里我们第三次定义命名空间wd
namespace wd{//第三次声明并且定义实体showvoid show(){cout << "我是第三次被声明啦" << endl;}
}

注意虽然命名空间可以随便声明,但是它里面的函数声明可是只能有一次定义嗷(就和正常的函数一样)。
编译运行:
在这里插入图片描述
不光是本文件可以重复声明命名空间,跨文件(或者说跨模块)也一样可以,这里我们在namespace3文件中定义一个同名wd:

#include<iostream>
using namespace std;//在namespace3.cpp文件中定义重名namespace wd
namespace wd
{void print(){cout << " 我是跨模块的命名空间嗷  " << endl;}
}

然后我们返回到刚刚的测试文件中去调用它:


#include <iostream>using namespace std;//在这里调用跨文件的namespace wd
namespace wd{void print();
}//第一次定义命名空间wd
namespace wd{void show(); //这里是第一次声明实体show()
}int main(){//调用wd中的show()//wd::show();//调用跨文件的namespace3.cpp中的wdwd::print();return 0;
}//这里我们第二次定义命名空间wd
namespace wd{//第二次定义实体showvoid show();
}//这里我们第三次定义命名空间wd
namespace wd{//第三次声明并且定义实体showvoid show(){cout << "我是第三次被声明啦" << endl;}
}

编译运行:
在这里插入图片描述

总结:
1、命名空间的提出是为了防止变量重名冲突而设置的,可以嵌套使用
2、去除了命名空间名就是所谓的匿名命名空间,匿名命名空间的实体无法跨模块调用。
3、在同一个模块中可以定义多次命名空间,在不同的模块中也可以定义多次命名空间。

const修饰类型和对象

const:修饰类型或对象成为常量值的关键字,常量值不可以改变且必须初始化。

#include <iostream>using namespace std;#define MAX 1000void test(){int a;//const int b; 必须要继续初始化,否则报错const int c = 1;//c = 2; error 常量是不能进行修改的//有同样效果的还有宏定义#definecout << MAX << endl;
}int main(){test();return 0;}

宏定义与const常量的区别(面试常考)

1、发生的时机不一样:
宏定义是在预处理时,而const常量是在编译时
2、类型检查不一样:
宏定义是没有类型检查的,只是简单做了字符串的替换,虽然也有编译阶段,但在编译阶段没有报错,将出错的时机推迟到了运行时,但运行时阶段的错误是更难发现的;而const是由类型检查的,这样更加安全一些

那么什么叫宏定义只是作了字符串的简单替换呢?
这里我们举例说明:

#include <iostream>using namespace std;//举例说明为什么宏定义只是进行了简单的字符串替换
#define kBase 3+4void test(){int a = 10;int d = a * kBase;cout << "d: " << d << endl;
}int main(){test();return 0;}

上面代码理想的值应该是得到10 * (3 + 4) = 70,但编译运行结果为:
在这里插入图片描述
我们可以用如下命令去查看预处理阶段的代码长什么样:
在这里插入图片描述
上面的constL.cpp和constL.i都是文件名,.i文件就是我们的预处理文件,打开它可以看见:
在这里插入图片描述

3+4被当作字符串一样直接替换了kBase,所以最后的结果就成了10*3+4-34。

总结:要定义常量时最好使用const或者枚举enum类型。

const修饰指针

# include <iostream>using namespace std;int main(){int a = 10;//这种形式是常量指针,表示p1所指向的a对象的值不可以改变//即p1也可以指向别人,如p1 = &b;//但(*p1) = 20; 企图修改a的值就是错误的,该值不可改变const int* p1 = &a;//int const* p2 = &a; 这种格式和上面p1指针是一个意思,且不怎么用//这种形式是指针常量,表示p3所指的地址值不可以改变//即p3不可以指向别人了,如p3 = &b; 就是错误的,指向不可改变//而(*p3) = 30; 这是可以的,其所指对象的值可以改变int* const p3 = &a;
}

C++堆空间申请方式以及内存泄露

C语言中申请堆内存空间使用的是malloc和free函数。
在C++中也有类似的方式:new表达式与delete表达式。

int * pint = new int(10); //new表达式申请空间的同时,也进行了初始化delete pint; //释放申请的堆空间

简单尝试:

#include <iostream>using namespace std;int main(){//new表达式执行完毕之后,返回的是相应类型的指针int* pint = new int(1);cout << "*pint  = " << *pint << endl;
}

运行编译:
在这里插入图片描述
但此时我们的代码是有问题的,因为没有释放掉我们的pint空间,即发生了内存泄漏。
那我们怎么检测我们的程序是否发生了内存泄露呢?
答案是使用一些内存检测工具,比较常用的如:valgrind
执行下面的命令下载它:
在这里插入图片描述

内存泄露检测工具-valgrind(面试高频考点)

下载完毕后我们执行以下操作:
在这里插入图片描述

上面的操作结束后现在我们就可以直接使用别名命令memcheck来检测内存泄露了,我们来检测一下我们刚刚的内存是否存在泄露:
在这里插入图片描述
从in use at exit:4bytes in 1 blocks等信息中可以看出,存在内存泄露问题,着就是内存检测工具valgrind的简单使用。

所以要记得回收内存啊!

关于new还有一种使用方式:

#include <iostream>using namespace std;int main(){//1、第一种new的使用方式//new表达式执行完毕之后,返回的是相应类型的指针int* pint = new int(1);cout << "*pint  = " << *pint << endl;delete pint;//2、第二种new的使用方式//new表达式要申请的空间为数组int* parr = new int[10];//注意数组的堆空间申请和释放的语法嗷,中括号别掉delete[] parr;}

引用

在这里插入图片描述

引用作为函数参数传递

首先回忆一下之前的几种参数传递方式:

1、值传递

还是使用经典的交换两个变量值的内容来作例子:
在这里插入图片描述
由上图可知,在使用值传递时,其实传递的仅仅是变量值的拷贝,而我们建立的在swap函数中定义的tmp值也不过是个临时变量,当swap函数执行结束后tmp变量也就随即消失了。所以值传递并不能实现交换两个值的内容,这是由于两个函数并不共享一块内存空间决定的,虽然它们都存在在栈空间内。
注意图中的箭头仅仅意味着进行了一个参数的拷贝而已,即a1的值拷贝给了变量x。

2、地址传递

在这里插入图片描述
地址传递就不一样了,上图明显可以看见指针px指向了a的地址,指针py指向了b的地址。
所以对两个指针解引用可以得到:*px = 1; *py = 2
在swap函数中,第20行代码tmp暂存了 *px的值,然后第二十一行中,px所指地址空间上的值被修改成了 *py的值,即 *px = 2;
第22行代码则将 *py的值变成了 1。
所以达到了我们想要交换两个变量值的目的:
在这里插入图片描述

3、引用传递

在这里插入图片描述
这就没啥好说的了,因为引用其实就是别名,所以操作x和y其实就是在直接操作a和b罢了。

引用的出现就是为了替代指针,尽量让程序员减少犯错的概率。
其底层实现依然是指针,而且是一个受限制的指针,即引用一旦被绑定到某个对象上后就不能够解绑去绑定到别的对象上。

引用作为函数的返回值

在这里插入图片描述
在这里插入图片描述

强制转换与函数重载

C风格强制转换:

TYPE a = (TYPE) EXPRESSION;

但这种风格存在缺陷,就是安全性不足,无法保证转换之后类型的合法性。

而C++风格的强制转换就不一样了

有四种:
1static_cast(最常用,比如常见的指针转换:把void*转换成其它类型的指针)2const_cast(去除常量属性)3dynamic_cast(动态类型转换,只用在多态时基类与派生类之间的转换)4reinterpret_cast(在任意类型之间轻易转换,但是不要轻易使用,用的最少)

上面四种转换方式只是含义不一样,但写法是通用的,形式都如下:
在这里插入图片描述

static_cast

这个没什么好讲的,就是正常用就型:

int* p = static_cast<int*> (malloc(sizeof(int)));

上面这一句代码就是一个很典型的应用,在C风格中malloc函数返回的是void*,如果要使该行代码不报错的话,就必须进行类型转换,那么此时用static_cast是非常合适的。

const_cast


#include <iostream>using namespace std;void display(int* p){ //明显要求传入一个非const指针*p = 10;cout << "*p = " << *p << endl;}int main(){const int a = 1;display(&a);//在实参传递时,只有const变量,如果传递成功的话就有修改a的值的风险}

如上面注释所说,这肯定是报错的:
在这里插入图片描述
那如果我们一定要传这个const常量参数呢?
那就可以用上const_cast了:

#include <iostream>using namespace std;void display(int* p){ //明显要求传入一个非const指针*p = 10;cout << "*p = " << *p << endl;}int main(){const int a = 1;//进行了去除const的强制类型转换display(const_cast<int*> (&a));}

现在再运行就没有问题了:
在这里插入图片描述
虽然我们使用了const_cast进行转换,但是我们并没有真正改变const变量的值,这一点要注意,即上面代码中的a的值是没有变化的。

而且更有意思的是,当我们打印指针p的值(即变量a的地址)的时候会发现,它的地址居然和常量a是一模一样的:

#include <iostream>using namespace std;void display(int* p){ //明显要求传入一个非const指针*p = 10;cout << "*p = " << *p << endl;cout << "p所指地址为: " << p << endl;
}int main(){const int a = 1;//进行了去除const的强制类型转换display(const_cast<int*> (&a));cout << "a的地址为: " << &a << endl;}

运行结果:
在这里插入图片描述
这就很扯:地址值是一样的,但是值不一样。

所以迷惑性很强,一般情况下最好不要用const_cast。(这里很多资料里面都没有一个明确的说法,据说是*p的值存在了所谓的寄存器中,并没有真正写入内存啥的,反正知道有这么回事就行了)

dynamic_cast和reinterpret_cast两个就不讲了,基本用不到

函数重载

在这里插入图片描述

C语言不支持函数重载!

在这里插入图片描述
在这里插入图片描述
由上图可以发现,确实对于不同的重载函数其实就是改变一下对应的名字来调用而已。add是函数名,然后add后面的ii就是参数列表中各个参数的缩写。

C++与C的混合编程

上一节我们知道了C++在内部是使用了名字改编的原理来支持函数重载的,但是C语言不支持函数重载自然也就没有所谓名字改编的操作,这就导致了C和C++在进行混合编程的时候会出现一些兼容问题:

在这里插入图片描述

为什么需要进行混合编程:很明显,C比C++早十二年出来,很多库都是C写的,C++只能去兼容和适应C的法则;

为了解决这样的问题,我们引入了下面的方式来解决兼容性问题:

在这里插入图片描述
上图右侧就是混合编程的编译结果,示例代码如下:

#include <iostream>using namespace std;//用C语言的方式来调用该函数
extern "C"{//只要放在该区域的代码,就会按照C的方式进行调用//不会进行名字改编int add(int x, int y){return x + y;}
}// end of extern "C"//下面这些重载函数都是按C++方式来进行调用的
long add(long x , long y){return x + y;
}int add (int x,int y,int z){return x + y + z;
}int add(int x,long y){return x + y;
}int add(long x, int y){return x + y;
}int main(){return 0;
}

上述这是 extern "C"声明在实现文件.cpp文件中的情况,但是如果是在头文件中情况又当如何呢?

在头文件中,文件是有可能被C编译器编译的,也有可能是被C++编译器编译的,自然的说C编译器肯定是不需要上面那段extern ''C"就能编译,会节省时间,而C++编译器则需要这段代码,如何做才能得到这样的效果?

答案是使用C++中的条件编译,宏定义:
在这里插入图片描述
所以在头文件中加上上述内容:

//宏_cplusplus只有C++的编译器才会定义
//C的编译器没有该宏
//意思就是只有该被包围起来的代码是被C++编译器编译时才会出现
//若是被C编译器编译的话就不会出现
#ifdef _cplusplus
extern "C"
{
#endifint add(int x,int y){return x + y;}
#ifdef _cplusplus
}
#endif

通过上述方法就可以完美解决C与C++的混合编程问题。

默认参数

在这里插入图片描述
这其实没啥好说的,就注意一下上面说的一个点:
默认参数的设置要求必须从右到左进行;
另外设置默认参数的时候要注意是否有其它的重载函数与其设置了默认参数的参数列表产生调用时的二义性就行。

inline函数

首先在C语言中,其实有类似的语法,函数宏定义:

#include <iostream>using namespace std;//C语言中的函数宏定义
#define multiply(x,y) x * yint main(){int a = 3, b = 4;int c = 5, d = 6;cout << multiply(a,b) << endl;//输出为12//但还是之前的问题,宏定义只是简单替换成了字符串//所以下面的语句其实是:a+b*c+d = 29cout << multiply(a+b,c+d) << endl;
}

编译运行:
在这里插入图片描述
接下来我们看在C++中有同样功能的inline函数:

#include <iostream>using namespace std;//C语言中的函数宏定义
#define multiply(x,y) x * y//C++中的inline函数
//为什么有inline函数:就是因为每次函数的调用都是绝对有开销的(比如栈空间的消耗)
//那么加上inline关键字的话,在编译时编译器会将该函数进行语句的替换
//下面的函数调用就会被替换成语句 x / y,极大的提升了效率
//它的效率与宏函数保持一致,还更加安全inline int divide(int x,int y){return x / y;
}int main(){int a = 3, b = 4;int c = 5, d = 6;cout << multiply(a,b) << endl;//输出为12//但还是之前的问题,宏定义只是简单替换成了字符串//所以下面的语句其实是:a+b*c+d = 29cout << multiply(a+b,c+d) << endl;//调用inline内联函数cout << divide(d,a) << endl;
}

为了降低犯错误的概率,尽量使用inline内联函数。

内联函数的使用要求

在这里插入图片描述

C++内存布局(面试常考)

在这里插入图片描述
上图是每一个进程被装载到内存中运行时的内存空间分布示意图,每个进程被装载到内存中运行时都会有如上几个区。
32位操作系统意思就是每一次读写数据的话只能读写32个位也就是四个字节,因为只有32根地址线来传送数据(所以32根地址线最大传送的数据就是当这32个位全为1的时候,最小就是当这三十二个位全为0的时候,这就决定了该类型操作系统的内存地址空间范围),那么2的32次方也就是4G大小的内存空间,其中一部分用来作OS的系统空间,即上图中的内核态,用来运行一些内核程序,而剩下的部分就是用户去区,也就是上图的用户态,用户进程(也就是我们所编写的程序)都会运行在用户态中。我们的C++程序也一样会运行在用户态里,只不过完整的程序根据其代码的不同会被分到不同的内存区域中,其中栈区总是位于虚拟地址的高位部分,向低地址方向进行生长,而堆区则在其下面由低地址向高地址生长,全局/静态区(或者说读写段)和只读段(或者说文字常量区和程序代码区)则依次往下存放。

接下来我们来一一验证,通过本次学习以后必须清楚自己写下的每一句代码中的数据是存储在哪个空间里的。

#include <iostream>using namespace std;int gNumber = 1;static long sNumber = 2;const int kNumber = 3;void test(){//对于使用指针声明的字符串,该字符串位于文字常量区,声明时应该加上const否则会有警告//所以正确的声明应该是:const char* pstr = "hello,world";char* pstr = "hello,world";//*ptr = 'H'; 错误,因为文字常量区是只读区域,所以从侧面反映了其确实位于文字常量区//该字符串位于栈上,相当于用"hello,world"字符串去初始化了这个字符数组char pstr2[] = "hello,world";int number = 1;const int number2 = 1;const int* const p = &number2;static int sLocalNumber = 10;//pint本身位于栈上int* pint = new int(1); //堆区delete pint;printf("pstr: %p\n",pstr);printf("&pstr: %p\n",&pstr);/*这里要注意辨析一下pstr2和&pstr2的区别,虽然它们俩打印出来的地址是一样的* pstr2是指该字符数组的首个元素的地址,即&pstr2[0]的地址,我们通过对其+1可以拿到&pstr2[1]的地址,也可以访问其元素* 而&pstr2的意思则是取整个字符数组的地址,也就是首个元素的地址* 但此时对&pstr2+1的话我们不会拿到第二个元素的地址,反而是会把整个字符数组当作第一个元素,然后去访问下一个字符数组* 的元素,也就是偏移的是整个数组的长度而不是偏移一个元素的长度*/printf("pstr2: %p\n",pstr2);printf("&pstr2: %p\n",&pstr2);//pstr2 = 0x11; 错误 数组名是一个常量,不能修改它的值printf("&gNumber: %p\n",&gNumber);//全局静态区printf("&sNumber: %p\n",&sNumber);//全局静态区printf("&number: %p\n",&number);//栈区printf("&number2: %p\n",&number2);//依然是放在栈上,因为该常量是定义在本函数内的,声明周期在本函数内printf("&kNumber: %p\n",&kNumber); //放在文字常量区,所谓文字常量意思是“字面常量“//包括数值常量、字符常量和符号常量printf("&sLocalNumber: %p\n",&sLocalNumber);//放在全局静态区//查看函数的地址//函数的名称即函数的入口地址存在于全局静态区,即程序存在它就存在//所以查看其地址时会发现函数地址和全局变量的地址相近//但是通过函数名去调用具体函数时就会在栈空间里了//所以函数内部的局部变量都是存放在栈空间上printf("&test: %p\n",&test);
}int main(){test();//查看main函数的地址printf("&main: %p\n",&main);}

编译运行:
在这里插入图片描述

自己可以对照着看看,加深一下理解,然后下面是对上面代码中提到的pstr2和*pstr2的区别图示:在这里插入图片描述

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

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

相关文章

深度学习基础之参数量(3)

一般的CNN网络的参数量估计代码 class ResidualBlock(nn.Module):def __init__(self, in_planes, planes, norm_fngroup, stride1):super(ResidualBlock, self).__init__()print(in_planes, planes, norm_fn, stride)self.conv1 nn.Conv2d(in_planes, planes, kernel_size3, …

后端解决跨域(极速版)

header(Access-Control-Allow-Origin: *); header(Access-Control-Allow-Methods:*); 代表接收全部的请求&#xff0c;"POST,GET"//允许访问的方式 指定域&#xff0c;如http://172.20.0.206//宝塔的域名&#xff0c;注意不是&#xff1a;http://wang.jingyi.icu等…

网络和系统操作命令

目录 ping&#xff1a;用于检测网络是否通畅&#xff0c;以及网络时延情况。ipconfig&#xff1a;查看计算机的IP参数配置信息&#xff0c;如IP地址、默认网关、子网掩码等信息。netstat&#xff1a;显示协议统计信息和当前TCP/IP网络连接。tasklist&#xff1a;显示当前运行的…

正点原子嵌入式linux驱动开发——U-boot图形化配置及其原理

经过之前对uboot的学习可以知道&#xff1a;uboot可以通过stm32mp15_trusted_defconfig来配置&#xff0c;或者通过文件stm32mp1.h来配置uboot。还有另外一种配置uboot的方法&#xff0c;就是图形化配置&#xff0c;以前的uboot是不支持图形化配置&#xff0c;只有Linux内核才支…

JMeter工具的介绍,安装

一、本文学习目标 1、能知道JMeter的优缺点 2、能掌握JMeter的安装流程 3、能掌握JMeter线程组的设置 4、能掌握JMeter参数化的使用 5、能掌握JMeter直连数据库操作 6、能掌握JMeter的断言. 二、JMeter简介 &#xff08;1&#xff09;Jmeter详细介绍 **JMeter&#xff08;A…

C++递归函数

在本文中&#xff0c;您将学习创建递归函数。调用自身的函数。 调用自身的函数称为递归函数。并且&#xff0c;这种技术称为递归。 递归在C 中如何工作&#xff1f; void recurse() {... .. ...recurse();... .. ... }int main() {... .. ...recurse();... .. ... } 下图显…

基于SSM+Vue的物流管理系统的设计与实现

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SSM 前端&#xff1a;VueHTML 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#xff1a;是 …

[每日算法 - 阿里机试] leetcode19. 删除链表的倒数第 N 个结点

入口 力扣&#xff08;LeetCode&#xff09;官网 - 全球极客挚爱的技术成长平台备战技术面试&#xff1f;力扣提供海量技术面试资源&#xff0c;帮助你高效提升编程技能&#xff0c;轻松拿下世界 IT 名企 Dream Offer。https://leetcode.cn/problems/remove-nth-node-from-end…

(面试)谈谈我对C++面向对象特性的理解

&#x1f4af; 博客内容&#xff1a;C读取一行内个数不定的整数的方式 &#x1f600; 作  者&#xff1a;陈大大陈 &#x1f680; 个人简介&#xff1a;一个正在努力学技术的准前端&#xff0c;专注基础和实战分享 &#xff0c;欢迎私信&#xff01; &#x1f496; 欢迎大家&…

yolov5 web端部署进行图片和视频检测

目录 1、思路 2、代码结构 3、代码运行 4、api接口代码 5、web ui界面 6、参考资料 7、代码分享 1、思路 通过搭建flask微型服务器后端&#xff0c;以后通过vue搭建网页前端。flask是第一个第三方库。与其他模块一样&#xff0c;安装时可以直接使用python的pip命令实现…

字符串常量池位于JVM哪里

Java6 和6之前&#xff0c;常量池是存放在方法区&#xff08;永久代&#xff09;中的。Java7&#xff0c;将常量池是存放到了堆中。Java8 之后&#xff0c;取消了整个永久代区域&#xff0c;取而代之的是元空间。运行时常量池和静态常量池存放在元空间中&#xff0c;而字符串常…

c语言:通讯录管理系统(增删查改)

前言&#xff1a;在大多数高校内&#xff0c;都是通过设计一个通讯录管理系统来作为c语言课程设计&#xff0c;通过一个具体的系统设计将我们学习过的结构体和函数等知识糅合起来&#xff0c;可以很好的锻炼学生的编程思维&#xff0c;本文旨在为通讯录管理系统的设计提供思路和…

雷达散射截面(RCS)相关概念

一、雷达散射截面(RCS) RCS被指定为直径为1.128 m的完美导电球体的倍数。该球体的可见表面为1 m,但仅具有较小的反向散射有效面积。因此,更好的反射表面可以具有比其几何尺寸大得多的RCS。 雷达截面积 二、简单目标的RCS 简单目标的RCS如下表所示: 三、瑞利、米氏和光学…

基于SSM的家庭财务管理系统设计与实现

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SSM 前端&#xff1a;采用JSP技术开发 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#x…

SpringBoot banner 样式 自动生成

目录 SpringBoot banner 样式 自动生成 图案网站&#xff1a; 1.第一步创建banner.txt文件 2.访问网站Ascii艺术字实现个性化Spring Boot启动banner图案&#xff0c;轻松修改更换banner.txt文件内容&#xff0c;收集了丰富的banner艺术字和图&#xff0c;并且支持中文banner下…

echarts

1 type值汇总 不同的type的值对应的图表类型如下&#xff1a; type: ‘bar’&#xff1a;柱状/条形图 type: ‘line’&#xff1a;折线/面积图 type: ‘pie’&#xff1a;饼图 type: ‘scatter’&#xff1a;散点&#xff08;气泡&#xff09;图 type: ‘effectScatter’&…

ansible - Role

1、简介&#xff1a; Ansible 中的角色&#xff08;Role&#xff09;是一种组织和封装Playbook的方法&#xff0c;用于管理和组织 Ansible代码。它可以将任务和配置逻辑模块化&#xff0c;以便在不同的Playbook中共享和重用。 2、通过 role 远程部署并配置 nginx (1) 准备目…

数组(数据结构)

优质博文&#xff1a;IT-BLOG-CN 一、简介 数组Array是一种线性表数据结构&#xff0c;它用一组连续的内存空间&#xff0c;存储一组具有相同类型的数据。 数组因具有连续的内存空间的特点&#xff0c;数据拥有非常高效率的“随机访问”&#xff0c;时间复杂度为O(1)。但因要保…

ubuntu使用whisper和funASR-语者分离-二值化

文章目录 一、选择系统1.1 更新环境 二、安装使用whisper2.1 创建环境2.1 安装2.1.1安装基础包2.1.2安装依赖 3测试13测试2 语着分离创建代码报错ModuleNotFoundError: No module named pyannote报错No module named pyannote_whisper 三、安装使用funASR1 安装1.1 安装 Conda&…

黑豹程序员-架构师学习路线图-百科:Database数据库

文章目录 1、什么是Database2、发展历史3、数据库排行网4、总结 1、什么是Database 当今世界是一个充满着数据的互联网世界&#xff0c;各处都充斥着大量的数据。即这个互联网世界就是数据世界。 支撑这个数据世界的基石就是数据库&#xff0c;数据库也可以称为数据的仓库。 …