2. C++基础下

运算符

单目运算符:一个运算对象,如 ~a

双面运算符:两个运算对象,如 a+b

三目运算符:三个运算对象

多目运算符:多个运算对象

基本运算符

1. 算数运算符

(1)判断是否能被3整除

if (data%3 == 0) cout<<"yes"<<endl;

2. 复合运算符

(1)以下表达式执行完后 a 的值为

int a = 12;
a += a-= a *= a; //从右向左指向,最后结果为0

从右往左看,首先是(a *= a),此步执行a = a * a,a的值为144。下一步执行(a -= a),即a = a - a,最终结果为0

3. 关系运算符

> < == >= <= !=

4. 逻辑运算符

&& || !

”短路特性“:当使用&&时,一旦检测到一个假,后面算术就不再执行;当使用 || 时,一旦检测到一个真,后面算数就不再执行

再c语言中,除了0为假,其他都为真

5. 位运算(二进制位运算)

(1)按位与 &

& 按位与,常用于将指定位 清0

unsigned char data;
data = data & 0xe7; 
//含义为data = data & 1110 0111,将3,4位清零;注意从0位开始数。
//0xe7就是1110 0111,但C++不支持这种二进制写法
data &= 0xe7

(2)按位或 |

| 按位或,常用于将指定位 置1

(3)按位取反 ~

(4)按位异或 ^

语法:相同为0,不同为1

特点:和1异或取反,和0异或保持不变

场景:将指定位 发生翻转

(5)左移 << (乘法)

左边舍弃,右边补0

data = 0b01100001 << 3; //data 变为 0b00001000,在位数足够大的情况下,相当于data * 2^3

(5)右移 << (除法)

无符号数:右边舍弃,左边补0

有符号数:

​ 正数:右边舍弃,左边补0

​ 负数:右边舍弃,左边补0(逻辑右移)

​ 负数:右边舍弃,左边补1(算术右移)

/* 案例1:data为1字节,将data的第3、4位清0,其他位保持不变 */
data = data & 0b11100111; //方法1
data &= ~(0x01<<4 | 0x01<<3) //方法2,推荐
/* 案例2:data为1字节,将data的第5、6位置1,其他位保持不变 */
data = data | 0b00110000;
data |= (0x01<<6 | 0x01<<5)

6. 三目运算符

表达式 ? 值1 : 值2

int data1 = 10;
int data2 = 20;

(data1>data2 ? data1 : data2) = 200;//C语言中该式不成立,但C++中可以这么用
cout<<data1<<endl;//输出10
cout<<data2<<endl;//输出200

7. 自增自减运算符

++i i++

运算符优先级

优先级别数值越小,优先级越高,同一优先级则看结合性
在这里插入图片描述
在这里插入图片描述

cout<<10>20?10:20; //报错
/* 输出运算符<<优先级别为5,大于优先级别为13的三目运算符;将使用cout<<10的结果带入 ? : 三目运算符,算数符>无法识别cout<<10的结果 */

随机数

rand()产生一个大于0的随机数

rand()其实是一种伪随机。函数在执行时,函数体是确定的,因此每次执行rand()都返回41

需要设置随机数种子,如:srand(0),相当于基准值;仅需设置一次就行了。

srand()给出不同的基准,rand()得到的随机数就不相同;

为了使srand()有不同的基准,采用变化的时间作为参数是一种常用的方式,即srand(time(NULL)),需要引入<time.h>

方法:rand()%(b-a+1) + a

  1. 产生60-100的随机数
rand()%41+60;
  1. 产生’a’-'z’的随机字母

ASCII码值分别为97,122

rand()%26+'a';//等价于rand()%26+97
  1. srand()产生不同的随机数
srand(time(NULL));//要引入# include <time.h>
for(int i=0; i<5; ++i) cout<<rand()<<endl;

控制语句

(1)if…else if…else

(2)switch

switch (表达式){//表达式只能是字符型或整型的(short int; int; long int; char)
case: 常量表达式1:
    语言1;
  	break;
case: 常量表达式1:
    语言2;
  	break;
default:
    语句3;
    break;
} 

(3)for循环,注意continue和break的使用

(4)while循环

数组

一维数组

