C语言初阶——4数组

1.一维数组

1.1的创建和初始化。

数组是一组相同类型元素的集合。

1.1.1 创建数组

int arr[ 4 ] = {1,2,3,4};

在这里插入图片描述

注意:

  1. [4]中的4是可以省略的.数组的长度就和后面初始化的列表里的元素个数一致~~
  2. [4]如果不省略的时候,后面的初始化列表的元素不能比4个多~~
  3. [4]如果不省略的时候, ,后面的初始化列表的元素可以比4个少的时候,就把数组剩下的元素填成0.
  4. 创建数组的时候, []里面的数字只能是一个常量, 不能是变量(在C89中)
  5. 如果省略[4] 中的数字,就必须使用{ }初始化~~
  6. 如果没有省略[4],并且也没有初始化~会显示一个随机值,这个值是VS自己填充的(16进制下是0xcc,翻译成中文是“烫烫烫”)0XCC:是CPU的一个特殊指令——int3中断指令.为了防止数组越界,设置的一个固定值
    在这里插入图片描述
    如果将int arr[4]放在main函数外面,这个数组就表示是一个全局变量了

在这里插入图片描述
如果是一个全局变量,没有被显式初始化,会被自动初始化为0.
如果是一个局部变量,没有被显式初始化,就是.上次残留的值~~

只要创建变量都应该显式初始化~~

1.1.2 数组初始化

数组的初始化是指,在创建数组的同时给数组的内容一些合理初始值(初始化)。针对字符数组初始化,可以像普通数组一样来使用{ }进行初始化,也可以使用“ ”用一个字符串。

char str1[] = { 'a','b','c' };
	char str2[] = { 'a',98,'c' };
	char str2[] = "abc";//注意:写双引号的时候,就是字符串,多了一个‘\0’
//不是每个字符数组都可以称为“字符串”
//字符串是一种特殊的字符数组,一定是以'\0'结尾(“ ”)!


strlen(str1);//这是一个未定义行为,!!!因为不是所有数组都是字符串数组,只有字符串数组结尾才是‘\0’,才能这么求长度
	

不是每个字符数组都可以称为“字符串”,字符串是一种特殊的字符数组,一定是以’\0’结尾(“ ”)!

给任何str系列的函数(比如strlen, strcmp…)进行传参的时候,必须要由人来保证参数是一个合法的字符串~

1.2 一维数组的使用

数组的使用:

  1. 求数组长度:sizeof(arr)/siziof(arr[0])
  2. 取下标 :[ ]
    0开始,到len-1结束
    10 [ 0, 9] 一旦数组下标越界,就是未定义行为!!
#include<stdio.h>

//创建数组

int main()
{
	int arr[10] = { 0 };
	//以循环的方式,给这个数组设置一个值,从1到10
	for (int i = 0; i < 10; i++)
	{
		arr[i] = i + 1;
	}
	for (int i = 0; i < 10; i++)
	{
		printf("%d\n", arr[i]);
	}
	return 0;
}

在这里插入图片描述

1.3 一维数组在内存中的存储

#include<stdio.h>

//数组在内存中的存储

int main()
{
	char arr[10] = { 0 };
	for (int i = 0; i < 10; i++)
	{
		printf("%p\n", &arr[i]);
	}

	return 0;
}

在这里插入图片描述
数组中的元素处在连续的内存空间上

2. 二维数组

二维数组本质上也是一维数组,只不过每个元素又是一个一维数组,所谓的数组,可以理解成一种特殊的类型,int arr[4];可以理解成是创建了4个int 类型的变量,也可以理解成创建了一个变量arr,其类型是int [4]

啥叫三维数组?
本质上也是一个-维数组, 只不过每个元素又是一个二 维数组~~
啥叫四维数组?
不管是几维数组,本质都是一维数组. 操作/语法都是和一维数组一 样的

2.1 二维数组的创建和初始化

在这里插入图片描述
如何看,可以借助VS编译器来看

1.第一种

#include<stdio.h>

//数组在内存中的存储
int main()
{
	//二维数组
	int arr[3][4] = { 0 };
	printf("啦啦啦");

	return 0;
}

在这里插入图片描述
在这里插入图片描述

2.第二种

#include<stdio.h>

