C语言详解(四) - 操作符

码字不易,若有错误,欢迎指正!

1. 算术操作符

+-*/%
模(整数取余)

1.1 这里的加法减法乘法运算与数学中相同,但除则有些不同:

  1. 对于只涉及整数的除法是整数除法,其结果是两个整数的商,是一个整数。
#include <stdio.h>

int main(){
    int ret1 = 4 / 2;
    printf("%d\n", ret1);//%d是指以有符号整型的形式打印ret1
    
    int ret = 5 / 2;
    printf("%d\n", ret2);
    
    return 0;
}

运行结果:
.png

  1. 而只要涉及到浮点数的除法就是浮点数除法,结果是一个浮点数,或者说是一个有精度的小数,与数学中的除法基本相同。
#include <stdio.h>

int main(){
    double ret1 = 4.0 / 2;
    double ret2 = 5.0 / 2;
    double ret3 = 5 / 2.0;
    double ret4 = 5.0 / 2.0;
    printf("ret1 = %d\n",ret1);
    printf("ret2 = %d\n",ret2);
    printf("ret3 = %d\n",ret3);
    printf("ret4 = %d\n",ret4);
    
    return 0;
}

运行结果:
.png

1.2 模%

模操作符**只适用于整数间的运算,**结果是两个整数相除的余数并且是一个整数。

#include <stdio.h>

int main() {
    int ret1 = 5 % 2;
    int ret2 = 4 % 2;
    printf("ret1 = %d\n", ret1);
    printf("ret2 = %d\n", ret2);
    //字符是以整数(ASCII码值)在内存中进行储存的,也可以看做整数。
    char a = 10;
    char b = 3;
    int c = a % b;
    printf("%d\n", c);
    return 0;
}

运行结果:
.png


2. 移位操作符

2.1 类型

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

这里的位指的是整数在内存中储存的补码的二进制位

2.2 整数在内存中的储存 - 原码 - 反码 - 补码

2.2.1 正整数和0

符号位:正整数的最高位是符号位,默认是0。
原码:由相应类型的整数直接写出的二进制形式,二进制位不够则在高位补0
正整数和0的原码、反码、补码均相同,故其补码就是其原码。

如 int类型的整数10的二进制形式是1010,int在内存中假设占32个bit位,则补二进制位
00000000 00000000 00000000 00001010,得到int型整数10的原码,同时也是反码、补码。

2.2.2 负整数

符号位:负数的最高位是符号位,默认为1。
反码:符号位不变,其它位按位取反得到的二进制数。取反:0->1,1->0;
补码:反码的二进制数+1得到的二进制数。

-10为例
二进制形式:1010
原码:10000000 00000000 00000000 00001010
反码:11111111 11111111 11111111 11110101
补码:11111111 11111111 11111111 11110110

2.3 左移操作符 <<

左移操作符的操作数只能是整数。

规则:左边抛弃,右边补零
如:

#include<stdio.h>
int main(){
    int n = 5;
    int m = n << 1;
    printf("%d\n", n);
    printf("%d\n", m);
    
    return 0;
}

运行结果:
.png
操作符_左移.png

把n的二进制位左移1位的结果赋值给m,故m中存放的是n << 1的值,但左移操作符并不改变n的值。

左移操作符的一个特点:以n=1,n=5为例

n=1n<<1n<<2n<<3n<<4n<<5
2481632
n=5n<<1n<<2n<<3n<<4n<<5
10204080160

n相同的情况下,随着左移位数的增加,n与n<<位数成倍数关系。


2.4 右移操作符 >>

规则:

  • 算术右移

左边用原符号位填充,右边丢弃。

  • 逻辑右移

不考虑符号位,左边直接用0补充,右边丢弃。

一般情况下是算术右移,因为考虑了原符号位,更加合理。
这两种右移规则对于正整数和0而言均相同,应为他们的二进制位最高位为0,不管哪种右移左边均补0。
对于负数而言这两种右移规则才有区别:

#include <stdio.h>

int main(){
    int n = -1;
    int m = n >> 1;
    printf("n = %d\n", n);
    printf("m = %d\n", m);
    
    return 0;
}

分析:(以-1为例)
右移操作符.png

