【C语言】sizeof与strlen有关数组和指针结合的相关问题

这一篇是对前面所学的内容的深度理解,数组和指针学习的不太好的就会在这里暴露出问题,所以复习是一个好习惯,同时本篇文章内容也会让大家对数组和指针以及sizeof和strlen有更深层次的理解~

1.sizeof与strlen

1.1sizeof

sizeof计算的是变量所占内存空间的大小,单位是字节;如果操作数本身就是类型,如int,char,计算的是使用该类型创建变量所占用内存空间的大小,比如下面代码:

//sizeof操作符
int main()
{
	int a = 0;
	printf("%zd\n", sizeof(a));
	printf("%zd\n", sizeof a);//证明了sizeof不是函数

	printf("%zd\n", sizeof(int));

	return 0;
}

上面代码我们能看出sizeof(a)和sizeof(int)实际上是一样的,因为a本身就是整型。值得注意的是,sizeof(a)可以将括号去掉,变为sizeof a也可以。而我们又知道,函数在调用的时候,即使是无参数的函数,都需要在后面添上(),这也证实了:sizeof是操作符而不是函数

1.2strlen

我们说sizeof不是函数,但是strlen可是实实在在的字符串函数。关于字符串函数将会再后面给大家更新,这里先掌握对strlen函数的使用。

strlen函数求的是字符串的长度,统计的是‘\0’之前的元素的个数,使用之前要引用头文件#include<string.h>我们来看函数原型:

//strlen函数

size_t strlen(const char* str)

它的返回值是size_t类型,size_t指的是无符号的整型,可能是unsigned int,unsigned long等,具体看编译器。形参是一个指针,指向字符串的第一个元素,统计\0’之前的元素的个数。我们来看下面的代码:

//strlen函数
#include<string.h>
int main()
{
	size_t len = strlen("abc\0def");
	printf("%zd\n", len);

	//const char* str = "abcdef";
	char str[] = "abcdef";
	printf("%zd\n", strlen(str));

	char arr[] = { 'a','b','c' };
	printf("%zd\n", strlen(arr));//随机值


	return 0;
}

第一个strlen打印出来应该是3,因为abc后就看到了\0,所以后面的就不用管它了;第二个打印出来就是6,因为字符串后默认都会添加一个“\0”;第三个这样的写法,数组中只有abc,并没有“\0”,由于并不知道“\0”具体的位置,所以strlen返回值将是一个随机值。

1.3sizeof与strlen对比

sizeofstrlen
1. sizeof是操作符
1. strlen是库函数,使用需要包含头文件 string.h
2. sizeof计算操作数所占内存的大小
单位是字节
2. srtlen求的是字符串的长度,统计的是 \0 之前字符的个数
3. 不关注内存中存放什么数据
3. 关注内存中是否有 \0 ,如果没有 \0 ,就会持续往后找,可
    能会越界,返回值为随机值

2.数组和指针笔试题分析

大家可以先尝试思考一下以下代码的结果:

2.1一维数组相关

#include <stdio.h>
int main()
{
	int a[] = { 1,2,3,4 };
	printf("%d\n", sizeof(a));
	printf("%d\n", sizeof(a + 0));
	printf("%d\n", sizeof(*a));
	printf("%d\n", sizeof(a + 1));
	printf("%d\n", sizeof(a[1]));
	printf("%d\n", sizeof(&a));
	printf("%d\n", sizeof(*&a));
	printf("%d\n", sizeof(&a + 1));
	printf("%d\n", sizeof(&a[0]));
	printf("%d\n", sizeof(&a[0] + 1));

	return 0;
}

分析:

sizeof(a):数组名单独在sizeof()内部,计算的是整个数组的大小,为4*4=16个字节。

sizeof(a+0):数组名并没有放在sizeof()内部,代表的是首元素的地址,加0还是首元素的地址,只要是地址在32位平台下为4个字节,64位平台下为8个字节。

sizeof(*a):数组名并没有放在sizeof()内部,代表的是首元素的地址,解引用代表的是第一个元素,为4个字节。

sizeof(a+1):数组名并没有放在sizeof()内部,代表的是首元素的地址,加1是第二个元素的地址,只要是地址在32位平台下为4个字节,64位平台下为8个字节。