用一段连续空间存放相同类型的变量,这样的容器(结构)叫数组

  1. 定义
int arr[5];//定义一个数组,有5个元素,每个元素都是int型
int *arr[5];//定义一个数组,有5个元素,每个元素都是int *型;[]的优先级大于*
int arr[5][10];//定义一个数组,有5个元素,每个元素都是一个10大小的数组
int (*arr[5])(int, int); //定义一个数组,有5个元素,每个元素都是函数的入口地址,有两个int形参,返回类型为int
  1. 初始化

如果数组的全部元素都初始化,那么可以省略[]的数值

如果省略了[]里面的数值,那么数组元素的个数由初始化元素个数决定

(1)全部元素的初始化

int arr[5] = {1,2,3,4,5};
int arr[] = {1,2,3,4,5};//两者效果一样

(2)部分元素的初始化

int arr[5] = {1,2,3}//未被初始化的部分,自动补0

(3)将所有元素初始化为0

int arr[5] = {0};//将第0个元素初始化为0,其他自动初始化为0
int arr[5] = {10}; //10,0,0,0,0

(4)指定下标初始化

int arr[5] = {[2]=10, [4]=30};//0, 0, 10, 0, 30

二维数组

  1. 概述
    在这里插入图片描述
  2. 初始化

(1)分段初始化

//完全初始化
int arr[3][4]={ {1,2,3,4}, {5,6,7,8}, {9,10,11,12} }; //按行给出值,一个大括号代表一行
//完全初始化,可以省略行数
int arr[][4]={ {1,2,3,4}, {5,6,7,8}, {9,10,11,12} };
//部分初始化
int arr[3][4]={ {1,2}, {5,6}, {9,10,11} }; //每行都没放满,但每行都有,未初始化的部分补0

(2)连续初始化

//完全初始化
int arr[3][4]={1,2,3,4,5,6,7,8,9,10,11,12}; //依旧按行放置,放满一行存放下一行
//完全初始化,可以省略行数
int arr[][4]={1,2,3,4,5,6,7,8,9,10,11,12};
//部分初始化
int arr[3][4]={1,2,5,6,9,10,11}; //上一行满了之后再放一行,这里arr[2]第3行全补0

(3)案例

int arr1[3][4]={ {1,2}, {5,6}, {9,10,11} };
int arr2[3][4]={1,2,5,6,9,10,11};
//则:arr1[1][2]+arr2[1][2] == 11

一维字符数组(字符串)

  1. 初始化

(1)逐个初始化(不推荐)

char arr[5]={'h','e','l','l','o'};

(2)字符串的方式初始化(推荐)

char arr[6]="hello";

注意:上面两种初始化,后者会多出一个char元素’\0’

  1. 遍历

(1)for循环逐个输出

(2)cout<<数组名;特别注意,这种方式要遇到’\0’才停止

  1. 获取字符串

(1)cin 的方法,遇到空格、回车结束输入

char str[16] = "";
cin>>str;

(2)cin.getline获取带空格的字符串

char str[16] = "";
cin.getline(str, sizeof(str));

二维字符数组

  1. 初始化
char arr[5][128] = {"hello","world", "thanks", "goodbye", "unbelievable"};
  1. 遍历
int row = sizeof(arr)/sizeof(arr[0]);
for(int i=0;i<row;i++){
	cout>>arr[i];
}
  1. 获取多个字符串
for(i=0;i<row;i++){
	cin>>arr[i];
}

函数

库函数(C++语言库)、自定义函数、系统调用(内核提高给用户的函数接口)
在这里插入图片描述
”出入栈“有开销;返回值先由被调用函数保存在寄存器中,再返回给调用函数

预处理

内存分区

进程:可执行文件从运行到结束整个动态的过程叫做进程。(进程占内存空间)
在32位平台中,每一个进程占4G空间(虚拟空间)
在这里插入图片描述

变量的存储

普通局部变量

在{}定义的普通变量,叫普通局部变量,生命周期和作用范围都在{}内,并采用就近原则

初始化:普通局部变量需要初始化,不初始化则值不确定

存储区域:栈区

普通全局变量(extern)

在函数外定义的普通变量;

作用范围:当前源文件以及其他源文件都有效

生命周期:整个进程

