重生之拯救C语言计划(操作符篇)

①操作符分类

算术操作符
移位操作符
位操作符
赋值操作符
单目操作符
关系操作符
逻辑操作符
条件操作符
逗号表达式
下标引用、函数调用和结构成员

②算术操作符

+          -           *           /          %

  1. 除了 % 操作符之外,其他的几个操作符都可以作用于整数和浮点数。
  2. % 操作符的两个操作数必须为整数。返回的是整除之后的余数。
  3. 对于 / 操作符如果两个操作数都为整数,执行整数除法。
    而只要有浮点数执行的就是浮点数除法。

③移位操作符

   <<(左移操作符)          >>(右移操作符)

注意:
1.移动的是补码的二进制位,移位操作符的操作数只能是整数
(补充:整数在内存中存储的都是补码的二进制序列)

原码、反码和补码的关系:
正整数的原码、反码和补码相同
负整数的反码=原码除符号位(即首位)全部取反,补码=反码+1
因此,把补码变成原码,可以选择:
(1)补码-1,再除符号位全部取反
(2)补码先除符号位全部取反,再+1

1.左移操作符:(补码)左边丢弃,右边补0

int main()
{
	int m = 7;
	int n = m << 1;//左移一位
	printf("m=%d n=%d", m, n);//打印的是原码
	return 0;
}
//m=7 n=14

若m的值在没被赋值的情况下,自身的值不会改变
7的原码(反码、补码)是:
00000000 00000000 00000000 00000111(正整数的原码,反码,补码相同)
左移一位后:
00000000 00000000 00000000 00001110 = 14
左移多位同理

当m为负数时:

int main()
{
	int m = -7;
	int n = m << 1;//左移一位
	printf("m=%d n=%d", m, n);//打印的是原码
	return 0;
}
//m=-7 n=-14

-7的原码是:
10000000 00000000 00000000 00000111
11111111 11111111 11111111 11111000(反码)(符号位不取反)
11111111 11111111 11111111 11111001(补码)
左移一位后
11111111 11111111 11111111 11110010(补码)
10000000 00000000 00000000 00001110(n的原码)= -14

2.右移操作符:逻辑移位、算术移位

逻辑移位:左边用0填充,右边丢弃
算术移位:左边用原该值的符号位填充,右边丢弃(大多数编译器默认)

int main()
{
	int m = -7;
	int n = m >> 1;//右移一位
	printf("m=%d n=%d", m, n);//打印的是原码
	return 0;
}
//m=-7 n=-4

-7的原码是:
10000000 00000000 00000000 00000111
11111111 11111111 11111111 11111000(反码)(符号位不取反)
11111111 11111111 11111111 11111001(补码)

右移一位后:
逻辑右移:
01111111 11111111 11111111 11111100(补码)
00000000 00000000 00000000 00000100(n的原码)= 4
算术右移:
11111111 11111111 11111111 11111100(补码)
10000000 00000000 00000000 00000100(n的原码)= -4

警告⚠:
对于移位运算符,不要移动负数位,这个是标准未定义的。
例如:

int main()
{
	int m = 7;
	int n = m << -1;//error
	printf("m=%d n=%d", m, n);//打印的是原码
	return 0;
}

④位操作符

   &(按位与)    |(按位或)    ^(按位异或)

1.&:按(二进制)位与

&:两个整数的相同位置:
(1)都为1时,&在该位置为1
(2)都为0时,&在该位置为0
(3)一个为0,一个为1时,&在该位置为0

int main()
{
	int a = 3;
	int b = -5;
	int c = a & b;
	printf("c=%d ", c);
	return 0;
}
//c=3

//00000000 00000000 00000000 00000011  --  3的原码
//10000000 00000000 00000000 00000101  -- -5的原码
//11111111 11111111 11111111 11111011  -- -5的补码
//00000000 00000000 00000000 00000011  --  3的补码
//00000000 00000000 00000000 00000011 = 3  --  3 & -5

&1和<<、>>一起使用可以知道一个整数补码的二进制序列的任何一位

想知道3的补码的最后一位数字是什么

int main()
{
	int a = 3;
	int c = a & 1;
	printf("c=%d", c);
	return 0;
}
//c=1

//00000000 00000000 00000000 00000011  --  3的补码
//00000000 00000000 00000000 00000001  --  1的补码
//00000000 00000000 00000000 00000001  --  3 & 1  //为1