sizeof(a[1]):a[1]指的是数组中的第2个元素,大小为4个字节。

sizeof(&a):&a代表的是取出整个数组的地址,只要是地址在32位平台下为4个字节,64位平台下为8个字节。

sizeof(*&a):取地址和解引用可相互抵消,等价与sizeof(a),计算的是整个数组的大小,为16个字节。

sizeof(&a+1):&a代表的是取出整个数组的地址,加1表示跳过整个数组后的地址,只要是地址在32位平台下为4个字节,64位平台下为8个字节。

sizeof(&a[0]):a[0]表示的是数组的第一个元素,&a[0]表示首元素的地址,只要是地址在32位平台下为4个字节,64位平台下为8个字节。

sizeof(&a[0]+1):a[0]表示的是数组的第一个元素,&a[0]表示首元素的地址,加1表示第二个元素的地址,只要是地址在32位平台下为4个字节,64位平台下为8个字节。

2.2字符数组相关

2.2.1代码1

#include <stdio.h>
int main()
{
	char arr[] = { 'a','b','c','d','e','f' };
	printf("%d\n", sizeof(arr));
	printf("%d\n", sizeof(arr + 0));
	printf("%d\n", sizeof(*arr));
	printf("%d\n", sizeof(arr[1]));
	printf("%d\n", sizeof(&arr));
	printf("%d\n", sizeof(&arr + 1));
	printf("%d\n", sizeof(&arr[0] + 1));

	return 0;
}

分析:

sizeof(arr):数组名单独在sizeof()内部,计算的是整个数组的大小,为6*1=6个字节。

sizeof(arr+0):数组名并没有放在sizeof()内部,代表的是首元素的地址,加0还是首元素的地址,只要是地址在32位平台下为4个字节,64位平台下为8个字节。

sizeof(*arr):数组名并没有放在sizeof()内部,代表的是首元素的地址,解引用代表的是第一个元素,为1个字节。

sizeof(arr[1]):arr[1]指的是数组中的第2个元素,大小为1个字节。

sizeof(&arr):&arr代表的是取出整个数组的地址,只要是地址在32位平台下为4个字节,64位平台下为8个字节。

sizeof(&arr+1):&arr代表的是取出整个数组的地址,加1表示跳过整个数组后的地址,只要是地址在32位平台下为4个字节,64位平台下为8个字节。

sizeof(&arr[0]+1):arr[0]表示的是数组的第一个元素,&arr[0]表示首元素的地址,加1表示第二个元素的地址,只要是地址在32位平台下为4个字节,64位平台下为8个字节。

2.2.2代码2

#include<stdio.h>
#include<string.h>
int main()
{
	char arr[] = { 'a','b','c','d','e','f' };
	printf("%d\n", strlen(arr));
	printf("%d\n", strlen(arr + 0));
	printf("%d\n", strlen(*arr));
	printf("%d\n", strlen(arr[1]));
	printf("%d\n", strlen(&arr));
	printf("%d\n", strlen(&arr + 1));
	printf("%d\n", strlen(&arr[0] + 1));

	return 0;
}

strlen(arr):arr表示首元素的地址,数组中没有‘\0’,越界访问,为随机值。

strlen(arr+0):arr+0表示首元素的地址,数组中没有‘\0’,越界访问,为随机值。

strlen(*arr):*arr表示第一个数组的元素‘a’,相当于将字符‘a’的ASCII码值97传入strlen,ASCII码值通常比较小,由于地址的特殊性,一般都会被操作系统使用,非法访问,error。

strlen(arr[1]):arr[1]表示第二个数组的元素‘b’,相当于将字符‘b’的ASCII码值98传入strlen,ASCII码值通常比较小,由于地址的特殊性,一般都会被操作系统使用,非法访问,error。

strlen(&arr):&arr代表的是取出整个数组的地址,整个数组的地址和首元素地址起始位置相同,统计'\0'之前的字符个数,数组中没有‘\0’,越界访问,为随机值。

strlen(&arr+1):&arr代表的是取出整个数组的地址,加1表示跳过整个数组后的地址,越界访问,为随机值。

