C++基础闯关100题,你能闯多少?【2024超硬核大厂高频面试题】_int p = (int )malloc(2 sizeof(double));(1)

总目录展示

该笔记共八个节点(由浅入深),分为三大模块。

高性能。 秒杀涉及大量的并发读和并发写,因此支持高并发访问这点非常关键。该笔记将从设计数据的动静分离方案、热点的发现与隔离、请求的削峰与分层过滤、服务端的极致优化这4个方面重点介绍。

一致性。 秒杀中商品减库存的实现方式同样关键。可想而知,有限数量的商品在同一时刻被很多倍的请求同时来减库存,减库存又分为“拍下减库存”“付款减库存”以及预扣等几种,在大并发更新的过程中都要保证数据的准确性,其难度可想而知。因此,将用一个节点来专门讲解如何设计秒杀减库存方案。

高可用。 虽然介绍了很多极致的优化思路,但现实中总难免出现一些我们考虑不到的情况,所以要保证系统的高可用和正确性,还要设计一个PlanB来兜底,以便在最坏情况发生时仍然能够从容应对。笔记的最后,将带你思考可以从哪些环节来设计兜底方案。


篇幅有限,无法一个模块一个模块详细的展示(这些要点都收集在了这份《高并发秒杀顶级教程》里),麻烦各位转发一下(可以帮助更多的人看到哟!)

由于内容太多,这里只截取部分的内容。

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

需要这份系统化的资料的朋友,可以点击这里获取


* 考虑类的情况


	+ const成员变量:不能在类定义外部初始化,只能通过构造函数初始化列表进行初始化,并且必须有构造函数;不同类对其const数据成员的值可以不同,所以不能在类中声明时初始化。
	+ const成员函数:const对象不可以调用非const成员函数;非const对象都可以调用;不可以改变非mutable(用该关键字声明的变量可以在const成员函数中被修改)数据的值。


**static**:


* 不考虑类的情况


	+ 隐藏。所有不加static的全局变量和函数具有全局可见性,可以在其他文件中使用,加了之后只能在该文件所在的编译模块中使用。
	+ 默认初始化为0,包括未初始化的全局静态变量与局部静态变量,都存在全局未初始化区。
	+ 静态变量在函数内定义,始终存在,且只进行一次初始化,具有记忆性,其作用范围与局部变量相同,函数退出后仍然存在,但不能使用。
* 考虑类的情况


	+ static成员变量:只与类关联,不与类的对象关联。定义时要分配空间,不能在类声明中初始化,必须在类定义体外部初始化,初始化时不需要标示为static;可以被非static成员函数任意访问。
	+ static成员函数:不具有this指针,无法访问类对象的非static成员变量和非static成员函数;不能被声明为const、虚函数和volatile;可以被非static成员函数任意访问。


##### 14、C++的顶层const和底层const?


**顶层const**:指的是const修饰的变量本身是一个常量,无法修改,指的是指针,就是 \* 号的右边。


**底层const**:指的是const修饰的变量所指向的对象是一个常量,指的是所指变量,就是 \* 号的左边。



int a = 10;
int* const b1 = &a; //顶层const,b1本身是一个常量
const int* b2 = &a; //底层const,b2本身可变,所指的对象是常量
const int b3 = 20; //顶层const,b3是常量不可变
const int* const b4 = &a; //前一个const为底层,后一个为顶层,b4不可变
const int& b5 = a; //用于声明引用变量,都是底层const


执行对象拷贝时有限制,常量的底层const不能赋值给非常量的底层const。


使用命名的强制类型转换函数const\_cast时,只能改变运算对象的底层const。



const int a;
int const a;
const int *a;
int *const a;


* int const a和const int a均表示定义常量类型a。
* const int \*a,其中a为指向int型变量的指针,const在 \* 左侧,表示a指向不可变常量。(看成const (\*a),对引用加const)
* int \*const a,依旧是指针类型,表示a为指向整型数据的常指针。(看成const(a),对指针const)


##### 15、简单介绍内存池?


内存池是一种内存分配方式。通常我们习惯直接使用new、malloc申请内存。


这样做的缺点在于所申请内存块的大小不定,当频繁使用时会造成大量的内存碎片并进而降低性能。