想知道3的补码的倒数第二位数字是什么

int main()
{
	int a = 3;
	int c = a & 1;
	printf("c=%d", c);
	return 0;
}
//c=1

//(3>>1)&1
//00000000 00000000 00000000 00000001  --  3>>1的补码
//00000000 00000000 00000000 00000001  --  1的补码
//00000000 00000000 00000000 00000001  --  (3>>1) & 1  //为1

2. |:按位或

|:两个整数的相同位置:
(1)都为1时,| 在该位置为1
(2)都为0时,| 在该位置为0
(3)一个为0,一个为1时,| 在该位置为1

int main()
{
	int a = 3;
	int b = -5;
	int c = a | b;
	printf("c=%d", c);
	return 0;
}
//c=-5

//00000000 00000000 00000000 00000011  --  3的原码
//10000000 00000000 00000000 00000101  -- -5的原码
//11111111 11111111 11111111 11111011  -- -5的补码
//00000000 00000000 00000000 00000011  --  3的补码
//11111111 11111111 11111111 11111011  --  3 | -5的补码
//10000000 00000000 00000000 00000101 = -5 --  3 | -5的原码

3. ^:按位异或

^:两个整数的相同位置:相同为0,相异为1

int main()
{
	int a = 3;
	int b = -5;
	int c = a ^ b;
	printf("c=%d", c);
	return 0;
}
//c=-8

//00000000 00000000 00000000 00000011  --  3的原码
//10000000 00000000 00000000 00000101  -- -5的原码
//11111111 11111111 11111111 11111011  -- -5的补码
//00000000 00000000 00000000 00000011  --  3的补码
//11111111 11111111 11111111 11111000  --  3 ^ -5的补码
//10000000 00000000 00000000 00001000 = -8 --  3 ^ -5的原码

易知:a ^ a = 0      a ^ 0 = a

^ 具有交换律,即 a ^ b ^ c = a ^ c ^ b

int main()
{
	int a = 3;
	int b = -5;
	int c = a ^ b ^ a;
	printf("c=%d ", c);
	return 0;
}
//c=-5

//一般方法
//00000000 00000000 00000000 00000011  --  3的原码
//10000000 00000000 00000000 00000101  -- -5的原码
//11111111 11111111 11111111 11111011  -- -5的补码
//00000000 00000000 00000000 00000011  --  3的补码
//11111111 11111111 11111111 11111000  --  3 ^ -5的补码
//00000000 00000000 00000000 00000011  --  3的补码
//11111111 11111111 11111111 11111011  --  3 ^ -5 ^ 3的补码
//10000000 00000000 00000000 00000101 = -5 --  3 ^ -5 ^ 3的原码

//交换律
//a ^ b ^ a = a ^ a ^ b = 0 ^ b = b = -5

一道变态的面试题:
不能创建临时变量(第三个变量),实现两个数的交换

int main()
{
	int a = 10;
	int b = 20;
	a = a ^ b;
	b = a ^ b;//b = a^b^b = a^0 = a
	a = a ^ b;//a = a^b^a = 0^b = b
	printf("a = %d b = %d\n", a, b);
	return 0;
}
//a=20 b=10

⑤赋值操作符

=

赋值操作符是一个很棒的操作符,它可以让你修改某些之前不满意的值

int main()
{
	int a = 1;//不是赋值,是初始化
	a = 20;//赋值
	return 0;
}

赋值操作符可以连续使用,例如:

int main()
{
	int a = 1;
	int x = 0;
	int y = 2;
	a = x = y + 1;//连续赋值
	return 0;
}

连续赋值确实会比较简洁,那下面的代码呢?

int main()
{
	int a = 1;
	int x = 0;
	int y = 2;
	x = y + 1;
	a = x;
	//这样的写法是不是更加清晰爽朗而且易于调试。
	return 0;
}

复合赋值符

-=    +=    *=    /=    %=
  >>=    <<=    &=    |=    ^=

int main()
{
	int x = 9;
	x = x + 2;
	x += 2;//复合赋值
	//两种方式是一个意思
	//其他运算符一样的道理。这样写更加简洁、高效
	return 0;
}

