指针讲解及注意事项(Plus)

8 篇文章 0 订阅

什么是指针

指针是C语言的核心概念,也是C语言的特色和精华所在,更是初学者学习的难点所在。掌握了指针,才谈得上是真正掌握了C语言,指针也就是内存地址,指针变量是用来存放内存地址的变量,不同类型的指针变量所占用的存储单元长度是相同的,而存放数据的变量因数据的类型不同,所占用的存储空间长度也不同。有了指针以后,不仅可以对数据本身,也可以对存储数据的变量地址进行操作。

指针的作用

1、指针就是指向一个特定内存地址的一个变量。指针可以有效地表示复杂的数据结构、动态分配内存、高效地使用数组和字符串、使得调用函数时得到多个返回值等。指针的应用往往与数组联系在一起,是最频繁的,也是最基础的。在定义数组和函数时,系统都会为其自动分配一个指向其首地址的指针。对于一个数组,其名称就是一个指针变量。

2、指针只是一个变量,它存储一个内存地址。如果传入一个地址,比传入一个副本效率要高,因为少了一个拷贝过程。

3、指针能够有效的表示数据结构,能动态分配内存,能较方便的使用字符串,有效的使用数组。

4、指针直接与数据等的储存地址有关,是比较重要的。比如,地址传递比值传递高效,因为值传递先从实参的地址中提出值,再赋值给形参带入函数计算;而指针则把形参的地址直接指向实参地址,使用时直接提出数据,使效率提高,特别在频繁赋值等情况下,缺点是在函数中也可以改变其值。

指针的优点

1.可以提高程序的编译效率和执行速度减小系统开销,使程序更加简洁。

2.通过指针被调用函数可以向调用函数处返回除正常的返回值之外的其他数据,从而实现两者间的双向通信。

3.利用指针可以实现动态内存分配(例如:动态数组)。

4.指针还用于表示和实现各种复杂的数据结构(例如:链表,树),从而为编写出更加高质量的程序奠定基础。

5.利用指针可以直接操纵内存地址,从而可以完成和汇编语言类似的工作。

6.让程序代码更加便利接触硬件控制等…

指针的缺点

指针可以操作任意内存,所以指针很灵活、很强大,但也引入了复杂性,并且申请内存之后需要手动释放如果不释放则会造成内存泄漏(可以使用智能指针来防止内存泄漏后面讲)。

指针的类型

我们都知道,变量有不同的类型,整型,浮点型,字符型等等。那指针有没有类型呢?其实是有的!比如

int *p;   //定义一个int 型指针变量变量名为p

举例:

int* p;      //可以指向一个int型变量或者一个int型数组
float* p;    //可以指向一个float型变量或者一个float型数组
double* p;   //可以指向一个double型变量或者一个double数组
char* p;     //可以指向一个char型变量或者一个char数组
void* p;     //可以指向任意变量使用时需要强制转换为该指针所指变量的类型
	

示例:

#include<stdio.h>
int main(void)
{
	int* int_p;         //可以指向一个int型变量或者一个int型数组
	int A = 10;
	int AA[10] = { 1,2,3,4,5,6,7,8,9,10 };
	int_p = &A;         //将int_p指向A的地址

	printf("A的值为:%d     A的地址为:%p   指针int_p指向A后     int_p所储存的地址为:%p    *p的内容为:%d    ", A, &A, int_p, *int_p);

	//当指针指向一个变量时可以通过 *int_p=666时,int_p所指向的那个变量的值也会等于666
	*int_p = 666;
	printf("*int_p=666 后 A的内容为:%d\n\n", A);

	//注意:AA和AA[0]意思一样!!
	int_p = AA;         //将int_p指向AA的首地址,相当于int_p=AA[0];      

	//注意:AA和AA[0]意思一样!!!
	printf("数组AA的值为:%d     数组AA的地址为:%p   指针int_p指向AA后     int_p所储存的地址为:%p    *int_p的内容为:%d\n\n", AA[0], &AA, int_p, *int_p);


	//重点:当p指向一个变量时   输出p表示 输出p所指向的 变量的地址(记住是:"变量的地址" ), 输出*p表示 输出p所指向的 变量的内容(记住是:"变量的内容" );
	
	printf("现在int_p已经指向了数组AA, 现在用*int_p的方式输出数组AA的内容  \n\n");
	printf("输出为:  ");
	for (int i = 0; i < 10; i++, int_p++)    //i是用来计数,    int_p++的意思为:每运行一次int_p就往后移动一个单位(这里单位的意思是:当指针为int型时单位就为4,当指针为double型时,一个单位就为8  );
	{
		printf("%d  ", *int_p);
	}

	//char指针与int 型指针类似,double和float和上面的例子中的int型指针用法几乎一模一样,下面讲解char和void型指针

	char ch = 'A';
	char* char_p;
	char_p = &ch;
	printf("\n\nch的值为:%c     ch的地址为:%p   指针char_p指向ch后     char_p所储存的地址为:%p    *char_p的内容为:%c\n\n", ch, &ch, char_p, *char_p);

	//char型指针也可以指向一个字符组

	char_p = (char *)"AAAA";   //有些编译器不需要强制类型转换,可以直接char_p="AAAA";


	//注意char型指针指向一个字符串输出时不需要像其他类型(例如:int float)一样在指针前加 * (如果char 型指针指向一个字符数组用*char_p进行输出 这是错的)
	printf("指针char_p指向一个字符串\"AAAA\"后     char_p所储存的地址为:%p    char_p的内容为:%s\n\n", char_p, char_p);   


	//void型指针赋值或输出指针所指的变量内容是都需要进行强制类型转换
	void* void_p;
	void_p =(char *) "AAAAA";
	printf("将\"AAAAA\"进行强制类型转换赋给void_p  例:void_p =(char *) \"AAAAA\";     (char *)void_p的输出结果为:%s\n\n", (char *)void_p);
	int Num = 666;

	void_p =(int *) &Num;

	printf("将int型变量Num进行强制类型转换赋给void_p  例:void_p =(int *) &Num;     *((int *)void_p)的输出结果为:%d      ", *((int *)void_p));
}

运行结果:
结果

数组和指针的区别与联系

这位大佬写的很好也很详细,这部分就看他的吧

指针与数组

指针与数组的关系 我们可以从下面这个方面入手。在C语言创建之初,很大一部分C语言的使用者是编译器的设计者,所以为了迎合这些人的口味儿,C语言做了很多特定的规则,这些规则后来在标准化的过程中被委员会采纳,成了语言标准的一部分。具体到指针与数组这一块儿,我们同样从两个方面来看,一是作为一个语言,数组是必须要支持的一种数组类型,原因很简单,数组是线性表的直接体现。而从编译器设计者的角度来看,如果为数组专门设计一套实现标准会非常繁杂(事实上,后来C++完成了这一任务,它就是标准库中的vector容器)。这一对矛盾最后以双方的相互妥协得以解决,而解决方法就是利用现有的指针来间接实现数组。然而,数组与指针终究不是一个东西,所以,语言在设计过程中就必须在二者之间和稀泥。到最后,就形成了这样一对令人头大的冤家。了解了这一背景,你就不会为他俩扑朔迷离的关系而感到惊讶了。

对于编译器而言,一个数组是一个地址,一个指针是一个地址的地址。

数组要么在静态存储区被创建(如全局数组),要么在栈上被创建。数组名对应着(而不是指向)一块内存,其地址与容量在生命期内保持不变,只有数组的内容可以改变。

//可以被更改的值叫左值,不可以被更改值的叫右值

void main(void)
{ 
	int a[10];
	a ++;//错误,a是左值,不能更改
}

指针可以随时指向任意类型的内存块, 远比数组灵活,但也更危险。下表是指针和数组一个简单的比较

数组和指针的优势

数组的优势

1.保存数据

2.直接访问数据

3.用于存储数目固定且类型相同的数据

4.编译器自动分配和删除

5.自身即为数据名

指针的优势

1.保存地址

2.间接访问数据,先取得指针的内容,然后以它为地址,取得数据

3.通常用于动态数据结构(如链表)

4.动态的分配和删除,相关函数为malloc()和free() C++中new和delete

5.通常指向隐式数据(如通过malloc或new动态分配的内存)

