C++入门精讲——入门看完这一篇就够了

文章目录

  • 前言
  • 1. C++发展历史
  • 2. 关键字
  • 3. 命名空间
    • 3.1 命名空间的概念
    • 3.2 命名空间的定义
    • 3.3 命名空间的使用
  • 4. C++输入、输出
  • 5. 缺省参数
    • 5.1 全缺省
    • 5.2 半缺省
  • 6. 函数重载
    • 6.1 几种不同类型的函数重载
    • 6.2 函数重载的原理——名字修饰(name Mangling)
      • 6.2.1 C程序为什么不支持函数重载?
      • 6.2.2 C++函数名修饰规则
  • 7. 引用
    • 7.1 引用的概念
    • 7.2 引用特性
    • 7.3 常引用
    • 7.4 使用场景
      • 7.4.1 做函数参数
      • 7.4.2 做函数返回值
      • 7.4.3 传值和传引用的效率分析
    • 7.5 引用和指针的联系
  • 8. 内联函数
    • 8.1 概念
    • 8.2 特性
    • 8.3 和宏对比
  • 9. auto的使用细则
    • 9.1 认识auto
    • 9.2 对指针和引用的区别
    • 9.3 一行多次定义的场景
    • 9.4 auto无法使用的场景
  • 10. 基于范围的for循环
    • 10.1 范围for的语法
    • 10.2 范围for的使用条件
  • 11. 指针空值nullptr

前言

什么是C++?

  • C语言是结构化和模块化的语言,适合处理较小规模的程序。对于复杂的问题,规模较大的程序,需要高度的抽象和建模时,C语言则不合适。为了解决软件危机, 20世纪80年代, 计算机界提出了==OOP(object oriented programming:面向对象)==思想,支持面向对象的程序设计语言应运而生。
  • 1982年,Bjarne Stroustrup博士在C语言的基础上引入并扩充了面向对象的概念,发明了一种新的程序语言。为了表达该语言与C语言的渊源关系,命名为C++。因此:C++是基于C语言而产生的,它既可以进行C语言的过程化程序设计,又可以进行以抽象数据类型为特点的基于对象的程序设计,还可以进行面向对象的程序设计

1. C++发展历史

1979年,贝尔实验室的本贾尼等人试图分析 unix 内核的时候,试图将内核模块化,于是在C语言的基础上进行扩展,增加了类的机制,完成了一个可以运行的预处理程序,称之为C with classes。

语言的发展就像是练功打怪升级一样,也是逐步递进,由浅入深的过程。我们先来看下C++的历史版本。

阶段内容
C with classes类及派生类、公有和私有成员、类的构造和析构、友元、内联函数、赋值运算符 重载等
C++1.0添加虚函数概念,函数和运算符重载,引用、常量等
C++2.0更加完善支持面向对象,新增保护成员、多重继承、对象的初始化、抽象类、静 态成员以及const成员函数
C++3.0进一步完善,引入模板,解决多重继承产生的二义性问题和相应构造和析构的处 理
C++98C++标准第一个版本,绝大多数编译器都支持,得到了国际标准化组织(ISO)和美 国标准化协会认可,以模板方式重写C++标准库,引入了STL(标准模板库)
C++03C++标准第二个版本,语言特性无大改变,主要:修订错误、减少多异性
C++05C++标准委员会发布了一份计数报告(Technical Report,TR1),正式更名 C++0x,即:计划在本世纪第一个10年的某个时间发布
C++11增加了许多特性,使得C++更像一种新语言,比如:正则表达式、基于范围for循 环、auto关键字、新容器、列表初始化、标准线程库等
C++14对C++11的扩展,主要是修复C++11中漏洞以及改进,比如:泛型的lambda表 达式,auto的返回值类型推导,二进制字面常量等
C++17在C++11上做了一些小幅改进,增加了19个新特性,比如:static_assert()的文 本信息可选,Fold表达式用于可变的模板,if和switch语句中的初始化器等
C++20自C++11以来最大的发行版,引入了许多新的特性,比如:模块(Modules)、协 程(Coroutines)、范围(Ranges)、概念(Constraints)等重大特性,还有对已有 特性的更新:比如Lambda支持模板、范围for支持初始化等
C++23制定ing