初始化:默认会初始化为0;全局变量和局部变量同名优先选择局部变量

存储区域:全局区

其他:使用外部源文件全局变量时,必须先extern声明,表明该变量或函数来自其他源文件

(1)变量的外部声明,01_fun.cpp文件

extern int data;//表明data来自外部,不对其进行初始化
void add_data(void){
    data += 100;
    return;
}

(2)函数的外部声明,01_code.cpp文件

extern void add_data(void);
int data = 10;
int main(void){
    add_data();
}

静态局部变量

定义形式:在{}用static定义的局部变量

int main(void){
    static int data = 20;
}

作用范围:在当前的{}内,在其他{}会显示未定义

生命周期:整个进程有效。即{}之后不会被释放空间。也就是该数会被累加

void test(void){
    static int a = 10;
    a++;
    cout<<a<<endl;
}

int main(void)
{
    test();//第一次调用输出11
    test();//第二次调用输出12

    return 0;
}

存储区域:全局区

静态全局变量

定义形式:在函数外,加static修饰的变量

作用范围:只能在当前源文件中使用,不能在其他源文件中使用

生命周期:整个进程

存储区域:全局区

全局函数和静态函数

全局函数(默认)

一般创建的函数都是全局函数,在当前源文件或其他源文件中都可以使用

只是其他源文件使用时需要加上extern声明

静态函数

只能在当前源文件中使用,可用于避免函数名冲突

static void func(void){
    
}

头文件包含

#include <> 从系统指定目录寻找

#include “” 从当前目录开始找,找不到再去指定目录

#define 宏

#define PI 3.14

编译四阶段:预处理、编译、汇编、链接

#undef N 取消宏定义

1. 带参数的宏

#define MY_MUL(a, b) a*b
cout<<MY_MUL(10,20);

宏参数没有类型。因为预处理阶段不需要内存

宏不能保证参数的完整性

#define MY_MUL(a, b) a*b
cout<<MY_MUL(10+10,20+20);//替换后变成:10+10*20+20 == 230

#define MY_MUL(a, b) ((a)*(b))

带参宏被调用多少次就会展开多少次,执行代码的时候没有函数调用的过程,不需要进栈出栈。带参宏浪费空间,节省时间

带参函数代码只有一份,存在代码段,调用的时候去代码段取指令,进栈出栈。浪费时间,节省空间

函数有作用域的限制,可以作为类的成员
宏函数没有作用限制,不能作为类的成员

指针

基础

1. 内存的概述

在32位平台,每一个进程拥有4G空间

系统为内存的每一个字节分配一个32位的地址编号(虚拟地址)

任何类型的指针变量都是4个字节大小(32位下)

cout<<sizeof(char *)<<endl;//输出:4
定义一个指针变量,保存int num的地址	int *p;
定义一个指针变量,保存int arr[5]的地址	int (*p)[5]
//1. 保存地址,一定是指针变量*p; 2. 保存谁就定义谁:int arr[5]; 3. 替换int (*p)[5]; 4. 观察小括号能否取消,发现优先级低,不能取消
定义一个指针变量,保存函数的入口地址	int (*p)(int, int);
定义一个指针变量,保存结构体的地址	struct node *p;
定义一个指针变量,保存指针变量int *p的地址	int **p;

在定义时,*p仅仅修饰p为指针变量;在使用时,表示取p所保存的地址编号对应空间的内容

2. 指针变量的初始化

指针变量如果不初始化,立即操作会出现段错误;p是局部变量,不初始化将不确定指向哪个空间

可以初始化为NULL,但不能对NULL指针进行操作。NULL本质上是(void *)0,内存中的起始位置,受到保护

在使用中,&和*相遇时要从右向左

指针变量的指向类型,决定了取值宽度:指针取值需要地址和取值宽度

指针变量的指向类型,决定了+1跨度

int num = 0x01020304;
int *p1 = &num;
cout<<hex<<*p1<<endl;//输出:1020304
short *p2 = (short *)&num;
cout<<hex<<*p2<<endl;//输出:304

3. 综合案例分析

在这里插入图片描述
案例1:取出0x0102的值

short *p = (short *)&num;
*(p+1);

案例2:取出0x02的值

char *p = (char *)&num;
*(p+2);

案例3:取出0x0203的值