指针和数组都可以在初始化的时候赋予字符串常量。尽管看上去一样,底层机制却不同。指针在定义的时候,编译器并不会为指针所指向的对象分配内存空间,它只是分配指针变量的空间(32位的系统给指针分配的内存为4字节,64位的系统给指针分配的内存为8字节)。除非以一个字符串常量对其进行初始化。下面的定义创建了一个字符串常量(为其分配了内存空间)

char* char_p = "abcde";

在ANSI C中,初始化指针时所指向的字符串被定义为只读,如果想通过指针修改字符串的
时候,会产生未定义的行为(通常编译时就会报错)。

char型数组也可以用字符串常量进行初始化,但是其内容可以被修改。

内容的复制和比较

不能对数组进行字节复制和比较,对于两个char型数组a, b, 不能用b = a进行复制,而应当使用标准库函数strcpy()。也不能使用if(b == a)进行比较,应当使用strcmp()。

而对于指针p,如果要想将数组a中的内容复制,要先申请一块内存区域(malloc(strlen(a))+1),然后使用strcpy()进行拷贝。

指针与数组的区别: C语言把内存划分成四个区,它把一般的变量和数组等存在于内存中的栈区,所以数组在C语言的定义中只是一组同类型的普通变量,即使这个变量有可能是指针。所以他的作用比指针小的很多,而指针可以指向任何区的任何数据,所以就会觉得指针和数组名很像,但是必须要注意的是,数组名只是指针中的一种,它是指针中只指向栈区的且指针的移动范围是有限的,即数组长度。而且数组在定义之初就已经有了自己的内存,一般的指针如果未指向某一个内存块时,它是没有自己的内存的,即所谓的野指针。

联系:如上面所说,数组只是定义在栈区的一个连续变量,它的首地址就是一个指针。

总结:不仅数组有指针,所有变量都有指针,指针说白了就是内存中的地址,就像一个房间必须有一个房间号。在C/C++语言中定义一个指针,就是在栈区开辟一个内存空间用来存放它指向的内存地址,然后给指针赋值,就是把地址值赋值给刚才开辟的内存空间,然后通过访问该内存中的地址值来间接访问该地址下存放的数据。如果该地址值指向的是一块静态存储区,如字符串常量等,当然就不可以修改指向的内容啦。

数组指针

定义 int (*p)[n];
()优先级高,首先说明p是一个指针,指向一个整型的一维数组,这个一维数组的长度是n,也可以说是p的步长。也就是说执行p+1时,p要跨过n个整型数据的长度。
大小:一个int型指针长度的空间
如要将二维数组赋给一指针,应这样赋值:
int a[3][4];
int (*p)[4]; //该语句是定义一个数组指针,指向含4个元素的一维数组。
p=a; //将该二维数组的首地址赋给p,也就是a[0]或&a[0][0]
p++; //该语句执行过后,也就是p=p+1;p跨过行a[0][]指向了行a[1][]

所以数组指针也称指向一维数组的指针,也称行指针。

指针数组

定义 int p[n];
[]优先级高,先与p结合成为一个数组,再由int
说明这是一个整型指针数组,它有n个指针类型的数组元素。这里执行p+1时,则p指向下一个数组元素,这样赋值是错误的:p=a;因为p是个不可知的表示,只存在p[0]、p[1]、p[2]…p[n-1],而且它们分别是指针变量可以用来存放变量地址。但可以这样 p=a; 这里p表示指针数组第一个元素的值,a的首地址的值。
大小:n个int *的数据空间
如要将二维数组赋给一指针数组:
int *p[3];
int a[3][4];
p++; //该语句表示p数组指向下一个数组元素。注:此数组每一个元素都是一个指针
for(i=0;i<3;i++)
p[i]=a[i]
这里int *p[3] 表示一个一维数组内存放着三个指针变量,分别是p[0]、p[1]、p[2]
所以要分别赋值。

函数指针

什么是函数指针?

函数指针大家了解一下就行了,用得不多,但一定要认识它。
什么是函数指针

如果在程序中定义了一个函数,那么在编译时系统就会为这个函数代码分配一段存储空间,这段存储空间的首地址称为这个函数的地址。而且函数名表示的就是这个地址。既然是地址我们就可以定义一个指针变量来存放,这个指针变量就叫作函数指针变量,简称函数指针。