内存池则是在真正使用内存之前,预先申请分配一定数量、大小相等(一般情况下)的内存块留作备用。


当有新的内存需求时,就从内存池中分出一部分内存块,若内存块不够再继续申请新的内存。这样做的一个显著优点是,使得内存分配效率得到提升。


##### 16、宏定义和函数有何区别?


* 宏在编译时完成替换,之后被替换的文本参与编译,相当于直接插入了代码,运行时不存在函数调用,执行起来更快;函数调用在运行时需要跳转到具体调用函数。
* 宏定义属于在结构中插入代码,没有返回值;函数调用具有返回值。
* 宏定义参数没有类型,不进行类型检查;函数参数具有类型,需要检查类型。
* 宏定义不要在最后加分号。


##### 17、变量声明和定义区别?


* 声明仅仅是把变量的声明的位置及类型提供给编译器,并不分配内存空间;定义要在定义的地方为其分配存储空间。
* 相同变量可以在多处声明(外部变量extern),但只能在一处定义。


##### 18、strlen和sizeof区别?


* sizeof是运算符,并不是函数,结果在编译时得到而非运行中获得;strlen是字符处理的库函数。
* sizeof参数可以是任何数据的类型或者数据(sizeof参数不退化);strlen的参数只能是字符指针且结尾是’\0’的字符串。
* 因为sizeof值在编译时确定,所以不能用来得到动态分配(运行时分配)存储空间的大小。



int main(int argc, char const *argv[]){

  const char\* str = "name";
  sizeof(str); // 取的是指针str的长度,是8
  strlen(str); // 取的是这个字符串的长度,不包含结尾的 \0。大小是4
  return 0;

}


##### 19、简单描述内存泄漏?


**内存泄漏**一般是指堆内存的泄漏,也就是程序在运行过程中动态申请的内存空间不再使用后没有及时释放,导致那块内存不能被再次使用。


##### 20、什么函数不能声明为虚函数?构造函数为什么不能为虚函数?


* 普通函数(非成员函数)、构造函数、友元函数、静态成员函数、内联成员函数。
* 当派生类在创建对象的时候会调用基类的构造函数,但是如果基类的构造函数是虚函数的话,派生类的构造函数又会把基类的构造函数覆盖,所以无法进一步执行而出错。
* 虚函数通过虚函数表来实现,而指向虚函数表的指针也需要在对象实例化后创建,那么就违背了先实例化后调用的准则。


##### 21、常量指针和指针常量区别?


* 常量指针是一个指针,读成常量的指针,指向一个只读变量。如int const \*p或const int \*p。
* 指针常量是一个不能给改变指向的指针。指针是个常亮,不能中途改变指向,如int \*const p。


##### 22、a和&a有什么区别?



假设数组int a[10];
int (*p)[10] = &a;


* a是数组名,是数组首元素地址,+1表示地址值加上一个int类型的大小,如果a的值是0x00000001,加1操作后变为0x00000005。\*(a + 1) = a[1]。
* &a是数组的指针,其类型为int (\*)[10](就是前面提到的数组指针),其加1时,系统会认为是数组首地址加上整个数组的偏移(10个int型变量),值为数组a尾元素后一个元素的地址。
* 若(int \*)p ,此时输出 \*p时,其值为a[0]的值,因为被转为int \*类型,解引用时按照int类型大小来读取。


##### 23、虚函数与纯虚函数的区别?


* 定义一个函数为虚函数,不代表函数为不被实现的函数。
* 定义他为虚函数是为了允许用基类的指针来调用子类的这个函数。
* 定义一个函数为纯虚函数,才代表函数没有被实现。
* 定义纯虚函数是为了实现一个接口,起到一个规范的作用,规范继承这个类的程序员必须实现这个函数。


##### 24、数组名和指针(这里为指向数组首元素的指针)区别?


* 二者均可通过增减偏移量来访问数组中的元素。


数组名不是真正意义上的指针,可以理解为常指针,所以数组名没有自增、自减等操作。


当数组名当做形参传递给调用函数后,就失去了原有特性,退化成一般指针,多了自增、自减操作,但sizeof运算符不能再得到原数组的大小了。


##### 25、内联函数和宏定义的区别?