strlen(&arr[0] + 1):arr[0]表示数组第一个元素,&arr[0]表示第一个元素的地址,加1表示第二个元素的地址,数组中没有‘\0’,越界访问,为随机值。

2.2.3代码3

#include<stdio.h>
#include<string.h>
int main()
{
	char arr[] = "abcdef";
	printf("%d\n", sizeof(arr));
	printf("%d\n", sizeof(arr + 0));
	printf("%d\n", sizeof(*arr));
	printf("%d\n", sizeof(arr[1]));
	printf("%d\n", sizeof(&arr));
	printf("%d\n", sizeof(&arr + 1));
	printf("%d\n", sizeof(&arr[0] + 1));

	return 0;
}

sizeof(arr):数组名单独在sizeof()内部,计算的是整个数组的大小,“\0”也算一个元素,数组大小为为7*1=7个字节。

sizeof(arr+0):数组名并没有放在sizeof()内部,代表的是首元素的地址,加0还是首元素的地址,只要是地址在32位平台下为4个字节,64位平台下为8个字节。

sizeof(*arr):数组名并没有放在sizeof()内部,代表的是首元素的地址,解引用代表的是第一个元素,为1个字节。

sizeof(arr[1]):arr[1]指的是数组中的第2个元素,大小为1个字节。

sizeof(&arr):&arr代表的是取出整个数组的地址,只要是地址在32位平台下为4个字节,64位平台下为8个字节。

sizeof(&arr+1):&arr代表的是取出整个数组的地址,加1表示跳过整个数组后的地址,只要是地址在32位平台下为4个字节,64位平台下为8个字节。

sizeof(&arr[0]+1):arr[0]表示的是数组的第一个元素,&arr[0]表示首元素的地址,加1表示第二个元素的地址,只要是地址在32位平台下为4个字节,64位平台下为8个字节。

2.2.4代码4

#include<stdio.h>
#include<string.h>
int main()
{
	char arr[] = "abcdef";
	printf("%d\n", strlen(arr));
	printf("%d\n", strlen(arr + 0));
	printf("%d\n", strlen(*arr));
	printf("%d\n", strlen(arr[1]));
	printf("%d\n", strlen(&arr));
	printf("%d\n", strlen(&arr + 1));
	printf("%d\n", strlen(&arr[0] + 1));

	return 0;
}

strlen(arr):arr表示首元素的地址,‘\0’前有6个元素,字符串长度为6。

strlen(arr+0):arr+0表示首元素的地址,‘\0’前有6个元素,字符串长度为6。

strlen(*arr):*arr表示第一个数组的元素‘a’,相当于将字符‘a’的ASCII码值97传入strlen,ASCII码值通常比较小,由于地址的特殊性,一般都会被操作系统使用,非法访问,error。

strlen(arr[1]):arr[1]表示第二个数组的元素‘b’,相当于将字符‘b’的ASCII码值98传入strlen,ASCII码值通常比较小,由于地址的特殊性,一般都会被操作系统使用,非法访问,error。

strlen(&arr):&arr代表的是取出整个数组的地址,整个数组的地址和首元素地址起始位置相同,统计'\0'之前的字符个数,‘\0’前有6个元素,字符串长度为6。

strlen(&arr+1):&arr代表的是取出整个数组的地址,加1表示跳过整个数组后的地址,越界访问,为随机值。

strlen(&arr[0] + 1):arr[0]表示数组第一个元素,&arr[0]表示第一个元素的地址,加1表示第二个元素的地址,‘\0’前有5个元素,长度为5。

2.2.5代码5

#include<stdio.h>
#include<string.h>
int main()
{
	const char* p = "abcdef";
	printf("%d\n", sizeof(p));
	printf("%d\n", sizeof(p + 1));
	printf("%d\n", sizeof(*p));
	printf("%d\n", sizeof(p[0]));
	printf("%d\n", sizeof(&p));
	printf("%d\n", sizeof(&p + 1));
	printf("%d\n", sizeof(&p[0] + 1));

	return 0;
}

sizeof(p):p是指针变量,指针变量是地址,只要是地址在32位平台下为4个字节,64位平台下为8个字节。