那么这个指针变量怎么定义呢?虽然同样是指向一个地址,但指向函数的指针变量同我们之前讲的指向变量的指针变量的定义方式是不同的。例如:
int(*p)(int, int);

这个语句就定义了一个指向函数的指针变量 p。首先它是一个指针变量,所以要有一个“*”,即 ( *p);

其次前面的 int 表示这个指针变量可以指向返回值类型为 int 型的函数;后面括号中的两个 int 表示这个指针变量可以指向有两个参数且都是 int 型的函数。所以合起来这个语句的意思就是:定义了一个指针变量 p,该指针变量可以指向返回值类型为 int 型,且有两个整型参数的函数。p 的类型为 int(*)(int,int)。

所以函数指针的定义方式为:
函数返回值类型 (* 指针变量名) (函数参数列表);

“函数返回值类型”表示该指针变量可以指向具有什么返回值类型的函数;“函数参数列表”表示该指针变量可以指向具有什么参数列表的函数。这个参数列表中只需要写函数的参数类型即可。

我们看到,函数指针的定义就是将“函数声明”中的“函数名”改成“(*指针变量名)”。但是这里需要注意的是:“(*指针变量名)”两端的括号不能省略,括号改变了运算符的优先级。如果省略了括号,就不是定义函数指针而是一个函数声明了,即声明了一个返回值类型为指针型的函数。

那么怎么判断一个指针变量是指向变量的指针变量还是指向函数的指针变量呢?首先看变量名前面有没有“”,如果有“”说明是指针变量;其次看变量名的后面有没有带有形参类型的圆括号,如果有就是指向函数的指针变量,即函数指针,如果没有就是指向变量的指针变量。

最后需要注意的是,指向函数的指针变量没有 ++ 和 – 运算。

注意:函数指针的类型一定要与所指函数的类型相同(函数的返回值),形参类型也要相同,当一个函数指针指向一个函数时就能像使用函数一样使用它。

示例:

#include<stdio.h>
int fun(int Num1, int Num2)
{
	return Num1 > Num2 ? Num1 : Num2;
}
int main(void)
{
	int (*p)(int, int);
	p = fun;
	printf("66和88中最大的是:%d ", p(66, 88));
	
	return 0;
}

运行结果如图:

在这里插入图片描述

动态数组

在实际编程中,有些数组的个数并不能确定,如果定义得数组过大,浪费内存空间,如果定义得 太小,又容纳不下。也就是说,用静态数组的方法很难解决。那么,如何解决这个问题呢? 因为数组名代表了该数组的首地址,也就是指针,如果定义一个指针表示数组名,之后将一段内存 空间的地址赋给该指针,就可以实现动态数组啦!
为了实现动态数组,必须能够动态申请内存空间。C语言提供了一些内存管理函数,这些函数可以 根据需要动态地申请内存空间,以实现动态数组的要求。

实现动态数组的相关函数

malloc()函数

分配若干个字节的空间,返回一个指向该区域的指针。若系统不能提供足够大的内存空间,或者初始的指向不明确,那就铁定返回NULL,当然了,设计指针问题错的太离谱了,直接弹出黄框框警告。
malloc()的原型为:
void malloc(unsigned int size),size表示向系统申请的空间大小,调用成功将返回一个指向void类型的指针void,常用来作为一个万金油指针,和函数里面求导类似,不管会不会,首先去求导。
指针本质是一个被存储着的地址,过去讲过内存中划分为了若干个区域,各种数据类型的值都可以分一块上去,这个区域近似于无限大,说这句话,就是要把所谓的short,int,char看作不同类型的地址,类似于不同的学区,其依托的划片学校不同。
废话这么多,还是要体会到,同种类型的指针才能彼此赋值。如果要调用malloc的返回值赋值给某个指针,应该先根据该指针的基类型,采用一种叫做强制类型转换的方式,将malloc返回的类型不明确指针,

malloc()函数的用法:
原型:void "malloc(unsigned size)