C++还在不断的向后发展。但是:现在公司主流使用还是C++98和C++11。

关于那些更新的版本特性,待我们将C++学习的很深入了,再去慢慢探寻它的真谛。

2. 关键字

在认识完C++后,话不多说直接进入C++的学习。

C++总计63个关键字,C语言32个关键字。这些关键字不需要特别记,在平常使用中,我们都会熟悉到。

在这里插入图片描述

3. 命名空间

3.1 命名空间的概念

先来看一个问题,我们在写C语言时,我们取的变量名,有时会与库中的变量名发生命名冲突

例如:

#include<stdio.h>
#include<stdlib.h>

int rand = 10;
int main()
{
	printf("%d", rand);
	return 0;
}

运行,程序报错

在这里插入图片描述

这是因为在库中已经有了 rand 这个函数,再次使用 rand 这个名字定义变量或者函数时,编译器会分不清你到底想使用哪个 rand

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

3.2 命名空间的定义

定义命名空间,需要使用到namespace关键字,后面跟命名空间的名字,然后接一对{}即可,{}中即为命名空间的成员。

  1. 正常的命名空间的定义
namespace czh
{
	// 命名空间中可以定义变量/函数/类型
	int rand = 10;
	int Add(int left, int right)
	{
		return left + right;
	}

	struct Node
	{
		struct Node* next;
		int val;
	};
}
  1. 命名空间可以嵌套
namespace N1
{
	int a;
	int b;
	int Add(int left, int right)
	{
		return left + right;
	}
	namespace N2
	{
		int c;
		int d;
		int Sub(int left, int right)
		{
			return left - right;
		}
	}
}
  1. 同一个工程中允许存在多个相同名称的命名空间,编译器最后会合成同一个命名空间
// test.cpp
namespace N1
{
	int a;
	int b;
	int Add(int left, int right)
	{
		return left + right;
	}
	namespace N2
	{
		int c;
		int d;
		int Sub(int left, int right)
		{
			return left - right;
		}
	}
}
// test.h
namespace N1
{
	int Mul(int left, int right)
	{
		return left * right;
	}
}
// 最终上面test.h和test.cpp中的两个N1会被合成一个。

注:一个命名空间就定义了一个新的作用域,命名空间中的所有内容都局限于该命名空间中

3.3 命名空间的使用

命名空间中成员该如何使用呢?

namespace N
{
	int a = 0;
	int b = 1;

	int Add(int left, int right)
	{
		return left + right;
	}

	struct Node
	{
		struct Node* next;
		int val;
	};
}

方法一:加命名空间名称及作用域限定符

这里的::就是作用域限定符,可以指定要访问命名空间中的成员。

int main()
{
	printf("%d\n", N::a);
	return 0;
}

方法二:使用using 将命名空间中某个成员引入

using N::b;
int main()
{
	printf("%d\n", N::a);
	printf("%d\n", b);
	return 0;
}

方法三:使用using namespace 命名空间名称引入

using namespace N;
int main()
{
	printf("%d\n", a);
	printf("%d\n", b);
	Add(10, 20);
	return 0;
}

4. C++输入、输出

#include<iostream>

using namespace std;

int main()
{
	// 输入
	int a = 0;
	cin >> a;

	// 输出
	cout << "Hello world!!!" << endl;
	cout << "a = " << a << endl;
	return 0;
}