vs2019上的运行结果:为算数右移
.png
C语言的算术右移对于移动负数位的行为没有进行定义,是错误的。想实现效果合理使用左移右移就可以满足。


3. 位操作符

3.1 分类

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

注意:

  1. 其操作数只能是整数。
  2. 这里的位是二进制位

3.2 按位与 &

二进制对应的为相与,二者同时为1时这一位的结果才为1,否则为0。

#include <stdio.h>

int main(){
    int n = 1  & 5;
    printf("%d\n", n);
    
    return 0;
}

按位与.png
运行结果:
.png

3.3 按位或 |

二进制对应的位相或,二者都为0时这一位的结果才为0,否则为1。

#include <stdio.h>

int main(){
    int n = 1 | 5;
    printf("%d\n", n);
    
    return 0;
}

按位或.png
运行结果:
.png

3.4 按位异或 ^

二进制对应的位相异或,二者相异时为1,相同时为0。

#include <stdio.h>

int main(){
    int n = 1 ^ 5;
    printf("%d\n", n);
    
    return 0;
}

按位异或.png
运行结果:
.png

3.5 一些例子

3.5.1 交换两个整数

借助临时变量交换:

#include <stdio.h>

int main(){
    int a = 5;
    int b = 10;
    //借助临时变量t
    int t = 0;
    t = a;
    a = b;
    b = t;
    printf("a=%d,b=%d", a, b);
    
    return 0;
}

不借助临时变量交换:

思路1:借用其中一个整数(a)存放两个整数(a+b)的和,再借助另一个整数(b)完成交换。
缺点:当a与b没有超出整型的范围但a与b的和超出了整型的范围时结果会发生错误。

#include <stdio.h>

int main(){
    int a = 5;
    int b = 10;
    
    printf("a=%d,b=%d", a, b);
    //a中存放a+b的值
    a = a + b;
    //b中得到原来a的值
    b = a - b;
    //a中得到原来b的值
    a = a - b;
    printf("a=%d,b=%d", a, b);
    
    return 0;
}

思路2:借助按位异或操作符实现
一个知识点:异或满足交换律, 且有 a^b^a等价于aa^b^b等价于a,任何整数与0异或均是自身,

#include <stdio.h>

int main(){
    int a = 5;
    int b = 10;
    
    //a与b异或的结果存放在a中
    a = a ^ b;
    //相当于a^b^b,得到a,赋值给b
    b = a ^ b;
    //相当于a^b^a,得到b,赋值给a
    a = a ^ b;
    
    return 0;
}