char *p = (char *)&num;
(short *)(p+1)

4. 注意事项

void num;//报错,无法识别要分配多少内存空间
void *p;//通过,因为默认都是4字节
*p; //失败,因为通过void判定取值宽度,需要先执行类型强转

越界访问

char ch = 'a';
int *p = &ch;
*p;//会越界

5. 数组和指针

数组名代表了一个指向数组首元素的常量指针

指向同一数组的两个指针可以相减,得到它们间的个数;但不能相加

指针/地址/数组名

指针是一个保存对象地址的变量,即地址变量;

地址是地址变量的值;

数组名是一个地址常量

int a = 20; a是变量,20是常量;指针是变量,数组名是常量

字符串和指针

1. 字符数组

char str1[128] = "hello world";
sizeof(str1) == 128字节

局部放栈区,全局放全局区

2. 字符串指针变量

char *str2 = "hello world";
sizeof(str2) = 4字节

str2的内容(即一个地址)放在栈区/全局区,它的内容(即"hello world")放在文字常量区

!!文字常量区只读,不能修改

指针数组

本质是数组,只是每个元素都是指针

int *arr[4];
sizeof(arr) == 16B

字符指针数组

char *arr[4] = {"hello","world", "thanks", "goodbye", "unbelievable"};

arr中存放四个指针,每个指针指向放在文字常量区的字符串

数组指针

数组首元素地址 和 数组首地址

数组首元素地址:&arr[0] == arr; +1会跳过一个元素

数组首地址:&arr; +1会跳过整个数组

arr本身是一个地址,&arr是地址的地址,而&arr[0]仅仅是一级地址

int (*p)[4];//定义数组,int型,int arr[4]; 用指针表示,则用(*p)取代arr
int arr[5] = {1,2,3,4,5}
int (*p)[5] = &arr;

对数组首地址取* == 数组首元素地址

数组指针和指针数组

char *arr1[2];// = {"one","two"};//会提示C++ forbids converting a const to char *
char (*arr2)[2];// = {"three","four"};//报错
cout<<sizeof(arr1)<<endl;//输出:8
cout<<sizeof(arr2)<<endl;//输出:4

char *arr3[2] = {"one","two"};//会提示C++ forbids converting a const to char *
//char (*arr4)[2] = {"three","four"};//报错
char arr4[2][6] = {"three","four"};
char (*parr4)[6] = arr4;//指向一维数组的数组指针 ,此时parr4指向的是arr4数组的第一个元素(元素是数组)
cout<<*(parr4+1)<<endl;//输出:four

二维数组和数组指针的关系

二维数组名 代表的是第0行的行地址;+1则跳过一行

arr[1] => *(arr+1) 第一行第0列的列地址
&arr[1] => &*(arr+1)=>arr+1 第1行的行地址
*arr+1 => 第0行第1列的列地址
arr[1]+2 =>*(arr+1)+2 =>第1行第2列的列地址
**arr ==*(*(arr+0)+0) == arr[0][0]

注意:多维数组在物理上都是一维存储,在逻辑上是多维的

指针与函数

!!一维数组 作为函数的形参,会被优化成一维指针变量

!!二维数组 作为函数形参,会被优化为一维指针变量(数组指针)

void test(int arr[3][4]){//可以写成(*arr)[4]
    cout<<sizeof(arr)<<endl;//output:4
    cout<<sizeof(arr[0])<<endl;//output:16
}

int main(void)
{
    int arr[3][4] = {1,2,3,4,5,6,7,8,9,10,11,12};
    cout<<sizeof(arr)<<endl;//output: 48
    test(arr);
}

如果将变量地址作为返回值,会报段错误,因为该地址指向的值已经被释放掉了。可以给变量加static

函数指针

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

int main(void){
   int (*p)(int x, int y) = NULL;
   p = func;
   cout<<p(10, 20)<<endl;//调用
}

对于函数指针而言,+1和*p都没有意义

*p会被优化成p

typedef定义函数别名

typedef int (*FUN_TYPE)(int x, int y);
FUN_TYPE p = NULL;
p = func;

函数指针作为函数的指针

案例:实现一个算法,完成加减乘除

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

int my_del(int x, int y){
    return x-y;
}