说明:

  1. std是C++标准库的命名空间名,C++将标准库的定义实现都放到这个命名空间中。
  2. 使用cout标准输出对象(控制台)和cin标准输入对象(键盘)时,必须包含<iostream>头文件以及按命名空间使用方法使用std
  3. coutcin是全局的流对象,endl是特殊的C++符号,表示换行输出,他们都包含在包含<iostream>头文件中。
  4. <<是流插入运算符,>>是流提取运算符。
  5. 使用C++输入输出更方便,不需要像printf/scanf输入输出时那样,需要手动控制格式。C++的输入输出可以自动识别变量类型。
  6. 实际上coutcin分别是ostreamistream类型的对象,>><<也涉及运算符重载等知识,这些知识我们我们后续才会学习。

注意:

早期标准库将所有功能在全局域中实现,声明在.h后缀的头文件中,使用时只需包含对应头文件即可,后来将其实现在std命名空间下,为了和C头文件区分,也为了正确使用命名空间,规定C++头文件不带 .h;旧编译器(VC 6.0)中还支持<iostream.h>格式,后续编译器已不支持,因此推荐使用<iostream>+std的方式。

5. 缺省参数

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

缺省参数说的简单点,就是给函数参数写默认值

void Func(int a = 0)
{
	cout << a << endl;
}
int main()
{
	Func(); // 没有传参时,使用参数的默认值0
	Func(10); // 传参时,使用指定的实参10
	return 0;
}

缺省参数分类:

  1. 全缺省
  2. 半缺省

5.1 全缺省

全缺省指的是函数的所有参数都给缺省值

像这样:

void Func(int a = 10, int b = 20, int c = 30)
{
	cout << "a = " << a << endl;
	cout << "b = " << b << endl;
	cout << "c = " << c << endl;
}

有四种调用此函数的方式

  1. 什么都不传
Func();

此时a,b,c的值为函数的默认参数值10,20,30。

  1. 传一个参数
Func(0);

此时a的值为0,b和c的值为默认值20和30。

  1. 传两个参数
Func(0, 1);

此时a和b的值为0和1,c的值为默认值30。

  1. 传三个参数
Func(0, 1, 2);

此时a,b,c的值为0,1,2;

注意

传参不能写成这种形式

Func(, 1, );
Func(, , 2);
Func(, 1, 2);
Func(0, , 2);

总结

实参是从左向右依次填充的。

5.2 半缺省

只要有参数没有给缺省值,那么它就是半缺省的

像这样:

void Func(int a, int b = 10, int c = 20)
{
	cout << "a = " << a << endl;
	cout << "b = " << b << endl;
	cout << "c = " << c << endl;
}