sizeof(p+1):p是指针变量,p+1也是指针变量,指针变量是地址,只要是地址在32位平台下为4个字节,64位平台下为8个字节。

sizeof(*p):*p表示字符串的第一个字母,大小为1个字节。

sizeof(p[0]):p[0]表示字符串的第一个字母,大小为1个字节。

sizeof(&p):p是指针变量,&p是二级指针变量,指针变量是地址,只要是地址在32位平台下为4个字节,64位平台下为8个字节。

sizeof(&p+1):p是指针变量,&p是二级指针变量,&p+1也是指针变量,指针变量是地址,只要是地址在32位平台下为4个字节,64位平台下为8个字节。

sizeof(&p[0]+1):p[0]表示字符串的第一个字母,&p[0]表示第一个元素的地址,加1表示第二个元素的地址,只要是地址在32位平台下为4个字节,64位平台下为8个字节。

2.2.6代码6

#include<stdio.h>
#include<string.h>
int main()
{
	const char* p = "abcdef";
	printf("%d\n", strlen(p));
	printf("%d\n", strlen(p + 1));
	printf("%d\n", strlen(*p));
	printf("%d\n", strlen(p[0]));
	printf("%d\n", strlen(&p));
	printf("%d\n", strlen(&p + 1));
	printf("%d\n", strlen(&p[0] + 1));

    return 0;
}

strlen(p):p是指针变量,指向字符串的第一个元素,‘\0’前有6个元素,字符串长度为6。

strlen(p+1):p是指针变量,p+1指向字符串的第二个元素,‘\0’前有5个元素,字符串长度为5。

strlen(*p):*p表示字符串的第一个字母‘’a’,相当于将字符‘a’的ASCII码值97传入strlen,ASCII码值通常比较小,由于地址的特殊性,一般都会被操作系统使用,非法访问,error。

strlen(p[0]):p[0]表示字符串的第一个字母‘’a’,相当于将字符‘a’的ASCII码值97传入strlen,ASCII码值通常比较小,由于地址的特殊性,一般都会被操作系统使用,非法访问,error。

strlen(&p):p是指针变量,&p是二级指针变量,和字符串关系不大,没有‘\0’,越界访问,为随机值。

strlen(&p+1):p是指针变量,&p是二级指针变量,&p+1也是指针变量,没有‘\0’,越界访问,为随机值。

strlen(&p[0]+1):p[0]表示字符串的第一个字母,&p[0]表示第一个元素的地址,加1表示第二个元素的地址,‘\0’前有5个元素,字符串长度为5。

2.3二维数组相关

#include<stdio.h>
#include<stdlib.h>
int main()
{
	int a[3][4] = { 0 };
	printf("%d\n", sizeof(a));
	printf("%d\n", sizeof(a[0][0]));
	printf("%d\n", sizeof(a[0]));
	printf("%d\n", sizeof(a[0] + 1));
	printf("%d\n", sizeof(*(a[0] + 1)));
	printf("%d\n", sizeof(a + 1));
	printf("%d\n", sizeof(*(a + 1)));
	printf("%d\n", sizeof(&a[0] + 1));
	printf("%d\n", sizeof(*(&a[0] + 1)));
	printf("%d\n", sizeof(*a));
	printf("%d\n", sizeof(a[3]));

	return 0;
}

首先我们明确:a[3][4]是二维数组,其中数组中有三个一维数组,类型为int[4],每个一维数组有四个元素,类型为int。

sizeof(a):数组名单独在sizeof()内部,计算的是整个数组的大小,为4*4*3=48个字节。

sizeof(a[0][0]):a[0][0]代表第一个一维数组中的第一个元素,大小为4个字节。

sizeof(a[0]):a[0]单独放在sizeof内部,计算的是整个一维数组的大小,即第一行,大小为16个字节。

sizeof(a[0]+1):a[0]并没有单独放在sizeof内部,表示一维数组首元素的地址,a[0]+1表示a[0][1]的地址,只要是地址在32位平台下为4个字节,64位平台下为8个字节。

sizeof(*(a[0]+1)):a[0]+1代表第一行的第二个元素的地址,解引用得到第一行的第二个元素,大小为4个字节。