运行结果:
AQVCW@X(0Q{@9AO(E6{Y79T.png

3.5.2 一个整数(假设是t)在内存中的二进制形式(补码)的1的个数

借助移位操作符<<和按位与操作符&
思路1:对于int型在内存中占4个字节或8个字节(这里以4个字节为例子),32个bit,
xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx
考虑32个位按位逐个检查,借助1,其在内存中的补码为00000000 00000000 00000000 00000001,1与一个整数按位与之后得到的结果的32个位只有最低位可能为1,其他位由于都是0,不管x是0还是1&后都是0。故可以通过判断t & 1的结果判断t二进制补码的最低位是否为1,若是0则最低位是0,若是非0则最低位为1。借助一个循环分别判断t的每一个二进制位即可。

#include <stdio.h>

int main() {
    int t1 = 10;
    int t2 = -10;

    int i = 0;
    int cnt1 = 0;
    int cnt2 = 0;
    for (i = 0; i < 32; i++) {
        if (t1 & (1 << i)) {
            cnt1++;
        }
        if (t2 & (1 << i)) {
            cnt2++;
        }
    }
    printf("cnt1 = %d, cnt2 = %d\n", cnt1, cnt2);
    return 0;
}

运行结果:
.png

#include <stdio.h>

int main(){
    int num = -1;
    int i = 0;
    int cnt = 0;//计数
    while(num){
        cnt++;
        num = num&(num-1);
    }
    printf("cnt = %d\n",cnt);
    return 0;
}

4. 赋值操作符

4.1 分类

=赋值
+=加等
-=减等
*=乘等
/=除等
%=模等
&=按位与等
|=按位或等
^=按位异或等
<<=左移等
>>=右移等

4.2 使用

一般赋值使用:

int a = 10;
double b = 3.14;

连续赋值:

int a,b,c;
a = b = c = 10;
//等价于
c = 10;
b = c;
a = b;

复合赋值操作符:

int a,b;
a = 5;
b = 10;
a += b;
//等价于a = a + b
a &= b;
//等价于a = a ^ b

5. 单目操作符 - 操作数只有一个

5.1 分类

+正值
-负值
++自增(有前缀和后缀两种形式)
自减(有前缀和后缀两种形式)
逻辑取反
~按位取反(二进制形式)
&取地址
*解引用(间接访问)
(类型)强制类型转换
sizeof操作数的类型长度(单位是字节)

5.2 +/-

+:是一个操作符,但很少使用,对于整数不需要,对于负数不起作用。
-:把一个正数变为负数,把一个负数变为正数。

int a = 10;
a = +a;//a == 10
a = -a;//a == -10
int b;
b = -a;//b == 10

.png

5.3 ++/–

++:

int a = 10;
a++;
--a;
//等价于a = a + 1;
//等价于a += 1;

前缀形式++a符合先自增再使用,a先增加1,接着再使用a,用的是a自增后的值。
后缀形式a++符合先使用再自增,a先使用,接着再自增a,用的是a自增前的值。

–:同++。

5.4 ! 逻辑取反

C语言在判断真假时,以0表示假,非0表示真。
对0进行逻辑取反!0结果是真(非0)。
对非零值进行逻辑取反如!10结果是假(0)。

#include <stdio.h>

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

运行结果:
N[S_J{1NGH)0)T_]8US3BXW.png

5.5 ~ 按位取反

按一个整数的二进制补码形式取反,0变1,1变0。

int a = 10;
int b = ~a;
printf("b = %d\n", b);

00000000 00000000 00000000 0000101010的二进制补码
11111111 11111111 11111111 11110101~10
11111111 11111111 11111111 11110100~10的反码
10000000 00000000 00000000 00001011~10的原码

运行结果:
.png

5.6 & 取地址操作符

取出操作数在内存中的地址。
操作数可以是变量,字符串常量,函数,数组,结构体等。

#include <stdio.h>

int main(){
    int a = 10;
    //创建一个指针变量p存放变量a的地址
    int* p = &a;
    
    printf("%p\n", &a);
    printf("%p\n", p);
    return 0;
}

运行结果:
.png

5.7 * 间接访问操作符

  • 对于一个具有具体指向的指针变量p(假设其指向整型变量a)来说,已经知道p中存放的是变量a的地址,可以通过间接访问操作符*得到变量a的值,获得使用和改变变量a的值的权限(如果对p没有主动做出限制的话)。
  • 与指针紧密相关。
#include <stdio.h>

int main(){
    int a = 10;
    int p = &a;
    printf("*p = %d\n", *p);
    *p = -10;
    printf("%d\n", *p);
    
    return 0;
}

运行结果:
.png

5.8 sizeof

计算的是操作数类型内存中所占字节的大小,以字节为单位。

注意:类型本身并不占内存的大小,即不在内存中开辟空间。但当创建了某种类型的变量时就会在内存中开辟该类型相应大小的空间。

5.8.1 一般使用

#include <stdio.h>
int main(){
    int a = 10;
    printf("sizeof(a) = %d", sizeof(a));
    printf("sizeof(int) = %d", sizeof(int));
    //sizeof操作符的括号可以省略,但在计算类型时是不能省略的
    printf("sizeof(a) = %d", sizeof a);
    //printf("sizeof int = %d", sizeof int);此为错误用法
    
    return 0;
}

运行结果:
.png

5.8.2 与数组的联系

在数组进阶中已经提到过,在回顾一下。
数组名一般情况下表示数组首元素的地址,但有两个例外。

  • 数组名单独遇到sizeof时计算的是整个数组的大小。
  • 数组名遇到&时,取出的是整个数组的地址。
#include <stdio.h>

void test(char s[]);

int main(){
    char str[10] = {0};
    //这里计算的是整个字符数组的大小
    printf("sizeof(str) = %d\n", sizeof(str));
    test(str);
    
    return 0;
}
//这是个测试
//等价于
//void test (char* s)
void test(char s[]){
    //这里计算的是一个字符指针的大小。
    printf("sizeof(s) = %d\n", sizeof(s));
}

一个指针的大小可能是4字节或8字节。

运行结果:
W_}SKB572G7AQ_E})C7J7.png