⑥单目操作符

 !           逻辑反操作
 -           负值
 +          正值
 &          取地址
 *           间接访问操作符(解引用操作符)
 sizeof   操作数的类型长度(以字节为单位)
 ~          对一个数的二进制按位取反
 - -         前置、后置- -
 ++        前置、后置++
 (类型)   强制类型转换

1. ! 逻辑反操作

即 ! 真 = 假 , ! 假 = 真

int main()
{
	int a = 5;
	int b = !a;
	printf("b=%d", b);
	return 0;
}
//b=0

 +     -     比较简单,就不赘述了

2. & 取地址操作符

不知道你看见它会不会觉得有些眼熟呀,没错!它也是上文 位操作符 中的 &按位与
但虽然它们两都长这样,但所代表的意思却截然不同哦
&取地址操作符是单目操作符,而 &按位与双目操作符

int main()
{
	int a = 10;
	int* p = &a;//a变量的地址

	int arr[10];
	int(*pa)[10] = &arr;//这是整个数组的地址
	return 0;
}

3. * 解引用操作符

有 & 取地址操作符, 就有 * 解引用操作符

int main()
{
	int a = 10;
	int* p = &a;//a变量的地址
	printf("*p=%d", *p);
	//对p进行解引用操作,*p是通过p中存放的地址,找到p指向的对象
	//*p其实就是a
	return 0;
}
//*p=10

4. sizeof 计算类型或变量所占内存空间大小(单位:字节)

sizeof计算的结果是 size_t 类型的,size_t 是无符号整型的,对size_t 类型的数据进行打印,可以使用%zd,%u也可

int main()
{
	int a = 10;
	printf("%zd", sizeof(a));//4
	printf("%zd", sizeof(int));//4
	return 0;
}

sizeof后面的括号中写的不是类型的时候,括号可以省略,这就可以说明sizeof不是函数,而是单目操作符

int main()
{
	int a = 10;
	printf("%zd", sizeof a );//4
	printf("%zd", sizeof(int));//4
	return 0;
}

问:下面代码中
(1)、(2)两个地方分别输出多少?
(3)、(4)两个地方分别输出多少?

#include <stdio.h>
void test1(int arr[])
{
	printf("%d\n", sizeof(arr));//(3)
}

void test2(char ch[])
{
	printf("%d\n", sizeof(ch));//(4)
}

int main()
{
	int arr[10] = { 0 };
	char ch[10] = { 0 };
	printf("%d\n", sizeof(arr));//(1)
	printf("%d\n", sizeof(ch));//(2)
	test1(arr);
	test2(ch);
	return 0;
}

答案:
(1) 40       (2)10       (3) 4/8       (4)4/8
(1) 40:  arr数组中有10个元素,每个元素都是int类型,所以所占空间大小=4 * 10=40
(2) 10:  ch数组中有10个元素,每个元素都是char类型,所以所占空间大小=1 * 10=10
(3)(4) 4/8:
传参传过去的不是数组,而是数组首元素的地址,所以可以把arr想成是指向数组首元素的指针变量
32位平台下有32个比特位即4个字节,因此指针变量的大小全为4
64位平台下有64个比特位即8个字节,因此指针变量的大小全为8

5. ~ 全部取反

int main()
{
	int a = 0;
	printf("~a=%d\n", ~a);
	return 0;
}
//~a=-1

//00000000 00000000 00000000 00000000  -- 0的补码
//11111111 11111111 11111111 11111111  -- ~a的补码(符号位也取反)
//10000000 00000000 00000000 00000001 = -1 -- ~a的原码

如何让某个整数的补码的二进制序列的某一位数发生改变,而其它位不变?

int main()
{
	int a = 10;
	//00000000 00000000 00000000 00001010
	//如何让10的补码的二进制序列的倒数第五位从0变成1,其它位不变
	// 让10 | (1<<4)
	//00000000 00000000 00000000 00010000  -- 1<<4 的补码
	//00000000 00000000 00000000 00011010
	a |= (1 << 4);
	printf("a=%d\n", a);//a=26
	//如何再让a的补码的二进制序列的倒数第五位从1变成0,其它位不变
	//00000000 00000000 00000000 00011010
	//让 a & 下面的数
	//11111111 11111111 11111111 11101111(可以看出这是 ~(1<<4) )
	//00000000 00000000 00000000 00001010
	a &= (~(1 << 4));
	printf("a=%d\n", a);//a=10
	return 0;
}