宏定义在预处理的时候进行简单的字符串替换,而内联函数在编译时在每个调用内联函数的地方将函数展开,这样不用使内联函数占用栈空间,提高效率。


宏定义没有类型检查,但是内联函数还是具有函数的性质,有参数以及返回值。


##### 26、struct与union的区别?


* struct可以存储多个成员变量信息;而union每个成员会共用同一个存储空间。
* 系统分配给union的内存size就是size最大的成员所需空间大小,struct在分配空间时,根据成员类型不同,会存在字节对齐情况,具体对齐标准和机器有关,可能是4字节或8字节等对齐方式。
* 在任何同一时刻,union值存放了一个被先选中的成员,而结构体struct的所有成员都存在。


##### 27、什么是野指针和悬空指针?


都是是指向无效内存区域(这里的无效指的是"不安全不可控")的指针,访问行为将会导致未定义行为。


**野指针**,指的是没有被初始化过的指针。



int main(void) {
int * p;
std::cout<<*p<<std::endl;
return 0;
}


为了防止出错,对于指针初始化时都是赋值为 nullptr,这样在使用时编译器就会直接报错,产生非法内存访问。


**悬空指针**,指针最初指向的内存已经被释放了的一种指针。



int main(void) {
int * p = nullptr;
int* p2 = new int;
p = p2;
delete p2;
}


此时 p和p2就是悬空指针,指向的内存已经被释放。继续使用这两个指针,行为不可预料。需要设置为p=p2=nullptr。此时再使用,编译器会直接保错。


避免野指针比较简单,但悬空指针比较麻烦。c++引入了智能指针,C++智能指针的本质就是避免悬空指针的产生。


**产生原因及解决办法**:


野指针:指针变量未及时初始化 => 定义指针变量及时初始化,要么置空。


悬空指针:指针free或delete之后没有及时置空 => 释放操作后立即置空。


##### 28、final和override关键字


**final**:


当不希望某个类被继承,或不希望某个虚函数被重写,可以在类名和虚函数后添加final关键字,添加final关键字后被继承或重写,编译器会报错。例子如下:



class Base
{
virtual void foo();
};

class A : public Base
{
void foo() final; // foo 被override并且是最后一个override,在其子类中不可以重写
};

class B final : A // 指明B是不可以被继承的
{
void foo() override; // Error: 在A中已经被final了
};

class C : B // Error: B is final
{
};


**override**:


当在父类中使用了虚函数时候,你可能需要在某个子类中对这个虚函数进行重写,以下方法都可以:



class A
{
virtual void foo();
}
class B : public A
{
void foo(); //OK
virtual void foo(); // OK
void foo() override; //OK
}


如果不使用override,当你手一抖,将foo()写成了foo()会怎么样呢?


结果是编译器并不会报错,因为它并不知道你的目的是重写虚函数,而是把它当成了新的函数。


如果这个虚函数很重要的话,那就会对整个程序不利。


所以,override的作用就出来了,它指定了子类的这个虚函数是重写的父类的,如果你名字不小心打错了的话,编译器是不会编译通过的:



class A
{
virtual void foo();
};
class B : public A
{
virtual void f00(); //OK,这个函数是B新增的,不是继承的
virtual void f0o() override; //Error, 加了override之后,这个函数一定是继承自A的,A找不到就报错
};


##### 29、static的用法和作用?


* 隐藏,static函数,static变量均可。当同时编译多个文件时,所有未加static前缀的全局变量和函数都具有全局可见性。
* 保持变量内容的持久。(static变量中的记忆功能和全局生存期)存储在静态数据区的变量会在程序刚开始运行时就完成初始化,也是唯一的一次初始化。  
 共有两种变量存储在静态存储区:全局变量和static变量,只不过和全局变量比起来,static可以控制变量的可见范围,说到底static还是用来隐藏的。
* static的第三个作用是默认初始化为0(static变量)。  
 其实全局变量也具备这一属性,因为全局变量也存储在静态数据区。在静态数据区,内存中所有的字节默认值都是0x00,某些时候这一特点可以减少程序员的工作量。