在内存的动态存储区分配n个长度为size的连续空间。
malloc()函数的返回值
申请成功,则返回新分配内存块的起始地址;否则,返回NULL。
注意: malloc()函数的返回值是一个无类型指针,可以指向任何类型的数据。在实际使用时,需将malloc()函 数的返回值强制转换成被赋值指针变量的数据类型,以免出错。

举个例子:

    int *p=NULL;
    scanf("%d",&n);
    p=(int *)malloc(n*sizeof(int));//申请n个sizeof个字节的存储空间,也叫做内存吧,注意这个强制类型转化!

free()函数
释放malloc分配的存储空间,原型为 void free(void *p),该函数的功能是是释放动态申请的由指针P指向的存储空间,该函数无返回值。free()中参数给出的地址是函数malloc申请空间时候返回的地址,free执行后,以前分配的由指针P指向的内存被清空。
Lp=(int *)malloc(sizeof(int));
free(Lp);
比方说这个组合就是一对的数值,成对出现,按照百度的解释,free让动态数据内存结构变的真的可以动态,否则就是个伪动态,只能申请,不能释放。
标准库中malloc函数的实现原理。要了解针对malloc的内存存储结构,malloc不像全局变量一样,不是在编译器编译的时候就会分配内存空间,而是在调用到malloc函数时才会分配空间。有时还会中途调用free函数释放空间出来。
malloc是从堆区申请空间,函数结束后不会自动释放,如果不人为地释放的话,要等到程序结束后,系统才会自动回收
malloc用于向系统申请一定数量的内存,如果你的程序一味的申请内存供自己使用,那么系统可分配给其它程序的内存就会减少,到最后无内存可用,最终导致系统运行缓慢,不稳定等问题。显然,这种只索取不奉献的行为是不可取的因此,C语言提供了free函数,用于向系统返还借用的内存。有借有还,再借不难。

free()函数的用法

原型: void free(void *ptr)
free()函数的功能
释放由ptr指向的内存块(ptr是调用malloc()函数的返回值)。
free()函数的返回值:无
注意: 使用malloc()函数申请的内存块,操作结束后应及时使用free()函数释放。尤其是循环使 用malloc()函数时,如果不及时释放不再使用的内存块,很可能很快就耗尽系统的内存资源,从而导致 程序无法继续运行。

示例:

//这里写个求平均分的代码
#include<stdio.h>
#include<stdlib.h>   //使用malloc和free需要包含此头文件
int main(void)
{
	int Num, i;
	float Sun = 0;
	float* Lp;
	printf("请输入学生人数:");
	scanf("%d", &Num);
	Lp = (float *)malloc(sizeof(float) * Num);
	if (Lp == NULL)
	{
		printf("申请内存失败!");
		return -1;
	}
	for (i = 0; i < Num; i++)
	{
		printf("请输入第%d名学生的成绩:", i + 1);
		scanf("%f", &Lp[i]);
	}

	for (i = 0; i < Num; i++)
	{
		Sun += Lp[i];
	}
	free(Lp);
	printf("同学的平均分为:%.2f 分", Sun / Num);
}

结果如同:
在这里插入图片描述

注意: 申请成功后可以像使用普通数组一样使用动态数组!

下面使用C++来实现动态数组(如果没有学过C++可以跳过)

new 和 delete 是 C++ 用于管理 堆内存 的两个运算符,对应于 C 语言中的 malloc 和 free,但是 malloc 和 free 是函数,new 和 delete 是运算符。除此之外,new 在申请内存的同时,还会调用对象的构造函数,而 malloc 只会申请内存;同样,delete 在释放内存之前,会调用对象的析构函数,而 free 只会释放内存。

new的用法

1.开辟单变量地址空间

使用new运算符时必须已知数据类型,new运算符会向系统堆区申请足够的存储空间,如果申请成功,就返回该内存块的首地址,如果申请不成功,则返回零值。

new运算符返回的是一个指向所分配类型变量(对象)的指针。对所创建的变量或对象,都是通过该指针来间接操作的,而动态创建的对象本身没有标识符名。

一般使用格式:
格式1:指针变量名=new 类型标识符;
格式2:指针变量名=new 类型标识符(初始值);
格式3:指针变量名=new 类型标识符 [内存单元个数];