//数组在内存中的存储
int main()
{
	//二维数组
	int arr[3][4] = { 0 };
	int arr1[3][4] = { 1,2,3,4,5 };
	printf("啦啦啦");

	return 0;
}

在这里插入图片描述

3.第三种

	int arr3[3][4] = {
		{1,2},
		{3,4},
		{5}
	};

{1,2,0,0}
{3,4,0,0}
{5,0,0,0}

4.第四种

int arr4[3][4] = {
		(1,2),
		(3,4),
		(5)
	};

这里的(1,2)涉及到逗号表达式
逗号表达式:a,b, C =>的值就是c,即最后一个逗号后面的值.
所以这种二维数组初始化内容为:
{2,4,5,0}
{0,0,0,0}
{0,0,0,0}

5.第五种

	int arr5[][4] = {
		1,2,3
	};

前面的可以省略
这个数组初始化为{1,2,3,0}
针对二维数组,列数一定要存在,不能省略,对于三维、四维、五维,都是只有第一个方括号可以省略不写,其他均不能省略

2.2. 二维数组的使用

#include<stdio.h>

//二维数组的使用

int  main()
{
	int arr[3][4] = {
		2,4,5
	};
	for (int row = 0; row < 3; row++)
	{
		// arr[row]  这个操作相当于取出其中的某一行,这一行也是一个数组
		for (int col = 0; col < 4; col++)
		{
			printf("%d ", arr[row][col]);//这里会先去取第一个下标,然后再取第二个下标
		}
		printf("\n");
	}
	return 0;
}

在这里插入图片描述

2.3 二维数组在内存中的存储

像一维数组一样,这里我们尝试打印二维数组的每个元素。它也和一维数组一样是顺序存储的

#include<stdio.h>
//数组在内存中的存储
int  main()
{
	int arr[3][4] = {
		2,4,5
	};
	for (int row = 0; row < 3; row++)
	{
		// arr[row]  这个操作相当于取出其中的某一行,这一行也是一个数组
		for (int col = 0; col < 4; col++)
		{
			printf("%p ", &arr[row][col]);//这里会先去取第一个下标,然后再取第二个下标
		}
		printf("\n");
	}
	return 0;
}

在这里插入图片描述

3. 数组作为函数参数

C语言中的第二个惊天BUG:数组名变成指针
数组名作为函数的参数也会触发转成指针的操作.在函数内部是无法直接求数组的长度的.必须要在函数外面提前把长度求好,再以参数的形式传给函数内部

3.1 实现冒泡排序

冒泡排序的核心思路,比较两个相邻元素的大小,看看是不是符合排序的要求.(例如,要求进行升序排序,就看前一个元素是不是比后一个元素小)如果不符合排序要求,就进行交换~
从前往后遍历,每次取出一个最大的元素放到数组最后了;从后往前遍历,每次取出一个最小的元素放到数组最前了~~

#include<stdio.h>
//冒泡排序
int bubbleSort(int arr[], int size)
{
	//写一个从后往前遍历的版本,
	//每次从后往前遍历比较相邻元素,每一趟比较把最小的元素放在最前面

	//bound => 边界的意思,使用这个变量的值,表示已排序区间和待排序区间的分界线
	//[0,bound ) 表示已排序区间
	//[bound,size)表示待排序区间
	int bound = 0;
	for (; bound < size; bound++)
	{
		//通过这个循环,来及控制后续比较相邻元素的比较趟数
		//接下来实现一趟比较交换的过程
		for (int cur = size - 1; cur > bound;cur --)//size-1表示最后一个元素的下标。可以通过arr[size-1]来获取到最后一个元素
		{//进行比较  
		//相邻元素就是以cur为基准
			//cur - 1 , cur + 1;
			//由于我们现在cur 是从 size -1 开始的 所以我们不能使用cur +1,cur+1的话可能数组下标越界,产生未定义行为
			 //所以我们使用cur -1来表示相邻元素
			if (arr[cur - 1] > arr[cur])
			{//不符合升序条件,就进行交换
				int tem = arr[cur - 1];
				arr[cur - 1] = arr[cur];
				arr[cur] = tem;
			
			}
		}
	}
}
int main()
{
	int arr[] = { 2,5,1,6,7,8,10 };
	int size = sizeof(arr) / sizeof(arr[0]);
	bubbleSort(arr, size);
	for (int i = 0; i < size; i++)
	{
		printf("%d ", arr[i]);
	}
	return 0;
}