int my_mul(int x, int y){
    return x*y;
}

int my_div(int x, int y){
    return x/y;
}

int calc(int x, int y, int (*func)(int, int)){
    return func(x,y);
}

int main(void){
    // 函数名是函数的入口地址
    cout<<calc(10,20,my_add)<<endl;
    cout<<calc(10,20,my_del)<<endl;
    cout<<calc(10,20,my_mul)<<endl;
    cout<<calc(10,20,my_div)<<endl;
    return 0;
}

动态内存

静态在栈区或全局区,动态一般在堆区

int *p = new int;
*p = 100;
delete p;

//一定要释放空间,new和delete要同时出现

int *q = new int(100); //动态申请int空间,并初始化为100
delete q;

//申请数组空间
int *arr = new int[5]{1,2,3,4,5};
delete []arr; //也要加[]

字符串

size_t:64位架构中size_t是:long long unsigned int;32位架构中是:long unsigned int

#include <string.h>
size_t strlen(const char *s);//测量字符串长度;
char *strcpy(char *dest, const char *src);//将src复制给dest
char *strcat(char *dest, const char *src);//将src连接到dest后面
char *strncat(char *dest, const char *src, size_t n);//表示只复制前n个字符
int strcmp(const char *s1, const char *s2);
int strcmp(const char *s1, const char *s2, size_t n);//比较前n个字符

示例1

char str1[] = "";//str1只有1字节,拷贝过来后会造成溢出(内存污染,覆盖掉后面内存的值);可以写成char str1[128];
char str2[] = "world";
strcpy(str1, str2);
cout<<str1<<endl;

注意,dest参数是数组类型,不能用字符串指针,因为char *str 会放到只读区文字常量区

结构体

定义和初始化

结构体中的成员拥有独立的空间。定义结构体时不要初始化成员变量的值,因为这是类型定义;

C++ 11这样赋值虽然能够通过编译,但实际是在实例化时复制该值

1. 定义和初始化

定义局部结构体变量时,必须初始化,且遵守成员的顺序和其数据类型

struct Student{
    int num;
    char name[32];
};
Student zs = {100, "zs"};//注意name不能用=号赋值,用strcpy
cout<<zs.num<<" "<<zs.name<<endl;

当暂时不想对结构体变量初始化,又要避免其为随机值,可以先清空结构体变量

memset(&zs, 0, sizeof(zs))

memset原型为

void *memset(void *_Dst, int _Val, size_t _Size);
//将地址从_Dst开始,长度为_Size的所有字节都赋值为_Val

2. 赋值方式

Student ls;
//(1)逐个成员赋值
ls.num = zs.num;
strcpy(ls.name, zs.name);
//(2)相同结构体直接可以直接赋值
ls = zs;
//(3)内存拷贝,是第二种的底层实现
memcpy(&ls, &zs, sizeof(Student));

!结构体允许嵌套结构体

3. 结构体数组

Student arr[3] = {{1,"zs"},{2,"ls"},{3,"ww"}};

结构体指针变量

Student zs = {100, "zs"};//结构体变量
Student *p = &zs;
cout<<p->num;
Student arr[5];//结构体数组
...
for(int i=0; i<5; ++i)
	cin>>(arr+1)->num>>(arr+1)->name;

结构体的深拷贝和浅拷贝

1. 浅拷贝

浅拷贝:将结构体变量内容复制一份到另一个相同类型结构体变量之中

如果结构体中没有指针成员,则浅拷贝不会带来问题

如果有指针成员,会带来多次释放堆区空间的问题

zs.name = new char[32];
strcpy(zs.name, "zs");
ls = zs;
delete []zs.name;
delete []ls.name;
//zs和ls的name是同一个地址,zs释放了该地址指向的堆区的空间,ls又释放了一遍

解决方法:先为ls的name申请一份堆空间,再拷贝一边,也就是深拷贝

2. 深拷贝

ls.name = new char[32];
strcpy(ls.name, zs.name);

3. 结构体指针变量在堆区,结构体指针成员指向堆区

//结构体在堆区
Student *p = new Student;
//结构体指针成员指向堆区
p->name = new char[32];
//赋值
p->num = 100;
strcpy(p->name, "zs");
//释放空间,注意顺序
delete []p->name;
delete p;

