C++深入学习part_1

news2024/11/20 14:20:03

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_number
        cout << 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{

        //第二次定义实体show
        void show();
}

//这里我们第三次定义命名空间wd
namespace wd{

        //第三次声明并且定义实体show
        void 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中的wd
        wd::print();
        return 0;
}


//这里我们第二次定义命名空间wd
namespace wd{

        //第二次定义实体show
        void show();
}

//这里我们第三次定义命名空间wd
namespace wd{

        //第三次声明并且定义实体show
        void show(){
                cout << "我是第三次被声明啦" << endl;
        }
}

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

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

const修饰类型和对象

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

#include <iostream>

using namespace std;

#define MAX 1000

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

int main(){
        
        test();

        return 0;
        
}

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

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

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

#include <iostream>

using namespace std;

//举例说明为什么宏定义只是进行了简单的字符串替换
#define kBase 3+4

void 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"
{
#endif
        
        int 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 * y


int main(){
        int a = 3, b = 4;
        int c = 5, d = 6;
        cout << multiply(a,b) << endl;//输出为12
        //但还是之前的问题,宏定义只是简单替换成了字符串
        //所以下面的语句其实是:a+b*c+d = 29
        cout << 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 = 29
        cout << 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.coloradmin.cn/o/1064389.html

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

相关文章

2023年【高处安装、维护、拆除】考试题及高处安装、维护、拆除最新解析

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 高处安装、维护、拆除考试题考前必练&#xff01;安全生产模拟考试一点通每个月更新高处安装、维护、拆除最新解析题目及答案&#xff01;多做几遍&#xff0c;其实通过高处安装、维护、拆除考试资料很简单。 1、【单…

OpenCV C++ Look Up Table(查找表)

OpenCV C Look Up Table&#xff08;查找表&#xff09; 引言 在图像处理和计算机视觉中&#xff0c;查找表&#xff08;Look Up Table, LUT&#xff09;是一种非常高效和实用的方法&#xff0c;用于快速地映射或更改图像的颜色和像素值。LUT 能够极大地提高图像处理算法的执…

XSS CSRF

XSS & CSRF xss&#xff1a;跨站脚本攻击&#xff1a;注入一些非法的脚本 csrf&#xff1a;冒充身份 XSS 反射型 /welcome&#xff1a;res.send(req.query.type) 输入什么就输出什么&#xff08;httpOnly:false&#xff0c;但不是解决方案&#xff09; 比如&#xff1a;?&…

【C语言|关键字】C语言32个关键字详解

&#x1f601;博客主页&#x1f601;&#xff1a;&#x1f680;https://blog.csdn.net/wkd_007&#x1f680; &#x1f911;博客内容&#x1f911;&#xff1a;&#x1f36d;嵌入式开发、Linux、C语言、C、数据结构、音视频&#x1f36d; &#x1f923;本文内容&#x1f923;&a…

不断优化的素数算法

前言&#xff1a;素数判断是算法中重要的一环&#xff0c;掌握优秀的素数判断方法是算法player的必修课。本文介绍的是由简到繁的素数算法&#xff0c;便于初学者从入门到精通。 素数&#xff08;质数&#xff09;&#xff1a;只能被 1 和它本身整除的数称作素数&#xff0c;如…

【Linux学习】05-2Linux上部署项目

Linux&#xff08;B站黑马&#xff09;学习笔记 01Linux初识与安装 02Linux基础命令 03Linux用户和权限 04Linux实用操作 05-1Linux上安装部署各类软件 05-2Linux上部署项目 文章目录 Linux&#xff08;B站黑马&#xff09;学习笔记前言05-2Linux上部署项目部署Springboot项目…

【AI视野·今日Sound 声学论文速览 第二十期】Fri, 6 Oct 2023

AI视野今日CS.Sound 声学论文速览 Fri, 6 Oct 2023 Totally 6 papers &#x1f449;上期速览✈更多精彩请移步主页 Daily Sound Papers Deep Generative Models of Music Expectation Authors Ninon Liz Masclef, T. Anderson Keller对音乐的情感反应的一个重要理论围绕着惊喜…

时间序列常用数据处理

1.组合技巧Compose 1.2 实例应用及其解释 # 用于组合多个数据处理方法 class Compose(object):def __init__(self, transforms):self.transforms transformsdef __call__(self, seq):for t in self.transforms:seq t(seq)return seq 这段Python代码定义了一个名为Compose的…

星火大模型AI接口Spring中项目中使用【星火、AIGC】

星火大模型AI接口使用 讯飞的星火大模型是有免费版本赠送200万个Token&#xff0c;就个人学习来说完全够用了。 免费申请过后&#xff0c;到控制台&#xff0c;两个红色方框是最要紧的。 星火认知大模型Web文档 | 讯飞开放平台文档中心 (xfyun.cn)这是官方文档对于接口的详细使…

【AI视野·今日NLP 自然语言处理论文速览 四十九期】Fri, 6 Oct 2023

AI视野今日CS.NLP 自然语言处理论文速览 Fri, 6 Oct 2023 Totally 44 papers &#x1f449;上期速览✈更多精彩请移步主页 Daily Computation and Language Papers MathCoder: Seamless Code Integration in LLMs for Enhanced Mathematical Reasoning Authors Ke Wang, Houxi…

如何一键转发朋友圈,快速跟圈?

你是否曾为在微信上快速转发别人的朋友圈而烦恼&#xff1f;每天都要花费大量时间下载商品图片和复制粘贴商家的文案&#xff0c;让人疲惫不堪。我觉得这样太繁琐太麻烦了&#xff0c;每天都会上新货&#xff0c;上传朋友圈都要花将近一个小时的时间&#xff0c;花了大量时间在…

【C++设计模式之状态模式:行为型】分析及示例

简介 状态模式&#xff08;State Pattern&#xff09;是一种行为型设计模式&#xff0c;它允许对象在内部状态改变时改变其行为&#xff0c;看起来就像是改变了其类。状态模式将对象的状态封装成不同的类&#xff0c;并使得对象在不同状态下有不同的行为。 描述 状态模式通过…

C++ 类和对象篇(五) 析构函数

目录 一、概念 1. 析构函数是什么&#xff1f; 2. 为什么要有析构函数&#xff1f; 3. 怎么用析构函数&#xff1f; 3.1 创建析构函数 3.2 调用析构函数 二、特性 三、由编译器生成的默认析构函数 四、对象的析构顺序 1. 局部对象 2. new出来的堆对象 3. 全局对象 一、概念 1…

Linux指令示范(1)

个人主页&#xff1a;Lei宝啊 愿所有美好如期而遇

小程序支付升级:实现微信支付V3接口接入

文章目录 用户付款流程业务流程讲解接入前准备快速接入1、引入开发库2、配置参数3、初始化商户配置4、微信支付对接5、支付回调-支付通知API 相较于 v2 版本&#xff0c;v3 版本的接口文档在阅读上可能显得相对凌乱。它的组织结构可能不太清晰&#xff0c;难以快速理解整个流程…

【Go语言实战】(25) 分布式算法 MapReduce

MapReduce 写在前面 身为大数据专业的学生&#xff0c;其实大学我也多多少少接触过mapreduce&#xff0c;但是当时觉得这玩意太老了&#xff0c;觉得这和php一样会被时代淘汰。只能说当时确实太年轻了&#xff0c;没有好好珍惜那时候的学习资源… 现在回过头来看mapreduce&a…

聊聊分布式架构——RPC通信原理

目录 RPC通信的基本原理 RPC结构 手撸简陋版RPC 知识点梳理 1.Socket套接字通信机制 2.通信过程的序列化与反序列化 3.动态代理 4.反射 思维流程梳理 码起来 服务端时序图 服务端—Api与Provider模块 客户端时序图 RPC通信的基本原理 RPC&#xff08;Remote Proc…

【算法练习Day13】二叉树的层序遍历翻转二叉树对称二叉树

​&#x1f4dd;个人主页&#xff1a;Sherry的成长之路 &#x1f3e0;学习社区&#xff1a;Sherry的成长之路&#xff08;个人社区&#xff09; &#x1f4d6;专栏链接&#xff1a;练题 &#x1f3af;长路漫漫浩浩&#xff0c;万事皆有期待 文章目录 二叉树的层序遍历翻转二叉树…

安装Ubuntu提示:系统找不到指定的文件。

今天我删除Ubuntu后重新下载&#xff0c;发现报错&#xff0c;错误信息如下&#xff1a; 这是因为系统没有卸载干净而导致的。 解决办法&#xff1a; 第一步&#xff1a; ##查询当前已安装的系统 wsl.exe --list --all 执行结果&#xff1a; 第二步&#xff1a; ##注销当前…

【GSEP202303 C++】1级 长方形面积

[GSEP202303 一级] 长方形面积 题目描述 小明刚刚学习了如何计算长方形面积。他发现&#xff0c;如果一个长方形的长和宽都是整数&#xff0c;它的面积一定也是整数。现在&#xff0c;小明想知道如果给定长方形的面积&#xff0c;有多少种可能的长方形&#xff0c;满足长和宽…