说明:格式1和格式2都是申请分配某一数据类型所占字节数的内存空间;但是格式2在内存分配成功后,同时将一初值存放到该内存单元中;而格式3可同时分配若干个内存单元,相当于形成一个动态数组。例如:

1.new int;  //开辟一个存放整数的存储空间,返回一个指向该存储空间的地址。int *a = new int 即为将一个int类型的地址赋值给整型指针a
2.int *a = new int(5) 作用同上,但是同时将整数空间赋值为5

2.开辟数组空间

对于数组进行动态分配的格式为:
   指针变量名=new 类型名[下标表达式];
   delete [ ] 指向该数组的指针变量名;

两式中的方括号是非常重要的,两者必须配对使用,如果delete语句中少了方括号,因编译器认为该指针是指向数组第一个元素的指针,会产生回收不彻底的问题(只回收了第一个元素所占空间),加了方括号后就转化为指向数组的指针,回收整个数组。

delete []的方括号中不需要填数组元素数,系统自知。即使写了,编译器也忽略。

请注意“下标表达式”不必是常量表达式,即它的值不必在编译时确定,可以在运行时确定。

一维: int *a = new int[100];    //开辟一个大小为100的整型数组空间

二维: int **a = new int[5][6]

三维及其以上:依此类推.

一般用法: new 类型 (初值)

delete的用法

  1. 删除单变量地址空间

    int *a = new int;
    
    delete a;   //释放单个int的空间
    
  2. 删除数组空间

    int *a = new int[5];
    
    delete []a;    //释放int数组空间
    

三、使用注意事项

  1. new 和delete都是内建的操作符,语言本身所固定了,无法重新定制,想要定制new和delete的行为,徒劳无功的行为。

  2. 动态分配失败,则返回一个空指针(NULL),表示发生了异常,堆资源不足,分配失败。

  3. 指针删除与堆空间释放。删除一个指针p(delete p;)实际意思是删除了p所指的目标(变量或对象等),释放了它所占的堆空间,而不是删除p本身(指针p本身并没有撤销,它自己仍然存在,该指针所占内存空间并未释放),释放堆空间后,p成了空指针。

  4. 内存泄漏(memory leak)和重复释放。new与delete 是配对使用的, delete只能释放堆空间。如果new返回的指针值丢失,则所分配的堆空间无法回收,称内存泄漏,同一空间重复释放也是危险的,因为该空间可能已另分配,所以必须妥善保存new返回的指针,以保证不发生内存泄漏,也必须保证不会重复释放堆内存空间。

  5. 动态分配的变量或对象的生命期。我们也称堆空间为自由空间(free store),但必须记住释放该对象所占堆空间,并只能释放一次,在函数内建立,而在函数外释放,往往会出错。

  6. 要访问new所开辟的结构体空间,无法直接通过变量名进行,只能通过赋值的指针进行访问。

    用new和delete可以动态开辟和撤销地址空间。在编程序时,若用完一个变量(一般是暂时存储的数据),下次需要再用,但却又想省去重新初始化的功夫,可以在每次开始使用时开辟一个空间,在用完后撤销它。

下面使用new和delete来实现动态数组:

//这里写个求平均分的代码
#include<iostream>
using namespace std;
int main(void)
{
	int Num, i;
	float Sun = 0;
	float* Lp;
	cout << "请输入学生人数:";
	cin >> Num;
	Lp = new float[Num];
	if (Lp == NULL)
	{
		printf("申请内存失败!");
		return -1;
	}

	for (i = 0; i < Num; i++)
	{
		cout<<"请输入第"<<i+1<<"名学生的成绩:";
		cin >> Lp[i];
	}

	for (i = 0; i < Num; i++)
	{
		Sun += Lp[i];
	}
	cout.precision(4);
	cout << "同学的平均分为:" << Sun / Num << " 分" << endl;
}

运行结果如图:
在这里插入图片描述
本篇到此结束。如果您发现本篇中有什么错误的地方麻烦您在评论区留言,谢谢!
如果遇到什么问题欢迎大家进群讨论或者加我qq
群内有各种学习资料,欢迎大家一起来学习!
本人qq:846581636
qq学习群:759252814
期待你的关注
感谢大家的支持,谢谢!

  • 7
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值