6. ++ 自增 - - 自减

前置++ : 先自增,再使用
后置++ : 先使用,再自增
 - -同理

int main()
{
	int a = 3;
	int b = 5;
	int c = ++a;
	int d = b++;
	printf("a=%d c=%d", a, c);//a=4 c=4
	printf("b=%d d=%d", b, d);//b=6 d=5
	return 0;
}

如果你遇见了这样的代码,请不要懵逼,因为编译器和你一样懵逼o(≧口≦)o

//这个代码是有问题的
int main()
{
	int a = 1;
	int b = (++a) + (++a) + (++a);
	printf("b=%d", b);
	return 0;
}

在vs2019编辑器上,b=12

在这里插入图片描述
而在gcc编译器上,b=10

在这里插入图片描述
所以,以后再遇见这种类型的题目不要懵逼,要相信,这是题目的问题︿( ̄︶ ̄)︿

7. (类型) 强制类型转换

注意:强制类型转换只有再万不得已的情况下使用

int main()
{
	int a = 3.14;
	//3.14 会被编译器识别为double类型
	return 0;
}

这种情况下,编译器能运行,但会报警告

在这里插入图片描述

当我们需要随机值时,会调用rand()函数,
但在调用该函数前又要调用srand()函数,srand()函数里又要使用time()函数,
time()函数的返回值时time_t,而srand()函数需要的是unsigned int 类型的数,
所以需要强制类型转换,从而使用
srand((unsigned int)time(NULL))

⑦关系操作符

 >      =      <      >=      <=
!= 用于测试“不相等”   == 用于测试“相等

这些关系运算符比较简单,没什么可讲的,但是注意不要将==写成=

⑧逻辑操作符

 && 逻辑与                  | | 逻辑或

等等,我们是不是又在哪里见过它们 Σ( ° △ °|||)
让我想想,哦(拍大腿),它们分别是double个 &按位与 和 | 按位或

那它们该如何区分呢?
&按位与 和 |按位或 需要根据整数的二进制判断,但 &&逻辑与 和 ||逻辑或 是根据数的值来判断。其实逻辑与和逻辑或在我们高中就有学过,但如果你不记得的话,叙述起来也很简单,就是
&& 按位与两边数字全为非0即为真,否则为假,|| 按位或两边数字有 >=1个为非0即为真,否则为假
1&2     ---->0            1|2       ---->3
1&&2   ---->1           1||2       ---->1

了解了这些之后,让我们开始做题吧
下面程序输出的结果是什么?

int main()
{
	int i = 0,j = 0, a = 0, b = 2, c = 3, d = 4;
	i = a++ && ++b && d++;
	printf("a = %d b = %d c = %d d = %d ", a, b, c, d);
	printf("i = %d\n", i);
	return 0;
}

答案是:a=1 b=2 c=3 d=4 i=0
在说明答案之前,让我们先来了解什么叫短路操作,所谓短路操作,简单理解为左右两边数字,只要确定了左边数字的真假,右边无需计算
例如:
&&逻辑与: 左边操作数如果为假,那么这个表达式就为假,不用判断右边数字
| |  逻辑或: 左边操作数如果为真,那么这个表达式就为真,不用判断右边数字

解析:
a++,即a先使用后自增,所以 i = 0 && ++b && d++,根据短路操作,因为左边为0,所以右边的b无需计算,所以第一个表达式为假(即a++ && ++b等于0), 所以 i = 0 && d++,又根据短路操作,因为左边为0,所以右边的d无需计算,第二个表达式为假,因此a自增后为1,bcd不变,i=0

那将&&变为 | |结果又是什么呢?

int main()
{
	int i = 0,j = 0, a = 0, b = 2, c = 3, d = 4;
	i = a++||++b||d++;
	printf("a = %d b = %d c = %d d = %d ", a, b, c, d);
	printf("i = %d\n", i);
	return 0;
}

答案是:a=1 b=3 c=3 d=4 i=1
详解:
a++,即a先使用后自增,所以 i = 0 || ++b || d++,此时还要判断++b是否为非0,
++b即b先自增后使用,所以第一个表达式为0||3为真,所以i=1||d++,
根据短路操作,因为左边为非0,所以右边的d无需计算,
因此a,b自增后分别为1,3, c,d不变, i=1