写代码的过程中,经常会遇到这类问题:
到底是+1还是-1 ,还是不加不减,到底是<还是<=。"差一问题"实际开发中,非常容易出错的问题。也是测试_工程师在设计测试用例的时候,重点关注的地方。解决思路:把边界值具体代入数值,推演程序是否符合要求

自己写的代码

在这里插入代码片//自己实现一下冒泡排序~先实现一下老师讲课版本(从前往后遍历)
int bubble(int arr[], int size)
{//从后往前遍历

	int bound = 0;
	for (; bound < size; bound++)
	{
		for (int cur = size - 1; cur > bound; cur--)
		{
			if (arr[cur - 1] > arr[cur]) {
			
				int tem = arr[cur - 1];
				arr[cur - 1] = arr[cur ];
				arr[cur] = tem;
			}
		}
	}

}

再写一个从前往后遍历版本;

int bubble2(int arr[], int size)
{
	int bound = size-1;

	for (; bound > 0; bound--)
	{
		for (int cur = 0; cur <= bound; cur++)
		{
			if (arr[cur]  < arr[cur+1])
			{
				int tem = arr[cur ];
				arr[cur ] = arr[cur+1];
				arr[cur+1] = tem;
			}
		}
	}
	return 0;
}

4. 数组的应用实例1:三子棋

在这里插入图片描述

只要连成一条线就赢了。

  1. 我们先表示棋盘:创建一个 3*3的二维数组,每个元素是一个char类型,'x’表示玩家1、'o’表示玩家2、'空格’表示空白。
  2. 游戏流程:
    (1)创建棋盘,并初始化~把所有位置都设为空格
    (2)打印棋盘
    (3)玩家进行落子,让玩家来输入一组坐标(row,col),进行落子
    (4)判定是否获胜
    (5)电脑进行落子~ 随机落子

先想清楚,程序该咋写,再动手,然后写成上面这种条理性的描述,然后在动手敲代码

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include<stdlib.h>
#include<string.h>
#include<time.h>

#define MAX_ROW 3
#define MAX_COL 3


//1、创建棋盘并初始化
void Init(char chessBoard[MAX_ROW][MAX_COL])
{
#if 0
	for (int row = 0; row < MAX_ROW; row++)
	{
		for (int col = 0; col < MAX_COL; col++)
		{
			chessBoard[row][col] = ' ';
		}
	}
#endif

	srand((unsigned int)time(0));
	memset(chessBoard, ' ', MAX_ROW * MAX_COL * sizeof(char));
}

//2、打印棋盘

void  Print(char chessBoard[MAX_ROW][MAX_COL])
{
	
	for (int row = 0; row < MAX_ROW; row++)
	{
		printf("+---+---+---+\n");
		printf("| %c | %c | %c |\n", chessBoard[row][0], chessBoard[row][1], chessBoard[row][2]);

	}
	printf("+---+---+---+\n");
}
int menu()
{
	printf("======================\n");
	printf("欢迎来到三子棋游戏\n");
	printf("1. 开始游戏\n");
	printf("0. 退出游戏\n");
	printf("======================\n");
	printf("请输入您的选择: \n");
	int choice = 0;
	scanf("%d", &choice);
	return choice;
}
//3.提示玩家落子(输入坐标)
void playerMove(char chessBoard[MAX_ROW][MAX_COL])
{
	while (1)
	{
		printf("轮到玩家落子!\n");
		printf("请输入您要落子的位置坐标:(row,col):");
		int row = 0;
		int col = 0;
		scanf("%d %d", &row, &col);
		//
		if (row < 0 || row >= MAX_ROW
			|| col<0 || col>MAX_COL)
		{
			printf("您的输入有误!\n");
			continue;
		}
		if (chessBoard[row][col] != ' ')
		{
			//当前位置已经有子了,不能重复落子
			printf("该位置已经有子了!\n");
			continue;
		}
		chessBoard[row][col] = 'X';
		break;
	}
}