sizoef(a+1):a并没有单独放在sizeof内部,表示二维数组首元素的地址,即第一行的地址,a+1表示第二行的地址,只要是地址在32位平台下为4个字节,64位平台下为8个字节。

sizoef(*(a+1)):a+1表示第二行的地址,解引用得到的是第二行,大小为4*4=16个字节。

sizeof(&a[0]+1):&a[0]代表的是取出整个一维数组的地址,+1表示第二行的地址,只要是地址在32位平台下为4个字节,64位平台下为8个字节。

sizeof(*(&a[0]+1)):&a[0]+1代表第二行的地址,解引用得到的是第二行,大小为4*4=16个字节。

sizeof(*a):a并没有单独放在sizeof内部,表示二维数组首元素的地址,即第一行的地址,解引用得到第一行,大小为4*4=16个字节。

sizoef(a[3]):a[3]单独放在sizeof内部,计算的是整个一维数组的大小,不需要实际存在,为4*4=16个字节。

对二维数组的理解稍微有些难,大家可反复观看,加深理解。

3.指针运算笔试题分析

3.1题目1

下列程序运行的结果是?

#include<stdio.h>
int main()
{
	int a[5] = { 1,2,3,4,5 };
	int* ptr = (int*)(&a + 1);
	printf("%d %d", *(a + 1), *(ptr - 1));

	return 0;
}

首先我们分析,&a取出的是整个数组的地址,类型为int*[5],本来+1是跳过整个数组的大小,即跳过int[5]大小,但是如果被强制转换成int*类型,+1只跳过一个int的大小,即第二个元素。我们看下面图解:

 *(a+1):a表示数组首元素的地址,+1表示第二个元素的地址,解引用就是第二个元素,所以结果是2。

*(ptr-1):ptr表示的是&a+1后再转化成int*类型,和我们上面举的例子不同,上面是转换成int*后再+1,我们现在是+1之后再转换。所以先跳过整个int[5],再往回跳过一个int,解引用就是5。

3.2题目2

在X86环境下,假设结构体的大小是20个字节,程序输出的结果是啥?

struct Test
{
	int Num;
	char* pcName;
	short sDate;
	char cha[2];
	short sBa[4];
}*p = (struct Test*)0x100000;

//指针+-整数
//
int main()
{
	 printf("%p\n", p + 0x1);//0x100014
	 printf("%p\n", (unsigned long)p + 0x1);//0x100001 - 整型+1,就是+1
	 printf("%p\n", (unsigned int*)p + 0x1); //0x100004

	 return 0;
}

首先我们定义了一个结构体类型,同时初始化结构体指针变量p为0x100000。

p+0x1:我们要知道,指针+1的增幅大小取决于指针类型。如int*的指针+1跳过1个int类型,char*类型的指针+1跳过1个char类型,那么结构体指针+1应该跳过一个结构体类型,所以应该跳过20个字节。但因为0x表示它是十六进制数,+1加的是十六进制的1,所以20转化为16进制应该为14,所以结果应该是0x100014。

(unsigned long)p + 0x1:我们将指针变量转化为unsigned long类型,此时它已经不是指针变量了,所以整型+1就是+1,没有什么结构体大小什么字节的,整数+1就是+1,就是简单的运算,所以结果为0x100001。

(unsigned int*)p + 0x1:我们将p转化为了unsigned int*类型的指针变量,此时指针+1跳过一个unsigned int大小,为4个字节,十六进制也是4,所以结果为0x100004。

3.3题目3

下面程序的结果是啥?

#include <stdio.h>

int main()
{
	int a[3][2] = { (0, 1), (2, 3), (4, 5) };
	int* p;
	p = a[0];
	printf("%d", p[0]);//1

	return 0;
}

二维数组中时逗号表达式,我们先对它进行化简:

//逗号表达式

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

所以该数组在内存中的存储应该是:

a[0]表示首元素的地址,即a[0]数组首元素1的地址,指针变量p也是指向首元素1的地址,p[0]相当于*(p+0) ,所以就是结果1。

3.4题目4

假设环境是x64环境,程序输出的结果是啥?