⑨条件操作符

exp1 ? exp2 : exp3
流程:先判断exp1的真假,
若为真,则只计算exp2且整个表达式结果为exp2的结果,exp3不计算
若为假,则只计算exp3且整个表达式结果为exp3的结果,exp2不计算

int main()
{
	int a = 10;
	int b = 8;
	int max;
	//if (a > b)
	//	max = a;
	//else
	//	max = b;
	max = (a > b ? a : b);//上面注释行可简写为该条件表达式
	printf("%d\n", max);
	return 0;
}

⑩逗号表达式

exp1, exp2, exp3, …expN,逗号表达式,就是用逗号隔开的多个表达式。
从左向右依次执行,前面表达式的结果会被“丢弃”(但是如果表达式对变量的值产生影响,那么该变量的值就会改变),只有最后一个表达式的结果会被留下并成为该逗号表达式的结果。

下面程序输出的结果是什么呢?

int main()
{
	int a = 1;
	int b = 2;
	int c = (a > b, a = b + 10, b = a + 1);//逗号表达式
	printf("c=%d\n", c);
	return 0;
}
//c=13

解析:
由题意,对c进行初始化时,先要计算逗号表达式的值,逗号表达式从左向右依次执行
因此,先执行a>b为假,结果为0,因为不是最后一个表达式的结果,所以0被丢弃;接着执行a=b+10,所以a=12,因为不是最后一个表达式的结果,所以a=12被丢弃,但此时a的值已由1变为12;再执行b=a+1=13,因为是最后一个表达式的结果,所以c=b=13

下面程序中fun()函数有多少个实参呢?

int main()
{
	int v1, v2, v3, v4, v5, v6;
	fun((v1, v2), (v3, v4), v5, v6);
	return 0;
}

答案:4个
(v1, v2)属于第一个实参,逗号表达式,真实的参数时v2
(v3, v4)属于第二个实参,逗号表达式,真实的参数是v4
v5属于第三个实参
v6属于第四个实参

⑪下标引用、函数调用和结构成员

1. [ ] 下标引用操作符

操作数:一个数组名 + 一个索引值

int main()
{
	int arr[10] = { 0 };
	int arr[3] = 4;//[]里面的值叫做索引值
	return 0;
}

2. ( ) 函数调用操作符

接受一个或者多个操作数:第一个操作数是函数名,剩余的操作数就是传递给函数的参数。
只要是函数,那么()函数调用操作符就不可省略

int main()
{
	test1(); //使用()作为函数调用操作符。
	test2("hello bit.");//实使用()作为函数调用操作符。
	return 0;
}

3.访问结构成员

.            结构体.成员名(非指针变量)
->          结构体指针->成员名(指针变量)

struct card
{
	char name[20];
	int age;
};

void Print(struct card* pa)
{
	printf("%s %d\n", pa->name, pa->age);//pa是指针变量,用->
}

int main()
{
	struct card a = { "zhangsan",18 };
	printf("%s %d\n", a.name, a.age);//a不是指针变量,用.

	Print(&a);
	return 0;
}

⑫表达式求值

既然操作符了解完了,那么就该带入表达式求解了

表达式求值的顺序一部分是由操作符的 优先级结合性 决定。
同样,有些表达式的操作数在求值的过程中可能需要转换为其他类型

1.隐式类型转换(char和short int)(选看)

C的整型算术运算总是至少以缺省整型类型的精度来进行的。

为了获得这个精度,表达式中的 字符短整型 操作数在使用之前被转换为普通整型,这种转换称为整型提升

整型提升的意义:
表达式的整型运算要在CPU的相应运算器件内执行,CPU内整型运算器(ALU)的操作数的字节长度一般就是int的字节长度,同时也是CPU的通用寄存器的长度。

因此,即使两个char类型的相加,在CPU执行时实际上也要先转换为CPU内整型操作数的标准长度
通用CPU(general-purpose CPU)是难以直接实现两个8比特字节直接相加运算(虽然机器指令中可能有这种字节相加指令)。所以,表达式中各种长度可能小于int长度的整型值,都必须先转换为int或unsigned int,然后才能送入CPU去执行运算

简单来说,就是 字符短整型 操作数 在需要 算术运算 或者需要 以%d形式打印 时,就要 整型提升