//5、提示电脑落子——随机落子
void computerMove(char chessBoard[MAX_ROW][MAX_COL])
{
	while (1)
	{
		printf("轮到电脑落子!\n");
		int row = rand() % MAX_ROW;
		int col = rand() % MAX_ROW;
		if (chessBoard[row][col] != ' ')
		{
			continue;
		}
		chessBoard[row][col] = 'O';
		break;
	}

}


//判断是否满
int IsFull(char chessBoard[MAX_ROW][MAX_COL])
{
	//查找下棋盘是否有空位
	for (int row = 0; row < MAX_ROW; row++)
	{
		for (int col = 0; col < MAX_COL; col++)
		{
			if (chessBoard[row][col] == ' ')
			{
				//找到空位置了,说明棋盘没满
				return 0;
			}
		}
	}
	//所有位置找完之后也没找到空格,最终才返回
	return  1;
}


//4.检查游戏是否结束
//返回值表示游戏状况
//1、返回“X”表示玩家获胜
//2、返回“O”表示电脑获胜
//3、返回“ ”表示还未获胜
// 4、返回“Q”表示和局 
char check(char chessBoard[MAX_ROW][MAX_COL])
{
	//1.检查所有的行,看是否有连成3个子的情况
	for (int row = 0; row < MAX_ROW; row++)
	{
		if (chessBoard[row][0] != ' '
			&& chessBoard[row][0] == chessBoard[row][1]
			&& chessBoard[row][0] == chessBoard[row][2])
		{
			return chessBoard[row][0];
		}
	}
	//2、再检查所有的列,看是否有连成3个子的情况
	for (int col = 0; col < MAX_COL; col++)
	{
		if (chessBoard[0][col] != ' '
			&& chessBoard[0][col] == chessBoard[1][col]
			&& chessBoard[0][col] == chessBoard[2][col])
		{
			return chessBoard[0][col];
		}
	}
	//3、再检查对角线,看是否有连成3个子的情况
	if (chessBoard[0][0] != ' '
		&& chessBoard[0][0] == chessBoard[1][1]
		&& chessBoard[0][0] == chessBoard[2][2])
	{
		return chessBoard[0][0];
	}
	if (chessBoard[0][2] != ' '
		&& chessBoard[0][2] == chessBoard[1][1]
		&& chessBoard[0][2] == chessBoard[2][0])
	{
		return chessBoard[0][2];
	}
	//4、判定是否和棋? 如果棋盘满了就算和棋
	if (IsFull(chessBoard))
	{
		return 'Q';
	}
	return ' ';


}

void game()
{
#if 1
	

		//一局游戏的核心函数

		//1.创建棋盘并初始化棋盘 一个字符型的二维数组
		char chessBoard[MAX_ROW][MAX_COL] = { 0 };
		Init(chessBoard);
		char winner = ' ';
		while (1)
		{
			//2.打印棋盘
			Print(chessBoard);
			//3.提示玩家落子(输入坐标)
			playerMove(chessBoard);
			//4.检查游戏是否结束
			winner = check(chessBoard);
			if (winner != ' ')
			{
				break;
			}
			//5、提示电脑落子——随机落子
			computerMove(chessBoard);
			//6.检查游戏是否结束
			winner = check(chessBoard);
			if (winner != ' ')
			{
				break;
			}
		}
		Print(chessBoard);
		if (winner == 'X')
		{
			printf("恭喜你,您赢了\n");
		}
		else if (winner == 'O')
		{
			printf("你真菜,你连电脑都下不会");
		}
		else
		{
			printf("和棋");
		}
}
	

#endif






int main()
{
#if 1
	while (1)
	{
		int choice = menu();
		if (choice == 1)
		{
			game();
		}
		else if (choice == 0)
		{
			printf("goodBye!\n");
			break;
		}
		else
		{
			printf("您的输入有误,请重新输入:\n");
		}
	}
#endif
	//打印的格式要匹配,否则程序会崩溃
	//int a = 100;
	//printf("%s\n", a);
	return 0;
}

5. 数组的应用实例2:扫雷游戏

5.1 扫雷游戏思考

1.创建地图,9*9的二维数组,元素类型是什么?
2.每个格子的状态有以下状态