//指针-指针 
#include<stdio.h>
int main()
{
	int a[5][5];
	int(*p)[4];
    p = a;
	printf("%d,%p\n", &p[4][2] - &a[4][2], &p[4][2] - &a[4][2]);

	return 0;
}

这段代码的数组a有5个元素,每个元素都是含有5个int类型元素的一维数组,此时数组指针变量p是一个int(*)[4]类型的指针。当我们将a赋值给p时,我们这道题目的核心其实就在于指针+1的增幅大小取决于指针类型,也是咱们一致强调的内容。同时,数组名代表数组首元素的地址。

1.a的类型为int(*)[5],+1跳过一个int[5]类型的大小。

2.p的类型为int(*)[4],+1跳过一个int[4]类型的大小。

所以两者是不一样的,他们的差值的绝对值表示指针之间元素的个数,从图中我们能直观看出差值为4,但是是前-后,所以为-4,%p是用来打印地址,,-4在内存中的补码为1111 1111 1111 1111 1111 1111 1111 1100,被当做地址打印,所以结果为FFFFFFFC,而%d是打印有符号整数,所以就是-4。

3.5题目5

下列程序的结果是啥?

#include <stdio.h>
int main()
{
	int aa[2][5] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
	int* ptr1 = (int*)(&aa + 1);
	int* ptr2 = (int*)(*(aa + 1));
	printf("%d,%d", *(ptr1 - 1), *(ptr2 - 1));//10 5

	return 0;
}

*(ptr-1):对于ptr1,&aa取出的是整个二维数组的地址,+1跳过整个二维数组,此时转化成int*类型,-1跳回一个int类型大小,再解引用结果结果就是10。

*(ptr2-1):对于ptr2,aa表示第一行的地址,+1表示第二行的地址,解引用表示第二行,也就是6,7,8,9,10这个一维数组的首元素地址,将它转化为int*类型,-1跳回1个int类型大小,再解引用的结果就是5。

3.6题目6

下列程序的结果是啥?

#include <stdio.h>
int main()
{
	char* a[] = { "work","at","alibaba" };
	char** pa = a;
	pa++;
	printf("%s\n", *pa);

	return 0;
}

数组a是一个指针数组,数组中存着work、at、alibaba三个字符串常量的地址。而他们的地址,也就是字符串常量地址的地址,我们用二级指针pa来接收。此时pa是char**类型,+1跳过一个char*类型的大小。如图所示:

所以*pa得到的是at的地址,%s从给出的地址开始打印直到\0,所以程序的结果应该是at。

3.7题目7

#include <stdio.h>
int main()
{
	char* c[] = { "ENTER","NEW","POINT","FIRST" };
	char** cp[] = { c + 3,c + 2,c + 1,c };
	char*** cpp = cp;

	printf("%s\n", **++cpp);//POINT
	printf("%s\n", *-- * ++cpp + 3);//ER
	printf("%s\n", *cpp[-2] + 3);//ST
	printf("%s\n", cpp[-1][-1] + 1);//EW

	return 0;
}

以上代码的图解如图所示:

 **++cpp:++cpp,那么cpp指向c+2,对其解引用,得到的就是c+2,即POINT的地址的地址,再解引用就得到POINT的地址,所以打印出的就是POINT。

*-- * ++cpp + 3:由于cpp已经在前面代码++了,所以++cpp指的是指向c+2的基础上再++,所以指向的是c+1,然后解引用,得到c+1,然后--,得到c+0也就是c,再次解引用,就得到了ENTER的地址,然后+3,就从ENTER的第4位开始打印,所以打印出的就是ER。

*cpp[-2] + 3:cpp[-2]就是cp[-2],我们知道cpp已经再上面经历了两次++,所以cpp,或者cp其实指向了c+1,此时减去2,那么回到第一个元素,就是c+3,然后解引用得到c+3,因为又+3,所以从FIRST的第4为开始打印,打印出的结果就是ST。

cpp[-1][-1] + 1:可以理解成*(*(cpp-1)-1)+1,cpp指向c+1,-1得到c+2的地址,解引用得到c+2,再-1得到c+1,+1所以从NEW的第2位开始打印,所以结果就是EW。

以上就是本文所有内容,感谢观看!

  • 27
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值