注意:

  1. 半缺省参数必须从右往左依次来给出,不能间隔着给
  2. 缺省参数不能在函数声明和定义中同时出现(一般都是声明给,定义不给
  3. 缺省值必须是常量或者全局变量

理解:

  1. 不能写成这种形式
void Func1(int a = 10, int b, int c);
void Func2(int a = 10, int b = 20, int c);
  1. 如果声明与定义同时出现缺省值,恰巧两个位置提供的值不同,那编译器就无法确定到底该用那个缺省值。

调用方法同全缺省。

6. 函数重载

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

6.1 几种不同类型的函数重载

  1. 参数类型不同
int Add(int left, int right)
{
	return left + right;
}

double Add(double left, double right)
{
	return left + right;
}
  1. 参数个数不同
void f()
{
	cout << "f()" << endl;
}

void f(int a)
{
	cout << "f(int a)" << endl;
}
  1. 参数类型顺序不同
void f(int a, char b)
{
	cout << "f(int a, char b)" << endl;
}

void f(char b, int a)
{
	cout << "f(char b, int a)" << endl;
}

注意:这样不属于函数重载,会报错

long long Add(int x, int y)
{
	return x+y;
}

int Add(int x, int y)
{
	return x+y;
}

因为它们参数类型,数量,顺序都一样,只是返回值不同,不构成函数重载;调用函数时,传两个int类型的值,但是编译器不知道你要调用哪个函数,编译报错。

总结:函数重载只与函数名和参数有关,和返回类型无关。

6.2 函数重载的原理——名字修饰(name Mangling)

6.2.1 C程序为什么不支持函数重载?

在C/C++中,一个程序要运行起来,需要经历以下几个阶段:预处理、编译、汇编、链接。

我们定义两个源文件和一个头文件来解释:

  • func.h
  • func.c
  • main.c

在 func.c 文件中定义并在 func.h 文件中声明这两个函数:

int func(int x,int y);
int func(int x,double y);

func.c 和 main.c 函数会经过预处理、编译、汇编形成对应的 func.o 文件和 main.o 文件。

链接时会将两个.o文件合并到一起,包括:

  • 合并段表
  • 符号表的合并与重定位

每一个.o文件都有一个符号表,符号表中存放着函数的地址。main函数调用函数时会去符号表中找相应函数的地址,然而我们定义的两个func函数形成了两个地址,导致编译器不知道调用哪个,所以报错。所以C程序不支持函数重载。

6.2.2 C++函数名修饰规则

相比起C程序而言,C++新增了一个函数名修饰规则来支持函数重载

这个规则就是将函数的参数带入符号表,所以函数参数的类型,数量,顺序不同代表的是不同的函数,找地址时就不会出错

  • 采用C语言编译器编译的结果:

在这里插入图片描述

结论:在 Linux 下,采用 gcc 编译完成后,函数名字的修饰没有发生改变。

  • 采用C++编译器编译的结果:

在这里插入图片描述

结论:在 Linux 下,采用 g++ 编译完成后,函数名字的修饰发生改变,编译器将函数参数类型信息添加到修改后的名字中。格式为:_Z+函数长度+函数名+类型首字母

7. 引用

7.1 引用的概念

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

类型& 引用变量名(对象名) = 引用实体;

引用符号:&

举例说明:

int main()
{
	int a = 10;
	int& ra = a;//<====定义引用类型
	printf("%p\n", &a);
	printf("%p\n", &ra);
    return 0;
}

运行结果:(打印出来地址相同

在这里插入图片描述

并且改变引用对象的值,原先的值也会改变!

注意:引用类型必须和引用实体是同种类型的

7.2 引用特性

  1. 引用在定义时必须初始化
  2. 一个变量可以有多个引用
  3. 引用一旦引用一个实体,再不能引用其他实体

代码举例加深理解:

int main()
{
	int a = 10;
	// int& ra; // 该条语句编译时会出错,违背特性1
	
	int& ra = a;
	int& rra = a;
	printf("&a:%p &ra:%p &rra:%p\n", &a, &ra, &rra); // 一个变量可以有多个引用

	int b = 20;
	ra = b; // 这条语句的意思是将b的值赋值给a,而不是ra从对a的引用变成对b的引用
	printf("&b:%p &ra:%p ra:%d\n", &b, &ra, ra);
	
	return 0;
}

运行结果:

在这里插入图片描述

7.3 常引用

顾名思义就是对常量的引用,但是常量具有不可修改性,所以引用一个常量时要加上const

代码举例加深理解:

int main()
{
	const int a = 10;
	//int& ra = a; // 该语句编译时会出错,a为常量
	const int& ra = a;
	//int& b = 10; // 该语句编译时会出错,b为常量
	const int& b = 10;
	double d = 12.34;
	//int& rd = d; // 该语句编译时会出错,类型不同
	const double& rd = d;
	return 0;
}

7.4 使用场景

7.4.1 做函数参数

之前在写C程序的交换函数时,因为形参是实参的一份拷贝,想要改变实参就要传地址,而现在有了引用就不用传地址了。

引用作为形参,在函数体内我们对引用的修改,就是对实参的修改。

例如:

void Swap(int& left, int& right)
{
	int temp = left;
	left = right;
	right = temp;
}

7.4.2 做函数返回值

引用做返回值时,可在函数外面修改函数里面的内容,前提是引用的变量出了函数不会销毁

int& Add(int a, int b)
{
    int c = a + b;
    return c;
}

这段代码传引用返回是错误的,因为函数调用完后c变量就被销毁了,而将c的引用返回后,会有类似于使用野指针的风险。

结论: 在函数返回时,出了函数作用域,如果返回对象还在(还没还给系统),则可以使用引用返回如果已经还给系统了,则必须使用传值返回

7.4.3 传值和传引用的效率分析

以值作为参数或者返回值类型,在传参和返回期间,函数不会直接传递实参或者将变量本身直接返回,而是传递实参或者返回变量的一份临时的拷贝,因此用值作为参数或者返回值类型,效率是非常低下的,尤其是当参数或者返回值类型非常大时,效率就更低。

7.5 引用和指针的联系

  • 在语法概念上引用就是一个别名,没有独立空间,和其引用实体共用同一块空间。
  • 引用在底层实现上实际是有空间的,因为引用是按照指针方式来实现的。

引用和指针的的汇编代码对比

在这里插入图片描述

可以看到引用和指针在底层是一模一样的

引用和指针的不同点:

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

8. 内联函数

8.1 概念

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

在这里插入图片描述

如果在上述函数前增加inline关键字将其改成内联函数,在编译期间编译器会用函数体替换函数的调用。

查看方式:

  1. 在release模式下,查看编译器生成的汇编代码中是否存在call Add

  2. 在debug模式下,需要对编译器进行设置,否则不会展开(因为debug模式下,编译器默认不会对代码进行优化,以下给出vs2022的设置方式)

在这里插入图片描述

内联函数 Add 的汇编

在这里插入图片描述

8.2 特性

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

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

在这里插入图片描述

  1. inline不建议声明和定义分离,分离会导致链接错误。因为inline在编译阶段被展开,就没有函数地址了,链接时就会找不到

8.3 和宏对比

宏的优缺点?

优点:

  1. 增强代码的复用性。

  2. 提高性能。

缺点:

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

C++有哪些技术替代宏?

  1. 常量定义,换用const enum

  2. 短小函数定义,换用内联函数

9. auto的使用细则

9.1 认识auto

  • 在早期C/C++中auto的含义是:使用auto修饰的变量,是具有自动存储器的局部变量,但遗憾的是一直没有人去使用它。
  • C++11中,标准委员会赋予了auto全新的含义即:auto不再是一个存储类型指示符,而是作为一个新的类型指示符来指示编译器,auto声明的变量必须由编译器在编译时期推导而得。

注:

使用auto定义变量时必须对其进行初始化,在编译阶段编译器需要根据初始化表达式来推导auto的实际类型。因此auto并非是一种“类型”的声明,而是一个类型声明时的“占位符”,编译器在编译期会将auto替换为变量实际的类型。

9.2 对指针和引用的区别

  1. 对指针来说*可有可无
int x = 0;
auto a = &x;
auto* b = &x;
  • a的类型是int*,此时autoint*
  • b的类型是int*,此时autoint

结论:使用auto加不加*都行,效果一样

  1. 对引用来说必须加上&
int x = 0;
auto& a = x;
auto b = x;
  • a的类型是int&autoint
  • b的类型是intautoint

结论:如果想用auto定义一个引用变量则必须加上引用符&

9.3 一行多次定义的场景

当在同一行声明多个变量时这些变量,必须是相同的类型,否则编译器将会报错。因为编译器实际只对第一个类型进行推导,然后用推导出来的类型定义其他变量。

例如:

auto a = 1, b = 2; // 没问题
auto c = 3, d = 4.0;  // 该行代码会编译失败,因为c和d的初始化表达式类型不同

9.4 auto无法使用的场景

  1. auto不能作为函数的参数
// 此处代码编译失败,auto不能作为形参类型,因为编译器无法对a的实际类型进行推导
void TestAuto(auto a)
{}
  1. auto不能用于声明数组
void TestAuto()
{
    int a[] = {1,2,3};
    auto b[] = {4,5,6};
}

10. 基于范围的for循环

10.1 范围for的语法

在C++98中如果要遍历一个数组,可以按照以下方式进行:

  • void TestFor()
    {
        int array[] = { 1, 2, 3, 4, 5 };
        for (int i = 0; i < sizeof(array) / sizeof(array[0]); ++i)
            array[i] *= 2;
        for (int* p = array; p < array + sizeof(array)/ sizeof(array[0]); ++p)
            cout << *p << endl;
    }
    

对于一个有范围的集合而言,由程序员来说明循环的范围是多余的,有时候还会容易犯错误。因此C++11中引入了基于范围的for循环。for循环后的括号由冒号:分为两部分:第一部分是范围内用于迭代的变量,第二部分则表示被迭代的范围

上述代码可以改成这样:

void TestFor()
{
    int array[] = { 1, 2, 3, 4, 5 };
    for(auto& e : array)
        e *= 2;
    for(auto e : array)
        cout << e << " ";
    return 0;
}

注意:

  • 与普通循环类似,可以用continue来结束本次循环,也可以用break来跳出整个循环。
  • 中间的:是范围for的规定写法,没有特殊意义

10.2 范围for的使用条件

  1. for循环迭代的范围必须是确定的
    对于数组而言,就是数组中第一个元素和最后一个元素的范围;对于类而言,应该提供begin和end的方法,begin和end就是for循环迭代的范围。注意:以下代码就有问题,因为for的范围不确定。
void TestFor(int array[])
{
    for(auto& e : array)
        cout<< e <<endl;
}
  1. 迭代的对象要实现++和==的操作(这里入门仅作了解,后续深入学习)

11. 指针空值nullptr

在良好的C/C++编程习惯中,声明一个变量时最好给该变量一个合适的初始值,否则可能会出现不可预料的错误,比如未初始化的指针。如果一个指针没有合法的指向,我们基本都是按照如下方式对其进行初始化:

void TestPtr()
{
    int* p1 = NULL;
    int* p2 = 0;
    // ……
}

NULL实际是一个宏,在传统的C头文件(stddef.h)中,可以看到如下代码:

#ifndef NULL
#ifdef __cplusplus
#define NULL 0
#else
#define NULL ((void *)0)
#endif
#endif

可以看到,NULL可能被定义为字面常量0,或者被定义为无类型指针(void*)的常量。不论采取何种定义,在使用空值的指针时,都不可避免的会遇到一些麻烦,比如:

void f(int)
{
    cout<<"f(int)"<<endl;
}
void f(int*)
{
    cout<<"f(int*)"<<endl;
}
int main()
{
    f(0);
    f(NULL);
    f((int*)NULL);
    return 0;
}

程序本意是想通过f(NULL)调用指针版本的f(int*)函数,但是由于NULL被定义成0,因此与程序的初衷相悖。
在C++98中,字面常量0既可以是一个整型数字,也可以是无类型的指针(void*)常量,但是编译器默认情况下将其看成是一个整型常量,如果要将其按照指针方式来使用,必须对其进行强转(void*)0

注意:

  1. 在使用nullptr表示指针空值时,不需要包含头文件,因为nullptr是C++11作为新关键字引入
    的。

  2. 在C++11中,sizeof(nullptr)sizeof((void*)0)所占的字节数相同。

  3. 为了提高代码的健壮性,在后续表示指针空值时建议最好使用nullptr

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Nokogiri库和OpenURI库使用HTTP做一个爬虫

Nokogiri和OpenURI是两个常用的Ruby库&#xff0c;用于编写爬虫程序。它们的主要功能如下&#xff1a; 1、Nokogiri&#xff1a;Nokogiri是一个强大的HTML和XML解析库&#xff0c;可以用于解析网页内容。它提供了一组简单易用的API&#xff0c;可以方便地遍历和操作HTML或XML文…

模型对象CSS2DObject始终在画布的左上角(问题解决)

写了个简单案例模拟一下这个问题&#xff0c;看下图片 下面看下c2渲染器相关代码部分 this.css2DRenderer new CSS2DRenderer(); this.css2DRenderer.render(this.scene, this.camera); this.css2DRenderer.setSize(width, height); this.css2DRenderer.domElement.style.pos…

【每日一题】合并两个有序数组

链接奉上&#xff1a;合并两个有序数组 目录 直接合并后排序&#xff1a;思路&#xff1a;代码实现&#xff1a; 双指针思路&#xff1a;代码实现&#xff1a; 直接合并后排序&#xff1a; 思路&#xff1a; 将nums2直接合并到nums1后边&#xff0c;并进行排序 代码实现&…

Proteus仿真--基于51单片机的LED模拟交通灯仿真(仿真文件+程序)

本文主要介绍基于51单片机的LED模拟交通灯仿真&#xff08;完整仿真源文件及代码见文末链接&#xff09; 仿真运行视频 Proteus仿真--基于51单片机的LED模拟交通灯仿真&#xff08;仿真文件程序&#xff09; 附完整Proteus仿真资料代码资料 百度网盘链接: https://pan.baidu.c…

STM32F4X SDIO(二) SDIO协议

上一节简单介绍了SD卡的分类&#xff0c;本节将会介绍SD卡的通信协议&#xff0c;也就是SDIO协议。 STM32F4X SDIO&#xff08;二&#xff09;SDIO协议 SD 卡管脚和寄存器SD卡管脚分布SD卡通信协议SD卡寄存器SD卡内部结构 SDIO总线SDIO总线拓扑SDIO总线协议SDIO协议的基本结构…

Oracle (7)Online Redo Log Files

目录 一、Oracle Online Redo Log Files及其相关内容介绍 1、Online Redo Log Files简介 2、Online Redo Log Files特点 3、Online Redo Log Files文件组 4、多路复用文件 5、联机重做日志文件工作方式 6、LGWR什么时候写重做 7、LS和LSN 8、删除Redo文件成员 9、删除…

CTF-php特性绕过

注意&#xff1a;null0 正确 nullflase 错误 Extract变量覆盖 <?php$flagxxx; extract($_GET);if(isset($shiyan)){ $contenttrim(file_get_contents($flag));//trim移除引号if($shiyan$content){ echoctf{xxx}; }else{ echoOh.no;} }?> extract() 函数从数组中将…

C语言每日一题(21)删除排序数组中的重复项

力扣 26.删除排序数组中的重复项 题目描述 给你一个 非严格递增排列 的数组 nums &#xff0c;请你 原地 删除重复出现的元素&#xff0c;使每个元素 只出现一次 &#xff0c;返回删除后数组的新长度。元素的 相对顺序 应该保持 一致 。然后返回 nums 中唯一元素的个数。 考…

Redis(02)| 数据结构-SDS

一、键值对数据库是怎么实现的&#xff1f; 在开始讲数据结构之前&#xff0c;先给介绍下 Redis 是怎样实现键值对&#xff08;key-value&#xff09;数据库的。 Redis 的键值对中的 key 就是字符串对象&#xff0c;而 value 可以是字符串对象&#xff0c;也可以是集合数据类型…

哪一个更好?Spring boot还是Node.js

前言 本篇文章有些与众不同&#xff0c;由于我自己手头有些关于这个主题的个人经验&#xff0c;受其启发写出此文。虽然SpringBoot和Node.js服务于很不一样的场景&#xff0c;但是这两个框架共性惊人。其实每种语言都有不计其数的框架&#xff0c;但仅仅一部分是真正卓越的。如…

【优选算法系列】第二节.双指针(202. 快乐数和11. 盛最多水的容器)

作者简介&#xff1a;大家好&#xff0c;我是未央&#xff1b; 博客首页&#xff1a;未央.303 系列专栏&#xff1a;优选算法系列 每日一句&#xff1a;人的一生&#xff0c;可以有所作为的时机只有一次&#xff0c;那就是现在&#xff01;&#xff01;&#xff01;&#xff01…

【数据结构】搜索树 与 Java集合框架中的Set,Map

作者主页&#xff1a;paper jie_博客 本文作者&#xff1a;大家好&#xff0c;我是paper jie&#xff0c;感谢你阅读本文&#xff0c;欢迎一建三连哦。 本文录入于《JAVA数据结构》专栏&#xff0c;本专栏是针对于大学生&#xff0c;编程小白精心打造的。笔者用重金(时间和精力…

TSINGSEE青犀睡岗离岗检测算法——确保加油站安全运营

众所周知&#xff0c;加油站是一个需要24小时营业的场所&#xff0c;由于夜间加油人员较少&#xff0c;员工极易处于疲劳或者睡眠状态&#xff0c;为保障安全和效率&#xff0c;通过TSINGSEE青犀睡岗离岗检测算法在加油站场景中&#xff0c;可以及时发现工作人员的疲劳状况&…

JAVA面试题简单整理

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、重载和重写的区别一、&和&&的区别一、get和post请求的区别 delete、put一、cookie和session的区别一、Autowired和Resource区别一、”和equals…

笔记:电子设备接地,接的到底是什么地?

电路中有“地”&#xff0c;设备中有“地”&#xff1b;都是“地”&#xff0c;此地非彼地。 混淆的原因 有些混淆&#xff0c;是以为中文翻译造成的&#xff0c;英文所有Ground都统一翻译为“地”&#xff1b; 例1&#xff1a;英文Circuit Ground&#xff0c;应该翻译为电路…

mac安装并使用wireshark

mac安装并使用wireshark 1 介绍 我们在日常开发过程中&#xff0c;遇到了棘手的问题时&#xff0c;免不了查看具体网络请求情况&#xff0c;这个时候就需要用到抓包工具。比较著名的抓包工具就属&#xff1a;wireshark、fildder。我这里主要介绍wireshark。 2 安装 以mac安装为…

Android-宝宝相册(第四次作业)

第四次作业-宝宝相册 题目 用Listview建立宝宝相册&#xff0c;相册内容及图片可自行设定&#xff0c;也可在资料文件中获取。给出模拟器仿真界面及代码截图。 &#xff08;参考例4-8&#xff09; 创建工程项目 创建名为baby的项目工程&#xff0c;最后的工程目录结构如下图所…

自学爬虫—作业1—requests模块

视频&#xff1a; 要求&#xff1a; 肯德基地址查询&#xff0c;爬某个关键字&#xff0c;获取下面的所有page的信息&#xff0c;存到一个json或者txt。 代码&#xff1a; 关键点&#xff0c;&#xff08;1&#xff09;每一个ajax的请求第一个键值对就是所有获得的地址的总数…

【算法|动态规划No.31 | 01背包问题】01背包模板题

个人主页&#xff1a;兜里有颗棉花糖 欢迎 点赞&#x1f44d; 收藏✨ 留言✉ 加关注&#x1f493;本文由 兜里有颗棉花糖 原创 收录于专栏【手撕算法系列专栏】【LeetCode】 &#x1f354;本专栏旨在提高自己算法能力的同时&#xff0c;记录一下自己的学习过程&#xff0c;希望…

【Linux】安装与配置虚拟机及虚拟机服务器坏境配置与连接---超详细教学

一&#xff0c;操作系统介绍 1.1.什么是操作系统 操作系统&#xff08;Operating System&#xff0c;简称OS&#xff09;是一种系统软件&#xff0c;它是计算机硬件和应用软件之间的桥梁。它管理计算机的硬件和软件资源&#xff0c;为应用程序提供接口和服务&#xff0c;并协…