1.0、C语言——初识C语言

目录

1.0、C语言——初识C语言

简介:

第一个C语言程序:

数据类型:

变量、常量:

变量的分类:

变量的 作用域 和 生命周期 :     

IO输入输出语句:

常量:

宏:

字符串定义:

函数的定义:

库函数 - strlen():

操作符 - sizeof():

分支、循环语句:

操作符:

原码、反码、补码:

常见关键字(自定义变量不能与关键字重名):

extern:(在main函数中表示引入外部符号,定义的全局变量只要用extern关键字引入都可以使用)

register(寄存器关键字):

typedef:

static:(static关键字可以修饰 局部变量、全局变量、函数方法)

内存地址:

指针:

结构体:


1.0、C语言——初识C语言

简介:

        C语言是一门面向过程计算机编程语言,与C++、C#、Java面向对象编程语言有所不同。C语言的设计目标是提供一种能以简易的方式编译、处理低级存储器、仅产生少量的机器码以及不需要任何运行环境支持便能运行的编程语言。C语言描述问题比汇编语言迅速、工作量小、可读性好、易于调试、修改和移植,而代码质量汇编语言相当。C语言一般只比汇编语言代码生成的目标程序效率低10%-20%。因此,C语言可以编写系统软件。 [2] 

        当前阶段,在编程领域中,C语言的运用非常之多,它兼顾了高级语言和汇编语言的优点,相较于其它编程语言具有较大优势。计算机系统设计以及应用程序编写是C语言应用的两大领域。同时,C语言的普适较强,在许多计算机操作系统中都能够得到适用,且效率显著。 [3] 

C语言拥有经过了漫长发展历史的完整的理论体系,在编程语言中具有举足轻重的地位。

第一个C语言程序:

#include<stdio.h>
int main() {
	printf("Hello World~");
}

        当我们输出的时候可能会出现控制台一闪而过就关闭的情况,那我们按照以下方法设置即可解决问题,如下图所示:

数据类型:

        char              // 在 Java 中 char 占2个字节,在 C 中 char 只占1个字节
        short             // 短整型,占2字节
        int                 // 整型,占4字节
        long              // 长整型,占4/8字节
        long long      // 更长的整型,占8字节
        float              // 单精度浮点数,占4字节
        double          // 双精度浮点数,占8字节

变量、常量:

        生活中的有些值是不变的(比如:圆周率、性别、身份证号码、血型等等)

        有些值是可变的(比如:年龄、体重、薪资)

        不变的量,C语言中用 常量 的概念来表示,变的值 C语言 中用 变量 来表示

变量的分类:

        局部变量

        全局变量

当局部变量和全局变量同名的时候,优先使用局部变量

变量的 作用域 和 生命周期 :     

作用域:

        作用域(scope),程序设计概念,通常来说,一段程序代码中所用到的名字并不总是有效/可用的

        而限制这个名字的可用性的代码范围就是这个名字的作用域

1、局部变量的作用域是遍历所在的局部范围

2、全局变量的作用域是整个工程

生命周期:

变量的生命周期指的是变量的创建到变量的销毁之间的一个时间段

1、局部变量的生命周期是:进入作用域生命周期开始,出作用域生命周期结束

2、全局变量的生命周期是:整个程序的生命周期

IO输入输出语句:

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>

int main() {
	int a = 0;
	scanf("%d",&a);
	printf("a = %d",a);
	return 0;
}

这里可以看到在代码的第一行有这样一段代码:#define _CRT_SECURE_NO_WARNINGS 1

如果不加的话会报这样一个错误,如下图所示:

        咱们的 vs编译器人为 scanf 不安全,所以他建议你用 scanf_s ,但是我建议不要使用,因为scanf是c#提供的,而 scanf_s 是 vs编译器 提供的,如果你用了 scanf_s编译器 那我们如果将代码放到另一个编译器上,比如说gcc上的时候会无法识别导致编译失败,那么我们的代码就变得不具有跨平台性 / 可移植性了。

        所以我建议大家 在代码的第一行加上 #define _CRT_SECURE_NO_WARNINGS 1 这么一句话就可以无视这个警告了