* static的第四个作用:C++中的类成员声明static


	+ 函数体内static变量的作用范围为该函数体,不同于auto变量,该变量的内存只被分配一次,因此其值在下次调用时仍维持上次的值;
	+ 在模块内的static全局变量可以被模块内所用函数访问,但不能被模块外其它函数访问;
	+ 在模块内的static函数只可被这一模块内的其它函数调用,这个函数的使用范围被限制在声明它的模块内;
	+ 在类中的static成员变量属于整个类所拥有,对类的所有对象只有一份拷贝;
	+ 在类中的static成员函数属于整个类所拥有,这个函数不接收this指针,因而只能访问类的static成员变量。
	+ static类对象必须要在类外进行初始化,static修饰的变量先于对象存在,所以static修饰的变量要在类外初始化;
	+ 由于static修饰的类成员属于类,不属于对象,因此static类成员函数是没有this指针的,this指针是指向本对象的指针。正因为没有this指针,所以static类成员函数不能访问非static的类成员,只能访问 static修饰的类成员;
	+ static成员函数不能被virtual修饰,static成员不属于任何对象或实例,所以加上virtual没有任何实际意义;静态成员函数没有this指针,虚函数的实现是为每一个对象分配一个vptr指针,而vptr是通过this指针调用的,所以不能为virtual;虚函数的调用关系,this->vptr->ctable->virtual function。


##### 30、拷贝初始化和直接初始化?


* 当用于类类型对象时,初始化的拷贝形式和直接形式有所不同:直接初始化直接调用与实参匹配的构造函数,拷贝初始化总是调用拷贝构造函数。拷贝初始化首先使用指定构造函数创建一个临时对象,然后用拷贝构造函数将那个临时对象拷贝到正在创建的对象。



string str1(“I am a string”);//语句1 直接初始化
string str2(str1);//语句2 直接初始化,str1是已经存在的对象,直接调用构造函数对str2进行初始化
string str3 = “I am a string”;//语句3 拷贝初始化,先为字符串”I am a string“创建临时对象,再把临时对象作为参数,使用拷贝构造函数构造str3
string str4 = str1;//语句4 拷贝初始化,这里相当于隐式调用拷贝构造函数,而不是调用赋值运算符函数


* 为了提高效率,允许编译器跳过创建临时对象这一步,直接调用构造函数构造要创建的对象,这样就完全等价于直接初始化了(语句1和语句3等价)。但是需要辨别两种情况。
* 当拷贝构造函数为private时:语句3和语句4在编译时会报错。
* 使用explicit修饰构造函数时:如果构造函数存在隐式转换,编译时会报错。


##### 31、初始化和赋值的区别?


* 对于简单类型来说,初始化和赋值没什么区别
* 对于类和复杂数据类型来说,这两者的区别就大了。



class A{
public:
int num1;
int num2;
public:
A(int a=0, int b=0):num1(a),num2(b){};
A(const A& a){};
//重载 = 号操作符函数
A& operator=(const A& a){
num1 = a.num1 + 1;
num2 = a.num2 + 1;
return *this;
};
};
int main(){

A a(1,1);
A a1 = a; //拷贝初始化操作,调用拷贝构造函数
A b;
b = a;//赋值操作,对象a中,num1 = 1,num2 = 1;对象b中,num1 = 2,num2 = 2
return 0;

}


##### 32、free()一个指针两次,会出现什么问题 ?


free一次后,原来指针所指向的堆中的内容已经被清空了,但指针本身的值并没有被置为null,还是指向原来它所指向的内存空间。


再free一次时,由于堆中的内容已经是无效的东西,所以就会出错。


不过,有的编译器在free时并没有清理堆中的内存,有时你对它free两次也不一定出错。不过这是一个很大的隐患,在实际写代码中千万要注意避开这点。


##### 33、浅拷贝是什么?深拷贝是什么?


**浅拷贝**:


* 在拥有指针成员的类中,一个对象利用拷贝构造函数或者赋值函数拷贝或者赋值给另一个对象的时候,直接将这个对象的指针成员赋值给另一个对象的指针成员,将一个指针赋值给另一个指针,就会使两个指针指向同一个空间,这就产生了浅拷贝。
* 浅拷贝会造成一些问题例如内存泄漏、同一片内存释放多次、一个指针修改了这块空间的值那么另一个指针也指向这块空间就会出错。


**深拷贝**:


* 在拷贝构造函数或赋值函数中不是直接的将指针赋给另外一个对象的指针,而是新开辟一块内存空间,将被拷贝或赋值的对象的指针成员指向新开辟的内存空间,然后再将数据拷贝过去。


##### 34、hash表解决冲突的方法?


* 开放地址法:当关键字key的哈希地址p=H(key)出现冲突时,以p为基础,产生另一个哈希地址p1,如果p1仍然冲突,再以p为基础,产生另一个哈希地址p2,…,直到找出一个不冲突的哈希地址pi ,将相应元素存入其中。
* 链地址法:基本思想是将所有哈希地址为 i 的元素构成一个称为同义词链的单链表,并将单链表的头指针存在哈希表的第i个单元中,因而查找、插入和删除主要在同义词链中进行。链地址法适用于经常进行插入和删除的情况。
* 建立公共溢出区:这种方法的基本思想是:将哈希表分为基本表和溢出表两部分,凡是和基本表发生冲突的元素,一律填入溢出表。
* 再哈希法: 再哈希法又叫双哈希法,有多个不同的Hash函数,当发生冲突时,使用第二个,第三个,….,等哈希函数计算地址,直到无冲突。虽然不易发生聚集,但是增加了计算时间。


##### 35、新建一个空类,里面有什么函数?


无参构造函数,析构函数,拷贝构造函数,重载赋值运算符函数。


##### 36、volatile关键字的作用?


volatile 关键字告诉编译器该关键字修饰的变量是随时可能发生变化的。


每次使用它的时候必须从内存中取出它的值,因而编译器生成的汇编代码会重新从它的地址处读取数据放在左值中。


如果该变量是一个寄存器变量或者表示一个端口数据或者是多个线程的共享数据,就容易出错,所以说volatile 可以保证对特殊地址的稳定访问。


##### 37、为什么析构函数一般写成虚函数?


由于类的多态性,基类指针可以指向派生类的对象,如果删除该基类的指针,就会调用该指针指向的派生类析构函数,而派生类的析构函数又自动调用基类的析构函数,这样整个派生类的对象完全被释放。


如果析构函数不被声明成虚函数,则编译器实施静态绑定,在删除基类指针时,只会调用基类的析构函数而不调用派生类析构函数,这样就会造成派生类对象析构不完全,造成内存泄漏。


将析构函数声明为虚函数,在实现多态时,当用基类操作派生类,在析构时防止只析构基类而不析构派生类的状况发生,要将基类的析构函数声明为虚函数。



#include
using namespace std;

class Parent{
public:
Parent(){
cout << “Parent construct function” << endl;
};
~Parent(){
cout << “Parent destructor function” <<endl;
}
};

class Son : public Parent{
public:
Son(){
cout << “Son construct function” << endl;
};
~Son(){
cout << “Son destructor function” <<endl;
}
};

int main()
{
Parent* p = new Son();
delete p;
p = NULL;
return 0;
}
//运行结果:
//Parent construct function
//Son construct function
//Parent destructor function


将基类的析构函数声明为虚函数:



#include
using namespace std;

class Parent{
public:
Parent(){
cout << “Parent construct function” << endl;
};
virtual ~Parent(){
cout << “Parent destructor function” <<endl;
}
};

class Son : public Parent{
public:
Son(){
cout << “Son construct function” << endl;
};
~Son(){
cout << “Son destructor function” <<endl;
}
};

int main()
{
Parent* p = new Son();
delete p;
p = NULL;
return 0;
}
//运行结果:
//Parent construct function
//Son construct function
//Son destructor function
//Parent destructor function


##### 38、C++中的重载、重写(覆盖)和隐藏的区别?


**重载(overload)**:


重载是指在同一范围定义中的同名成员函数才存在重载关系。


主要特点是函数名相同,参数类型和数目有所不同,不能出现参数个数和类型均相同,仅仅依靠返回值不同来区分的函数,重载和函数成员是否是虚函数无关。



class A{

virtual int fun();
void fun(int);
void fun(double, double);
static int fun(char);

}


**重写(覆盖)(override)**:  
 重写指的是在派生类中覆盖基类中的同名函数,重写就是重写函数体,要求基类函数必须是虚函数且:


* 与基类的虚函数有相同的参数个数
* 与基类的虚函数有相同的参数类型
* 与基类的虚函数有相同的返回值类型



//父类
class A{
public:
virtual int fun(int a){}
}
//子类
class B : public A{
public:
//重写,一般加override可以确保是重写父类的函数
virtual int fun(int a) override{}
}


重载与重写的区别:


* 重写是父类和子类之间的垂直关系,重载是不同函数之间的水平关系
* 重写要求参数列表相同,重载则要求参数列表不同,返回值不要求
* 重写关系中,调用方法根据对象类型决定,重载根据调用时实参表与形参表的对应关系来选择函数体


**隐藏(hide)**:  
 隐藏指的是某些情况下,派生类中的函数屏蔽了基类中的同名函数,包括以下情况:


* 两个函数参数相同,但是基类函数不是虚函数。和重写的区别在于基类函数是否是虚函数。



//父类
class A{
public:
void fun(int a){
cout << “A中的fun函数” << endl;
}
};
//子类
class B : public A{
public:
//隐藏父类的fun函数
void fun(int a){
cout << “B中的fun函数” << endl;
}
};
int main(){
B b;
b.fun(2); //调用的是B中的fun函数
b.A::fun(2); //调用A中fun函数
return 0;
}


* 两个函数参数不同,无论基类函数是不是虚函数,都会被隐藏。和重载的区别在于两个函数不在同一个类中。



//父类
class A{
public:
virtual void fun(int a){
cout << “A中的fun函数” << endl;
}
};
//子类
class B : public A{
public:
//隐藏父类的fun函数
virtual void fun(char* a){
cout << “A中的fun函数” << endl;
}
};
int main(){
B b;
b.fun(2); //报错,调用的是B中的fun函数,参数类型不对
b.A::fun(2); //调用A中fun函数
return 0;
}


##### 39、C++有哪几种的构造函数?


C++中的构造函数可以分为4类:


* 默认构造函数
* 初始化构造函数(有参数)
* 拷贝构造函数
* 移动构造函数(move和右值引用)
* 委托构造函数
* 转换构造函数



#include
using namespace std;

class Student{
public:
Student(){//默认构造函数,没有参数
this->age = 20;
this->num = 1000;
};
Student(int a, int n):age(a), num(n){}; //初始化构造函数,有参数和参数列表
Student(const Student& s){//拷贝构造函数,这里与编译器生成的一致
this->age = s.age;
this->num = s.num;
};
Student(int r){ //转换构造函数,形参是其他类型变量,且只有一个形参
this->age = r;

最后

我还通过一些渠道整理了一些大厂真实面试主要有:蚂蚁金服、拼多多、阿里云、百度、唯品会、携程、丰巢科技、乐信、软通动力、OPPO、银盛支付、中国平安等初,中级,高级Java面试题集合,附带超详细答案,希望能帮助到大家。

新鲜出炉的蚂蚁金服面经,熬夜整理出来的答案,已有千人收藏

还有专门针对JVM、SPringBoot、SpringCloud、数据库、Linux、缓存、消息中间件、源码等相关面试题。

新鲜出炉的蚂蚁金服面经,熬夜整理出来的答案,已有千人收藏

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

需要这份系统化的资料的朋友,可以点击这里获取

m = 1000;
};
Student(int a, int n):age(a), num(n){}; //初始化构造函数,有参数和参数列表
Student(const Student& s){//拷贝构造函数,这里与编译器生成的一致
this->age = s.age;
this->num = s.num;
};
Student(int r){ //转换构造函数,形参是其他类型变量,且只有一个形参
this->age = r;

最后

我还通过一些渠道整理了一些大厂真实面试主要有:蚂蚁金服、拼多多、阿里云、百度、唯品会、携程、丰巢科技、乐信、软通动力、OPPO、银盛支付、中国平安等初,中级,高级Java面试题集合,附带超详细答案,希望能帮助到大家。

[外链图片转存中…(img-GOwnJGbm-1715816753028)]

还有专门针对JVM、SPringBoot、SpringCloud、数据库、Linux、缓存、消息中间件、源码等相关面试题。

[外链图片转存中…(img-MiNd2SfE-1715816753028)]

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

需要这份系统化的资料的朋友,可以点击这里获取

  • 23
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值