a)未翻开(草地)
b)已翻开(数字)

c)是地雷
d)不是地雷

四种状态是两大类,用两个二维数组表示。
第一个数组表示翻开状态showMap:
char类型表示,如果是 * 表示未翻开 ; 如果是阿拉伯数字,就表示翻开
第二个数组表示是否地雷状态mineMap:
char 类型表示 如果是 【1】:表示是地雷 【0】:表示不是地雷 、 也可以int 类型表示 如果是数字1:地雷; 数字0:不是地雷

5.2 扫雷游戏程序基本流程

程序基本流程:

  1. 先创建地图(两个地图都要创建),并进行初始化
    a)针对showMap 初始化全是 *
    b)针对mineMp 初始化10个地雷(根据随机位置来摆放) 没地雷用数字0 表示,地雷位置用数字1表示
  2. 打印地图showMap
  3. 提示玩家输入要翻开的位置坐标,并进行校验
  4. 判定当前位置是否是雷,如果是雷则游戏失败,游戏结束
  5. 如果当前位置是最后一个“不是雷的格子”那么游戏胜利,游戏结束
  6. 如果不是雷,更新翻开的当前位置,把【*】替换成一个数字(就把showMap中对应位置的 * 修改成一个具体的数字)
    这个数字要根据当前位置周围8个格子的地雷数目来决定
  7. 下一步从2继续开始,直到游戏结束。

5.3 C语言函数的小bug:一个函数只能返回一个返回值

由于C语言函数不能直接返回两个值,所以我们只能通过参数的方式来返回,参数类型需要是指针类型,才能返回值。
这种做法是C语言中的一个常见做法,但是这个其实也是C语言中的无奈之举。

  • 输出型参数:如果参数类型是指针类型,函数内部就能够对函数外部的变量产生影响此时这样的参数就称为“输出型参数

主要是C语言的函数只有一个返回值,
如果需要一个函数需要产生多个输出结果,只能通过输出型参数的方式来解决。更优雅的方案:一个返回值能返回多个值。C语言不支持,但是GO语言支持一个函数返回多个值的。(也支持输出性参数)
go语言是学习python语言的,Python 很早就实现了这个功能,就是一个函数返回多个返回值。

5.3 扫雷游戏代码具体实现

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdio.h>
#include <time.h>
#include <string.h>


#define MAX_ROW 9
#define MAX_COL 9
#define MINE_COUNT 10

int menu()
{
	printf("=============================================\n");
	printf("欢迎来到扫雷游戏!\n");
	printf("==================1:游戏开始=================\n");
	printf("==================0:游戏结束=================\n");
	printf("=============================================\n");
	printf("请输入您的选择:");
	int choice = 0;
	scanf("%d", &choice);
	return choice;

}

void init(char showMap[MAX_ROW][MAX_COL], char mineMap[MAX_ROW][MAX_COL])
{
	memset(showMap, '*', MAX_ROW * MAX_COL * sizeof(char));
	srand((unsigned int)time(0));
	memset(mineMap, '0', MAX_ROW * MAX_COL * sizeof(char));
	int count = MINE_COUNT;
	while (count > 0)
	{
		int row = rand() % MAX_ROW;
		int col = rand() % MAX_COL;
		if (mineMap[row][col] == '1')
		{
			//表明这个位置已经是雷了;重新随机
			continue;
		}
		mineMap[row][col] = '1';
		count--;
	}
}

void Print(char showMap[MAX_ROW][MAX_COL])
{
	printf("  | ");
	for (int c = 0; c < MAX_COL; c++)
	{
		printf("%d ", c);
	}
	printf("\n");
	printf("--+------------------+");
	printf("\n");
	for (int i = 0; i < MAX_ROW; i++)
	{
		printf("%d |", i);

		for (int j = 0; j < MAX_COL; j++)
		{
			printf("%c ", showMap[i][j]);
		}
		printf("|\n");
	}
	printf("--+------------------+");
	printf("\n");
}
//由于C语言函数不能直接返回两个值,所以我们只能通过参数的方式来返回,参数类型需要是指针类型,才能返回值。
//这里的row 和col这俩变量都是输出型参数 ,这俩参数在调用函数的时候并不需要给传递具体的值。
//     但是在函数返回的时候,就会把这两个值返回到row 和col这俩变量里面,相当于写到函数外面的变量了
//这种做法是C语言中的一个常见做法,但是这个其实也是C语言中的无奈之举,
//
//输出型参数:如果参数类型是指针类型,函数内部就能够对函数外部的变量产生影响
//此时这样的参数就称为“输出型参数”
//主要是C语言的函数只有一个返回值,如果需要一个函数需要产生多个输出结果,只能通过输出型参数的方式来解决