那么每次写代码都要加这一行代码太麻烦了,这里告诉大家一个一劳永逸的方法,如下:

        我用的是 vs2022版本(其他版本的文件夹路径可能有些不一样~)

        找到 VCProjectItems文件夹,并且打开newc++file.cpp文件,加上#define _CRT_SECURE_NO_WARNINGS 1 这句话,保存关闭即可,

        但是这个文件需要管理员权限才可以修改,大家如果觉得麻烦就先在桌面创建一个记事本文件修改其内容也就是加上那句话保存关闭,然后再把文件命名改成newc++file.cpp再放回VCProjectItems文件夹替换掉原来的即可。

常量:

        在C中常量用const关键字修饰,常量的本质还是变量,只不过是一个拥有常属性的变量;对比学习Java用的是 final,常量只能赋值一次不可更改。

        在C中数组定义的长度只能是常量不能是变量,这是因为C语言在运行之前需要向系统申请分配空间,分配好之后不可更改所以数组的长度不能是变量(当然在C#中有一个malloc()函数,这里先不做介绍,他可以做到动态分配内存)

        而在Java中有new关键字,用new来申请分配空间,是动态的分配空间所以在Java中数组的长度可以用变量来定义

枚举常量:
enum关键字

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>

enum Color {
	RED,
	YELLOW,
	BLUE
};

int main() {
	enum Color color = RED;
	printf("%d", color);
	return 0;
}

输出的是 0 ,在enum中他们的常量值分别是:RED是0,YELLOW是1,BLUE是2
 

还有一种定义常量的方式 -> 用 #define -> 定义的 标识符常量,如下所示:

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>

#define MAX 100
int main() {
	int max = MAX;
	printf("max = %d",max);
}

宏:

        这里顺便再补充一种用 #define 定义 的用法:
宏的使用方式和函数定义很像,但是 宏 的定义方式更加简洁方便,代码如下所示:

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>

#define MAX(a,b) (a>b?a:b)
int main() {
	int max = MAX(6, 9);
	printf("Max = %d", max);
}

字符串定义:

        在C#语言中没有 String 数据类型,但是我们可以用数组来代替字符串数据类型:

int main() {
	char arr[] = { "abcdefghijk" };
	return 0;
}

        如果我们的字符数组中像下面这样定义的话,需要在数组的末尾加上 '\0' 表示结束,否则C#没有识别到结束符会一直往下搜寻 会乱码 数组长度也会异常 (当然这个'\0' 不占数组长度)

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>

int main() {
	char arr[] = {'a','b','c','d','\0'};
	printf("%s",arr);
	return 0;
}

函数的定义:

        C#语言函数的定义方式和Java差不多,只不过由于C#是面向过程编程所以不存在public、private这样的修饰关键字

int max(int a, int b) {
	return a > b ? a : b;
};

int main() {
	max(1,2);
	return 0;
}

然后函数调用的时候也不需要 new 一个对象来调用,直接调用就可以了

库函数 - strlen():

strlen() 计算字符串的长度,长度为11:

int main() {
	char arr[] = { "abcdefghijk" };
	int length = strlen(arr);
	printf("%d",length);
	return 0;
}

操作符 - sizeof()

sizeof() 用来计算占用内存大小:

int main() {
	char arr[] = { "abcdefghijk" };
	int size = sizeof(arr);
	printf("%d", size);
	return 0;
}

这里因为字符串末尾会以 \0 转移字符来结束,所以计算字符串存储大小的时候是 12 而不是 11
 

分支、循环语句:

if、else分支语句、while循环语句和Java相同就不多说了,直接上代码:

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <stdbool.h>

int main() {
	int num = 0;
	while (true) {
		if (num>10) {
			break;
		}
		else {
			printf("while循环执行到第 %d 遍了\n", num);
			num++;
		}
	}
	return 0;
}

操作符:

操作符的运用,这里的知识和Java中差不多也不做过多的介绍了,直接上运算符:

运算操作符:
        +   -    *    /    %

移位操作符:
        >>        <<

位操作符:
        &      |      ^

赋值操作符:
        =      +=      -=      &=      /=      &=      |=      ^=      >>=      <<= 

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

双目操作符:
        不过多解释了就是操作符两边都有操作数的操作符比如说+加号 -减号等等......

三目操作符:
        表达式1 ? 表达式2 :表达式3           (如果表达式1为真,则整个表达式的结果为表达式2,如果表示式为假则整个表达式的结果为表达式3)

逗号表达式:
        exp1,exp2,exp3,......expN (这里只需要了解用逗号也可以隔开表达式就可以了)

下标引用、函数调用和结构成员:
[]      ()      .      ->

原码、反码、补码:

正整数的 原、反、补都是一样的,

负数的原码符号位不变其他位取反得到反码,反码再加1得到补码

在我们的计算机中存储的都是 补码 ,虽然说正数的补码就是本身,但是我们就是统一这个说法嘛~都是补码

我们规定有符号数 的第一位为符号位,负号为1 , 正号为0,该符号为也占一个 bit

比如说 int 类型 4byte——32bit 那么符号位占第一位,剩下的31位为数值大小

常见关键字(自定义变量不能与关键字重名):

auto:
        auto关键字指的是自动的意思,我们的局部变量会在花括号开始时候被自动创建花括号结束后会被自动销毁,那么其实局部变量就是自动变量,在定义局部变量时 C#默认在前面加上了一个auto ,比如 auto int a = 0; 但是这个 auto 被省略了可以不写,默认局部变量就是自动变量

break:
        在循环中会用到来结束循环,或者 switch case 中也会用到拿来结束

const:
        用来修饰常变量

continue:
       
在循环中表示继续

default:
        默认

extern:(在main函数中表示引入外部符号,定义的全局变量只要用extern关键字引入都可以使用)

首先在 test.c 中定义一个变量testData = 2022:

 然后在 test2.c 中用 extern 关键字引入,如下:

 运行完全可以输出 testData = 2022

register(寄存器关键字):

先来给大家介绍一下什么是寄存器,存储数据的地方有 硬盘、内存、高速缓存、寄存器,那么如下图所示 -> 越往上走 存储器 的 访问速度就越快、造价就越高所以内存空间也就越小,不然一台电脑得多少钱啊~

        以前的计算机存储数据 有 内存 和 硬盘,这时 内存 和 cpu 处理的的速度还是比较搭配的,内存能拿多快我 cpu 就能处理的多快,但是经过时间的推移计算机的发展 cpu 的处理速度变得越来越快,但是内存的访问速度却跟不上,这个时候就算CPU的性能再高也无法提高计算机的速度,所以出现了 高速缓存 和 寄存器。

        先让内存 的数据加载到高速缓存,然后高速缓存的数据再加载到寄存器中去,然后cpu再去从寄存器中拿取数据,当cpu从寄存器中拿不到数据的时候再去依次逐层往下到高速缓存、内存中去拿取,但是每次拿取数据的首选都是寄存器 

那么在代码中如何去使用这个关键字呢?->  如下所示:

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>

int main() {
	register int a = 10;
	return 0;
}

        比如说我觉得这个 变量a 的使用频率会很高每次都要去内存中拿太慢了,我们只要在定义变量的前面加上 register关键字 ,就会将该变量放到寄存器之中(当然这也只是建议 编译器 将该变量放到寄存器中去,因为如果定义了很多个这样的变量不可能所有的变量都能放到寄存器中,所以最后还是由编译器去决定到底将哪个变量放到寄存器中去)

signed:        
        比如我们声明一个变量 int a = 10;那么在 int 前面默认是添加了一个 signed 关键字的,代表他是有符号数,那么与之对应的还有一个关键字 ->
unsigned:
        如果我们在声明变量前面加上一个 unsigned关键字,如 unsigned int a = -10; 那么代表这是一个无符号数字,不管是正数还是负数,在 C# 看来都是正数。

struct:
        结构体关键字

typedef:

顾名思义就是类型定义,这里应该理解为类型重命名,代码如下所示:

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>

int main() {
	unsigned int a = 10;
	typedef unsigned int un_int;
	un_int b = 10;
	printf("a = %d",b);
	return 0;
}

        以上代码我们先定义了一个 a ,但是发现这个 unsigned int 定义很麻烦,所以用 typedef 关键字将 unsigned int 重命名为 un_int ,然后用un_int 又去定义了变量 b,通过测试可以知道 变量a 和 变量b 的类型是相同的。

        当然 typedef关键字 不止这点应用场景,后面再详细介绍......

union:
        联合体/共用体    

volatile:
        这是一个体现我们C语言段位的关键字,哈哈~

static:(static关键字可以修饰 局部变量、全局变量、函数方法)

先来说说static修饰的局部变量

        没有 static 修饰的局部变量生命周期 -> 在花括号开始时被创建 -> 当执行到花括号结束时局部变量被销毁~

        但是当我们用static关键字修饰局部变量之后的生命周期 -> 变长了 -> 不再是到达花括号结束就被销毁了,而是程序结束的时候才会被销毁

当static修饰的全局变量

        如果一个全局变量没有用 static关键字 修饰的话,只要用 extern 声明之后便可以使用,

        但是如果我们在全局变量前用 static关键字 修饰的话,那么无论用不用 extern 声明,该变量只能在自己所在的源文件内部中使用,其他源文件无法使用该变量,也就是说 static 改变了全局变量的作用域

​​​​​​当static修饰的函数

        如果一个函数没有用 static关键字 修饰的话,只要用 extern 声明之后便可以使用,

        但是如果我们在函数前用 static关键字 修饰的话,那么无论用不用 extern 声明,该函数只能在自己所在的源文件内部中使用,其他源文件无法使用该函数 -> 也就是说 static 改变了函数的作用域(但是这个说法不太准确,准确的来说是改变了函数的链接属性,一个函数正常来说是有外部链接属性的,当被 static 修饰之后外部就看不到他了,此时他的外部链接属性 -> 就变成了 -> 内部链接属性)

内存地址:

        内存地址 和我们生活中的 住宅地址 差的不多,都是用一个名称来描述,比如说一栋楼被划分为了多个房门 -> 每个房门都有一个房门号就是可以当成我们所说的地址了,当然在计算机中内存的地址编号是用二进制数来表示的        

        我们买电脑的时候会看到有32位和64位的,就先来说说32位吧,这个32位指的是有32根地址线或者说数据线,那么这32根线都会通电,电又会有正负之分,那么我们用 1 表示正电、0 表示负电,这时候电信号就转换为了数字信号,我们知道32位 可以表示 2^32 个二进制数,所以就可以形成 2^32 个内存地址

        那大家觉得一个内存空间多大合适呢?内存单位有 bit、byte、kb、mb、gb、tb、pb,那么假设一个内存空间表示 1 bit,那么 2^32 个内存空间就是 2^32 bit  转换成gb就是 ->  0.5gb,显然0.5gb 太小了根本不够用,那再来假设 如果每块内存空间是 1byte 也就是1 字节的话呢?那么 2^32 块空间就是 2^32 byte 转换成gb就是 -> 4gb,嗯~好像挺合适了这个大小,所以说啊在计算机中每块内存的大小是 1byte 也就是1字节

        那么在代码中 比如说我们声明了一个变量 那么这时候就会向计算机声明一块相应的地址空间给该变量~

//那么由于 int 数据类型占 4 个字节,所以会向计算机申请 4 块内存空间

int a = 10;

 所以 int a = 10; 由于int 数据类型占 4个字节,所以他会向计算机申请 4 块内存空间 

指针:

        在C语言中有一个操作符叫做 取地址操作符 & ,代码如下:

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>

int main() {
	int a = 10;
	printf("&a = %p",&a);
}

        地址能不能存放起来呢?当然可以只要是个值都可以存放,那么由此引出存放地址的数据类型 -> 指针变量 他就是专门拿来存放地址的,代码如下:

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>

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

可以发现两次输出的结果是一样的~

        那要如何通过 p 来找到该地址中存放的东西呢?只需要在 p 前面加上 解引用操作符 * 变成 *p 即可,代码如下:

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>

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

运行后可以发现两次输出 第一次是 10 ,第二次是 20,也就是说我们通过 *p 找到了a 并且将a的值改成了 20(这里注意区分一下 -> 上述代码中:指针的类型是 int* ,指针变量名是 p,而 *p 是解引用操作符/间接访问操作符,这三个是不同的东西,不要弄混了~)

注意:这里的指针变量类型会根据存放地址的数据类型改变而改变并不都是 int*

        比如说 char ch = 'A';  那么存放 ch 的 指针变量 类型为 char* pc = &ch;

最后补充一点 -> 关于指针变量会分配多少内存空间呢?

        其实这个和操作系统有关,如果是 32位 的操作系统,那么每个地址就是一个32位的二进制数,32bit 就是 4byte,所以说在32位的操作系统中 每个指针变量 所分配的内存空间大小为 4字节,同理在 64位 操作系统中指针变量所占的内存空间大小就是 8字节

我用的是 vs2022 编译器可以通过配置管理器来切换 32位 和 64位,大家也可以用代码实测一下~

结构体:

        我们在描述一个复杂对象的时候,比如说一个人,一本书,一台计算机......这些对象都不能简单的用一两个变量就能描述出来,因为本书有很多特征 -> 书名、简介、作者、书号 等等

        那这个时候我们可以用 struct 关键字来定义一个类型名为 Book 的结构体,代码如下所示:

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>

struct Book {
	//书名
	char bookName[16];
	//简介
	char introduction[100];
	//作者
	char author[20];
	//书号
	short bookNum;
};
int main() {
	struct Book book1 = {"C语言程序设计","C语言从入门到精通","小澜",11011};
	printf("书名 = %s\n",book1.bookName);
	printf("简介 = %s\n", book1.introduction);
	printf("作者 = %s\n", book1.author);
	printf("书号 = %d\n", book1.bookNum);
	return 0;
}

        我们首先用 struct关键字 创建了一个Book的结构类型
        然后在Book中定义了四个和书有关的属性 -> 书名、简介、作者、书号
        接下来在 main 主函数中利用 struct关键字 以及 结构体类型-创建出一个该类型的 结构体变量book1,并且用 { } 花括号将 Book 里的每个属性赋值
        最后再用 . 操作符 和 变量book1 依次将 book1 这本书的信息打印出来~

这里类比Java的话 -> struct 其实和 Java 中的类class有些相似,但是呢又有些不同~

        如果要修改结构体属性值的话 直接 book1.xxxx = xxxx; 就可以了;
        那么【注意】:如果我们要修改的结构体属性的数据类型是 字符串 那么不能直接 book1.bookName = "xxxxxx"这样去修改,因为bookName是一个数组名,而数组名的本质其实也是地址所以不能直接更改他的内容。
        这里要用到一个库函数strcpy(全称 string copy) - 字符串拷贝的意思,那么要用到这个库函数的话先得引入 string.h 头文件,使用方法如下所示:

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <string.h>
struct Book {
	//书名
	char bookName[16];
	//简介
	char introduction[100];
	//作者
	char author[20];
	//书号
	short bookNum;
};

int main() {
	struct Book book1 = {"C语言程序设计","C语言从入门到精通","小澜",11011};
	struct Book* pb = &book1;
	strcpy(book1.bookName, "修改后的书名");
	return 0;
}

库函数 strcpy(位置,内容) 中有两个参数 -> 第一个是拷贝的位置,第二个是拷贝的内容

那么像这种自定义类型该如何去定义该类型的指针呢?

        其实也是一样的,代码如下所示:

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>

struct Book {
	//书名
	char bookName[16];
	//简介
	char introduction[100];
	//作者
	char author[20];
	//书号
	short bookNum;
};

int main() {
	struct Book book1 = {"C语言程序设计","C语言从入门到精通","小澜",11011};
	struct Book* pb = &book1;
	return 0;
}

如上所示:我们用 struct Book* pb = &book1; 创建出了Book类型的指针,并且存储了 book1 的地址

那么利用 指针 也可以将结构体的属性信息打印出来,代码如下所示:

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>

struct Book {
	//书名
	char bookName[16];
	//简介
	char introduction[100];
	//作者
	char author[20];
	//书号
	short bookNum;
};

int main() {
	struct Book book1 = {"C语言程序设计","C语言从入门到精通","小澜",11011};
	struct Book* pb = &book1;
	printf("书名 = %s\n", (*pb).bookName);
	printf("简介 = %s\n", (*pb).introduction);
	printf("作者 = %s\n", (*pb).author);
	printf("书号 = %d\n", (*pb).bookNum);
	return 0;
}

但是这种写法呢感觉很麻烦而且不是很直观,接下来给大家介绍一种操作符 "->" 他也能够做到相同的效果,代码如下所示:

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>

struct Book {
	//书名
	char bookName[16];
	//简介
	char introduction[100];
	//作者
	char author[20];
	//书号
	short bookNum;
};

int main() {
	struct Book book1 = {"C语言程序设计","C语言从入门到精通","小澜",11011};
	struct Book* pb = &book1;
	printf("书名 = %s\n", pb->bookName);
	printf("简介 = %s\n", pb->introduction);
	printf("作者 = %s\n", pb->author);
	printf("书号 = %d\n", pb->bookNum);
	return 0;
}

        解释一下:pb里面存放的是 book1 的地址,所以指向该地址的对象 book1,然后再用 "->" 操作符指向 该对象 book1 中的属性 bookname、introduction、author、bookNum

        这样写出来更加方便更加直观更容易理解

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值