如何进行整体提升呢?
整形提升是按照变量的数据类型的符号位来提升的
无符号整形提升,高位补0

例如:

int main()
{
	char a = 5;
	//00000101
	char b = 126;
	//01111110
	char c = a + b;//a和b要进行算术运算,且都为char类型,因此要进行整型提升

	//a、b的符号位(即最高位)都为0,所以用0提升
	//00000000 00000000 00000000 00000101  -- a整型提升后
	//00000000 00000000 00000000 01111110  -- b整型提升后
	//00000000 00000000 00000000 10000011  -- a+b
	//10000011  -- c时char类型,所以只能放8个比特位

	printf("c=%d\n", c);
	//c要以%d形式打印,又是char类型,就要整型提升
	//c的符号位是1,所以用1提升
	//11111111 11111111 11111111 10000011  -- c整型提升后(补码)
	//10000000 00000000 00000000 01111101 = -125 -- c整型提升后(原码)

	return 0;
}
//c=-125
int main()
{
	char a = 0xb6;//1011 0110
	short b = 0xb600;//1011 0110 0000 0000
	int c = 0xb6000000;
	if (a == 0xb6)
		printf("a");
	if (b == 0xb600)
		printf("b");
	if (c == 0xb6000000)
		printf("c");
	return 0;
}
//c

答案: c
解析:
a,b,c都要进行算术运算,又a,b分别是char和short int,所以a,b要整型提升,但c不用
a,b整形提升之后,变成了负数(符号位是1,用1提升,再将补码变为原码时符号位不变),所以表达式 a == 0xb6 ,b == 0xb600 的结果是假,
但是c不发生整形提升,则表达式 c == 0xb6000000 的结果是真.

int main()
{
	char c = 1;
	printf("%u ", sizeof(c));
	printf("%u ", sizeof(+c));
	printf("%u ", sizeof(-c));
	return 0;
}
//1 4 4

解析:
c只要参与表达式运算,就会发生整形提升,表达式 +c ,发生提升,所以 sizeof(+c) 是4个字节.
表达式 -c 也会发生整形提升,所以 sizeof(-c) 是4个字节,但是 sizeof( c) ,就是1个字节

2.算术转换

如果某个操作符的各个操作数属于不同的类型,那么除非其中一个操作数的转换为另一个操作数的类型,否则操作就无法进行。下面的层次体系称为寻常算术转换

long double
double
float
unsigned long int
long int
unsigned int
int

如果转换,上表中的数据类型由下往上转换,
如:int+float,那么就将int类型转化为float类型,再相加

3.操作符的属性

复杂表达式的求值有三个影响的因素。

  1. 操作符的优先级
  2. 操作符的结合性
  3. 是否控制求值顺序。

两个相邻的操作符先执行哪个?取决于他们的优先级。如果两者的优先级相同,取决于他们的结合性。

操作符优先级:(由上往下,优先级降低)
控制求值顺序:
&&逻辑与    ||逻辑或
?:条件赋值符   , 逗号表达式

1.&&逻辑与:当&&左边的值为0时,表达式为假,不用再计算右边的值是否为0
2.| | 逻辑或:当 | | 左边的值为非0时,表达式为真,不用再计算右边的值是否为0
3.exp1?exp2:exp3 条件表达式:
(1)当exp1为真时,计算exp2的值,而exp3不用计算;
(2) 反之则计算exp3的值,而exp2不用计算;
4.逗号表达式:执行顺序只能是从左往右执行

在这里插入图片描述

我们已经了解了操作符的属性,但在一些情况下我们仍不能某些代码的执行顺序,因此,我们要尽量避免写成以下代码

    a*b + c*d + e*f

注释:代码在计算的时候,由于 * 比+的优先级高,只能保证 * 的计算是比+早,但是优先级并不能决定第三个*比第一个+早执行。所以可能出现以下两种循序
你或许会问,不管是哪种顺序,计算结果难道不是一样吗?
如果这些都是单个变量,你说得对
但如果它们是一些较长的表达式,那是不是结果就可能不一样了呢
在这里插入图片描述
在这里插入图片描述


好了,那么本篇博客就到此结束了,如果你觉得本篇博客对你有些帮助,可以给个大大的赞👍吗,感谢看到这里,我们下篇博客见❤️
  • 14
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 8
    评论
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值