在这里插入图片描述

结构体的对齐规则

在这里插入图片描述

自动对齐

  1. 确定分配单位(一行分配多少字节):由最大基本类型的长度决定
  2. 确定成员的偏移量:成员偏移量 = 成员自身类型的整数倍
  3. 收尾工作:结构体总大小 = 分配单位整数倍

案例:画出下列结构体的内存布局

struct Data{
    char a;
    short b;
    int c;
    char d;
    short e;
}

cpu一次提取4个字节

最大类型为int,所以一行4个字节

最后得到三行,以*表示空,分别是 a * b b c c c c d * e e

不能插入到两个变量之间

强制对齐

#pragma pack(value) //指定对齐值value,注意为2的幂

此时分配单位是,min(最大基本类型的长度,value)

#pragma pack(8)
struct A {
    char a;
    int b;
    short c;
};
//分配为 a * * *		b b b b		c c * *
#pragma pack(2)
//则上述分配为 a *	b b		b b		c c

位域

1. 位域及压缩

在结构体中,以位为单位的成员,称之为位段(位域)

struct packed_data{
    unsigned int a:2;
    unsigned int b:6;
    unsigned int c:4;
    unsigned int d:4;
} data;
cout<<sizeof(data);//output: 4;int占4字节,因此还有16 bit的空位置
//如果改成unsigned char,则输出:2

在这里插入图片描述
unsigned int a:2,表示a只占2位二进制位

没有非位域隔开的位域,叫相邻位域。如abcd就是相邻位域

相邻位域可以压缩,但是压缩的位数不能超过自身类型的大小。

不能对位域取地址

2. 另起存储单元(禁止压缩)

struct packed_data{
    unsigned char a:4;
    unsigned char :0;//另起一个存储单元,禁止压缩
    unsigned char c:4;
} data;
cout<<sizeof(data); //输出:2	如果允许压缩,将输出:1

3. 无意义位

unsigned char :2;//无意义位段,要求占2位,但不用另起一段

案例

在这里插入图片描述

共用体 union

结构体:每个成员独立空间

共用体:所有成员共享空间,总大小为最大成员变量的大小

union Data {
    char a;
    short b;
    int c;
};

int main(void){
    Data data;
    data.a = 10;
    data.b = 20;
    data.c = 30;
    cout<<(int)data.a<<endl;//输出:30
    cout<<data.b<<endl;//输出:30
    cout<<data.c<<endl;//输出:30

    return 0;
}

对任意一个成员的操作都是对共有空间的操作
a可以操作一个字节,b可以操作2个字节,c可以操作4个字节

案例:

data.a = 0x01020304
data.b = 0x0102
data.c = 0x01
data.a 赋值后,4个字节内存储的分别是 04 03 02 01	(倒着存)
data.b 赋值后,4个字节内存储的分别是 02 01 02 01
data.c 赋值后,4个字节内存储的分别是 01 01 02 01	(最终)
取data.a,取到的是 01 01 02 01
取data.b,取到的是 01 01
取data.c,取到的是 01
cout<<hex<<data.a+data.b+data.c<<endl; //为0x 03 02 02 01,输出时再倒回来,就是0x01020203(开头的0会被省略)

枚举

enum POKER{HONGTAO, MEIHUA, FANGKUAI, HEITAO};//默认从0开始,0,1,2,3

可以用POKER定义变量,但没有意义

POKER po = HONGTAO;
pc = 100;//报错!!pc只能赋值为枚举列表中的值,如MEIHUA

自定义值

enum POKER{HONGTAO, MEIHUA=10, FANGKUAI, HEITAO};
//此时FANGKUAI也会被修改为11,HEITAO为12

链表

学生管理系统 main.cpp link.h link.cpp
link.h

#ifndef LINK_H
#define LINK_H

#include <iostream>
#include <string.h>
using namespace std;

//定义链表节点类型
struct Stu{
    int num;
    char name[32];
    Stu *next;
};

void help(void);
Stu *insertLink(Stu *head, Stu tmp);
void printLink(Stu *head);
Stu *searchLink(Stu *head, char *name);
Stu *deleteLink(Stu *head, int num);
Stu *freeLink(Stu *head);

#endif // LINK_H
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值