5.8.3 sizeof与strlen的区别与联系

  • 两者都可以计算大小
  • sizeof计算的是实际占内存中的储存单元的大小。
  • sizeof是操作符,strlen()是C语言的(头文件string.h)库函数。
  • strlen()函数计算的是字符串的长度,不包括字符串末尾的字符'0'
  • sizeof的括号可以去掉,但strlen()的括号不能去掉,这是strlen()函数的一个特征。

一个例子:

#include <stdio.h>
#include <string.h>

int main(){
    char str[20] = "Hello,World!";
    printf("sizeof(str) = %d\n", sizeof(str));
    printf("strlen(str) = %d\n", strlen(str));
    
    return 0;
}

5.9 () 强制类型转换

把操作数强制转换为想要的类型。若不使用强制类型转化可能会产生警告。

#include <stdio.h>

int main(){
    //这种情况会产生警告信息,3.14被默认为浮点型数据,被赋值给int型变量a时会丢失精度,是隐式类	  //型转换
    int a = 3.14;
    printf("a = %d\n", a);
    //强制类型转化,明确把浮点型数据转换为int型数据
    int b = (int)3.14;
    printf("b = %d\n", b);
    
    return 0;
}

M4GGSXDEW%}%@7)_4QMM2IF.png


6. 关系操作符

6.1 分类

>大于
>=大于等于
<小于
<=小于等于
!=不等于
==等于

6.2 注意事项

  • 等于是两个等号,赋值是一个等号,非常容易在这里出错。
  • 两个浮点型数据比较是否相等时不能直接比较,因为浮点数在内存中不能精确储存,总要损失一些精度。需要比较连个浮点数的差值的绝对值是否近似为0。即与一个非常小(如1e-5)的数相比较。
#include <stdio.h>

int main(){
    double a = 3.14;
    float b = 3.14;
    //1e-5 == 0.00001
    if(fabs(a-b) < 1e-5){
        printf("a==b\n");
    }
    else if(a-b > 1e-5){
        printf("a > b\n");
    }
    else{
        printf("a < b\n");
    }
    return 0;
}