void Input(char showMap[MAX_ROW][MAX_COL], int* row, int* col )
{
	while (1)
	{
		printf("请输入你要翻开的坐标(row,col):");
		scanf("%d %d", row, col);//这俩参数已经是指针了,不需要& ,&就意味得到变量的指针
		if (*row < 0 || *row >= MAX_ROW
			|| *col < 0 || *col >= MAX_COL)
		{
			printf("您的输入有误,请重新输入:");
			continue;
		}
		if (showMap[*row][*col] != '*')  //这里也是一样的道理他是指针,需要使用*进行解引用
		{
			//说明当前位置已经被翻开。需要重新输入
			printf("当前位置已经被翻开,请重新输入:");
			continue;
		}
		break;
	}
}




int checkMine(char mineMap[MAX_ROW][MAX_COL], 
	          int row, int col)
{
	if (mineMap[row][col] == '1')
	{
		return 1;
	}
	return 0;
}




int checkLastBlank(int* count)
{
	//思路:记录当前翻开格子的数量。一共81个格子,有10个是雷,只要翻开71个不是雷的格子就游戏胜利
	//这个count 的生命周期就是一局游戏的生命周期

	*count += 1;

	if (count == 71)
	{
		printf("恭喜你,游戏胜利!\n");
		return 1;
	}
	else
	{
		return 0;
	}
}


void Update(char showMap[MAX_ROW][MAX_COL],
	    char mineMap[MAX_ROW][MAX_COL],
	    int row, int col)
{
	//翻开指定位置之后,要把这个位置的“*”替换成一个数字
	//需要先统计这个位置周围8个格子中有几个雷,
	 
	int count = 0;
	for (int r = row - 1; r <= row + 1; r++)
	{
		for (int c = col - 1; c <= col + 1; c++)
		{
			if (r < 0 || r >= MAX_ROW
				|| c < 0 || c >= MAX_COL)
			{
				continue;
			}
			if (r == row && c == col)
			{
				continue;
			}
			if (mineMap[r][c] == '1')
			{
				count++;
			}
		}

	
	showMap[row][col] = count + '0';
	//showMap[row][col] = count + 48;
	
	}
}

void game()
{
	//1、先创建两个地图,并初始化
	char showMap[MAX_ROW][MAX_COL] = { 0 };
	char mineMap[MAX_ROW][MAX_COL] = { 0 };
	init(showMap, mineMap);
	int* count = 0;
	while (1)
	{
		//2、打印地图
		Print(mineMap);//这里为了我们测试方便,就顺便把地雷的地图打印出来方便我们看showMap的打印是否正确。最终游戏会删掉这句代码。
		Print(showMap);
		
		//3、提示玩家输入要翻开位置的坐标,并校验
		int row = 0;
		int col = 0;
		Input(showMap, &row, &col);

		//4、检查当前玩家翻开的位置是否是雷,如果是,则游戏失败
		if (checkMine(mineMap, row, col))
		{
			Print(mineMap);
			printf("你踩雷了,游戏失败\n");
			break;
		}
		//5、检查玩家翻开的当前位置是否是最后一个位置,如果是,则游戏胜利
		if (checkLastBlank(&count))
		{
			printf("恭喜你,扫雷成功!\n");
			break;
		}
		
		//6、更新翻开的当前位置,把【*】替换为一个数字,数字根据周围八个地图的地雷数量决定。
		Update(showMap, mineMap, row, col);
	}
}


int main()
{
	while (1)
	{
		int choice = menu();
		if (choice == 1)
		{
			game();
		}
		else if (choice == 0)
		{
			printf("游戏结束!\n");
			break;
		}
		else
		{
			printf("您的输入有误,请重新输入:\n");
			continue;
		}
	}
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值