运行结果:
_HDBM{​{)Y6WBRER20~(FSPO.png

7. 逻辑操作符

注意与按位与&按位或 |的区别:

  • &|是对二进制位进行操作。
  • &&||关注的是值的真和假,不是二进制的位。

7.1 分类

&&逻辑与
||逻辑或

7.2 使用

&&||是双目操作符,有两个操作数。

对于&&,当两个操作数都为真时,表达式的结果才为真(1),其他情况均为假(0)。
对于||,当两个操作数都为假时,表达式的结果才为假(0),其他情况均为真(0)。
这里的操作数可以使复杂的表达式。

例子:判断闰年

#include <stdio.h>

int main(){
    int year = 0;
    scanf("%d", &year);
    if(year%4 == 0 && year%100 != 0 || year%400 == 0){
        printf("isleap\n");
    }
    else{
        printf("is not leap");
    }
    return 0;
}

7.3 短路现象

a与b均表示表达式。

  • 对于&&,如a&&b,当子表达式a为假(0)时,整个表达式的值就是0,后面的子表达式b不在计算,子表达式b相当于被短路了。
  • 对于||,如a||b,当子表达式a为真(非0)时,整个表达式的值就是1,后面的子表达式b不再计算,子表达式b相当于被短路了。

8. 条件操作符

格式:表达式1 ? 表达式2 : 表达式3
用法类似于ifelse语句,表达式1为真时整个表达式的值是表达式2的值,表达式1为假时整个表达式的值是表达式3的值。
一个例子:

#include <stdio.h>

int main() {
    int a, b;
    scanf("%d%d", &a, &b);
    int max = a > b ? a : b;
    printf("max = %d", max);

    return 0;
}

运行结果:
QCFJ[_EMCPJ}@17]S~T03ZY.png


9. 逗号表达式

格式:表达式1,表达式2,表达式3,...,表达式n;

逗号表达式的最终的值是表达式n的值,表达式n之前的表达式可能会影响表达式n的值,也就是影响逗号表达式的值。

一个例子:

#include <stdio.h>

int main() {
    int a, b, c, d;
    int t1 = (a = 5, b = 3, c = 1, d = 10);
    int t2 = (a = 5, b = 3, c = 1, d = a + b + c);

    printf("t1 = %d\n", t1);
    printf("t2 = %d\n", t2);
    return 0;
}

运行结果:
.png


10. 下标引用操作符

格式 数组名[索引值],操作数有两个:数组名与索引值(或者说下标)。索引值范围0~数组长度-1。

int arr[10] = {1,2,3,4,5,6,7,8,9,10};
int a = arr[3];//3[arr]与arr[3]等价

访问时,这里的arr[3]3[arr]等价,下标引用操作符是二元操作符,两个操作数可以互换,类比其他二元操作符。但类似3[arr]不好理解。
arr[3]可以写为*(arr+3)或*(3+arr)
数组定义时则不行


11. 函数调用操作符

格式 函数名(可能有的一个或多个函数参数)

函数调用操作符的操作数至少有一个函数名,函数参数可能有一个或多个也可能没有函数参数。

一个例子:

#include <stdio.h>

int test1(int a, int b);
void test2();

int main(){
    int a,b;
    scanf("%d%d", &a, &b);
    test1(a, b);
    test2();
    
    return 0;
}
//测试1
int test1(int a, int b){
    return a > b ? a : b;
}
//测试2
void test2(){
    printf("test2\n");
    return;
}

运行结果:
(47CXX%P[]98K67{S~9K8KA.png


12. 结构成员访问操作符

12.1 分类

.圆点访问操作符(结构体)
->箭头访问操作符(结构指针)

12.2 使用

#include <stdio.h>

//定义一个学生的结构体类型并用typedef重命名为STU
typedef struct student{
    char name[20];
    char id[15];//学号
    int score;
}STU;

int main() {
    STU stu;
    scanf("%s %s %d", stu.name, stu.id, &stu.score);
    printf("%s %s %d\n", stu.name, stu.id, stu.score);

    STU* p = &stu;
    scanf("%s %s %d", stu.name, stu.id, &stu.score);
    printf("%s %s %d\n", p->name, p->id, p->score);
    return 0;
}

13. 表达式求值

13.1 隐式类型转换

13.1.1 整型提升

C的整型算术运算总是至少以缺省(或者说是默认)整型类型的精度进行的。

13.1.2 意义:

  • 表达式的整型运算要在CPU的相应运算器件内运行,CPU内整型运算器(AUL)的操作数的字节长度一般就是int的字节长度,同时也是CPU的通用寄存器的长度。所以对于运算中低于CPU内整型操作数的标准长度的情况,要先转换为CPU内整型操作数的标准长度在进行计算。
  • 通用CPU难以实现两个8比特字节直接相加计算,所以表达式中各种长度的可能小于int长度的整型值,必须先转换为int或unsigned int,然后再送入CPU进行运算。

13.1.3 整型提升要点

按照变量的数据类型的符号位进行提升的。

对于无符号整数高位直接补0
对于有符号整数

正数:char a = 1;
1的二进制补码为00000001
整型提升(高位补0)之后00000000 00000000 00000000 00000001
负数:char b = -1;
-1的二进制补码为11111111
整型提升(高位补1)之后11111111 11111111 11111111 11111111

13.1.4 例子

#include <stdio.h>

int main(){
    //char、short、int默认为有符号类型,若整型提升则进行的是有符号整数的整形提升
    char a = 0xf6;
    //11110110
    short b = 0xf600;
    //11110110 00000000
    int c = 0xf6000000;
    //11110110 00000000 00000000 00000000
    
    //参与运算之前先对char、short进行整形提升为int
    if(a == 0xf6){
    //11111111 11111111 11111111 11110110
        printf("a == 0xf6\n");
    }
    if(b == 0xf600){
    //11111111 11111111 11110110 00000000
        printf("b == 0xf600\n");
    }
    if(c == 0xf6000000){
    //11110110 00000000 00000000 00000000
        printf("c == 0xf6000000\n");
    }
    
    return 0;
}

运行结果:
.png

#include <stdio.h>

int main(){
    char a = 1;
    printf("sizeof(a) = %u\n", sizeof(a));
    //+a是一个表达式,sizeof计算的是a整形提升之后的大小,但+a、-a是实际上没有进行计算的。
    printf("sizeof(+a) = %u\n", sizeof(+a));
    //-a是一个表达式,sizeof计算的是a整形提升之后的大小
    printf("sizeof(-a) = %u\n", sizeof(-a));
    
    return 0;
}

.png

13.2 算数转换

同一个操作符的操作数类型不同时便无法继续进行运算,必须要转换为同一种类型才能继续进行算术运算。

13.2.1 寻常算术转换

long double多精度浮点类型(长精度浮点类型)
double双精度浮点型
float单精度浮点型
unsigned long int无符号长整型
long int长整型
unsigned int无符号整型
int有符号整型

若运算中出现多种类型的数据,优先转换顺序为:int ------>long double
如果不遵守优先转换顺序可能会造成数据精度丢失。

13.3 操作符的属性

13.3.1 优先级与结合性

  • 优先级
  • 结合性 - 左结合性与右结合性
  • 是否控制求值顺序

与表达式的属性相区别:值属性与类型属性。

13.3.2 解释

两个相邻的操作符,先看优先级的高低,优先级高的先进行运算;
若优先级相同,则看结合性,按结合性规定的顺序进行运算。

注意:

  • 优先级与结合性管的是两个相邻的操作符
  • 间隔开的操作符之间不受优先级与结合性的限制。

例如:int a = 1*2 + 3*4 + 5*6;

有优先级知道:
第一个号比第一个+号先进行运算;
第二个
号比第一个+号和第二个+号先进行运算;
第三个号比第二个+号先进行运算;
但:
不知道第一个
号是否比第二个+号运算的早;
不知道第三个*号是否比第一个+号运算的早。

一个有歧义的表达式:
int a = b + --b;

知道--操作符比+操作符先进行运算;

不知道b--b两个操作数谁先进行。

闭坑指南:我们写表达式时要确定表达式有唯一确定的值,如果不确定就需要重新写或对表达式进行合理拆分,使其成为几个简单的表达式。

13.3.3 操作符表格汇总

操作符描述用法结果类型结合性是否控制求值顺序
()括号(表达式)与表达式相同
()函数调用函数名(函数参数)函数返回类型L
[ ]下标引用数组名[下标]数组元素类型L
.结构成员访问结构名.结构成员结构成员类型L
->(结构指针)箭头访问结构指针->结构成员结构成员类型L
++后缀自增a++自身类型L
后缀自减a++自身类型L
!逻辑反!aR
~按位取反~aR
+正值+a自身类型R
-负值-a自身类型R
++前缀自增++a自身类型R
前缀自减–a自身类型R
*间接访问*a指针所指向的类型R
&取地址&a指针类型R
sizeof计算长度,单位字节sizeof(a)整型R
(类型)强制类型转换(类型)a强制转换的类型R
*乘法a*bL
/除法a/bL
%a%b整型L
+加法a+bL
-减法a-bL
<<左移a<<1整型L
>>右移a>>1整型L
>大于a>b整型L
>=大于等于a>=b整型L
<小于a<b整型L
<=小于等于a<=b整型L
==等于a==b整型L
!=不等于a!=b整型L
&按位与a & b整型L
^按位异或a ^ b整型L
|按位或a | b整型L
&&逻辑与a && b整型L
||逻辑或a || b整型L
? :条件操作符a ? b : c
=赋值a = 1R
+=加等a += 1R
-+减等a -= 1R
*=乘等*a = 1R
/=除等a /= 1R
%=模等a %= 2整型R
<<=左移等a <<= 1整型R
>>=右移等a >>= 1整型R
&=按位与等a &= 1整型R
^=按位异或等a ^ 1整型R
|=按位或等a |= 1整型R
,逗号a,b,c,…,nL

END

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

re怠惰的未禾

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值