04.C语言学习2.0

C语言学习2.0

1、C语言的语句语法

三种基本结构:分支结构、循环结构、顺序结构

1、条件分支结构:

if(){ . . . } else if { . . .} else { . . .}

注:1、在if 后面加入一个,条件判断结束;

​ 2、if 条件判断内容易将 == 写成赋值 = ;

​ 3、if 条件判断执行的命令只有一行;

多条件分支结构:switch case ;switch判断的是整型;

switch (a)
{
    case 1:
    case 2:
    case 3:
    //......
    default:
}
switch (a)
{
    case 1:
            printf("is 1!\n");
    case 2:
            printf("is 2!\n");break;
    case 3:
            printf("is 3!\n");break;
    default:
            break;
}//输出结果is 1! is 2!

注:1、char 和 int 都是可以进行 swtich 条件判断

​ 2、每一个分支结构(case) 后面要接 break 否则会一直执行后面的case直到跳出

2、循环结构

for(初始条件;循环条件判断;循环步长)
{

}
#include<stdio.h>
int main()
{
    for(int i=1;i<=100;i++)
        if(i % 2 == 0)
            printf("%d",i);
    printf("hello");
    return 0;
}

注:1、for 循环作用范围只有一条命令

​ 2、如果for 没有条件判断就会进入死循环,此时用break(作用:直接跳出当前结构)跳出循环;

while()//循环条件判断
{
//......
}
int i=100;
while(i)
{
        printf("%d",i);
        i--;
}
return 0;
do
{
//执行内容
}while(条件判断);

注:do{ …} while (); 无论满不满足循环条件都会执行一遍内容

int i=0;
for(;i;)
{
	printf("for output!\n");
}
while(i)
{
    printf("while output!\n");
}    
do
{
    printf("output!\n");
}while(i);
return 0;
//输出结果为:output!只有do while执行了一次

C语言随堂测试题:

#include<stdio.h>
#include<math.h>
//输出100-999内所有水仙花数
int main()
{
    int i,units,hundreds,decade;
    for(i=100;i<999;i++)
    {
        units=i/100;
        decade=i / 10 %10;
        hundreds=i%10;
        if((pow(units,3)+pow(hundreds,3)+pow(decade,3))==i){
            printf("%d is narcissus number",i);
        }
    }
    return 0;
}
#include<stdio.h>
int main()
{
    for(int row=1;row<=9;row++){
        for(int column=1;column<=row;column++){
            printf("%d * %d = %d",column,row,cloumn*row);
        }
        printf("\n");
    }
    return 0;
}//输出99乘法表

3、顺序结构

按照事物本身的特性,必须一个接一个来完成。

2、函数

1、简单介绍

作用:封装重复使用的代码

//返回值 函数名(传递的参数)
//{
//    函数主体
//    return 和返回值类型相同的数
//}
//验证一个数是否满足哥德巴赫猜想:任意>6的数可以拆分成两个素数的和
#include<stdio.h>
#include<math.h>
//判断一个数是否为素数
int IsPrime(int a)
{
    if(a==2) return 1;
    for(i=2;i<= sqrt(a);i++)
    {
        if(a%i==0)
            return 0;
    }
    return 1;
}
int main()
{
    int a;
    scanf("%d",&a);
    if(a<6)//输出一个大于6的数
    {
        printf("Error Input!Please input again!");
        return 0;
    }
    for(int i=1;i<a/2;i++)
    {
        if(IsPrime(i)==1 && IsPrime(a-i)==1)//判断是否存在两个素数
        {
            printf("first: %d\n second : %d\n",i,a-i);
        }
	}
    return 0;
}

2、宏:

define的应用:

1、宏定义:方便替换

#include<stdio.h>
#define  ABS(x) (x)<0? (-x):(x)
#define Max(a,b) (a>b)? (a):(b)
#define M(x,y,z) x*y+z
int main()
{
    printf("|-1| = %d\n",ABS(-1));
    printf("Max : %d\n",Max(3,2));
    int a=1,b=2,c=3;
    printf("%d\n",M(a+b,b+c,a+c));
    return 0;
}//运行结果为:|-1| = 1  Max : 3  12
//最后一个算式在替换的时候由我们想得到的(a+b)*(b+c)+(a+c)变成了a+a*b+c+a+c所以得出的结果不理想

2、条件编译:方便跨平台开发

//1.h 定义一些宏加一些宏判断,防止头文件重复包含
#ifndef __MYHEAD_H__
#define __MYHEAD_H__

#define LOG(log) Write_log(__FILE__,__func__,__LINE__,log)
void Write_log(char *FileName,char *FuncName,int line,char *log);
#endif

3、宏函数

用宏定义的函数,在某些时候能够代替函数的作用;函数式宏能是程序的运行速度稍微提高一点儿,但是当函数中有大量的宏替换的时候,又会使得程序变得臃肿。

#include<stdio.h>
#include"1.h"
void Write_log(char *FileName,char *FuncName,int line,char *log)
{
    printf("FileName:%s\n FuncName:%s\n Line:%d\n Log:%s\n",FileName,FuncName,line,log);//输出文件名,函数名,当前行数,日志
}
#include<stdio.h>
#include"1.h"
#include<math.h>
int main()
{
    // printf("%s\n",__FILE__);//所在文件名
    // printf("%s\n",__DATE__);//日期
    // printf("%s\n",__TIME__);//时间
    // printf("%d\n",__LINE__);//行数
    // printf("%s\n",__func__);//所在函数名称
	Write_log(__FILE__,__func__,__LINE__,"this is a test log");
    int i=0;int j=0;
    LOG("Zero error!\n");
}

单目运算符:a++ b–

双目运算符:+ - * / %

三目运算符:?‘yes’:‘No’

注:1、宏定义只做替换不做计算,所以在用宏函数的时候不加注意就会发生错误;

​ 2、在定义函数式宏的时候与一定要每个参数以及整个表达式都用()括起来;

​ 3、一行定义不下,\表示下一行还有内容

ps:x & (x-1) 能够将二进制中的1全部变成0

3、回调函数

回调函数是一种特殊的函数,它把函数作为参数传递给另一个函数,并在被调用函数执行完毕后被调用。

决定什么时候调用这个函数,但是并不会关心这个函数具体干什么,降低函数与被调函数之间的关联,把部分函数功能交由别的函数去完成,使函数与部分功能取消强绑定。

回调函数的作用是将代码逻辑分离出来,使得代码更加模块化和可维护。使用回调函数可以避免阻塞程序的运行,提高程序的性能和效率。另外,回调函数还可以实现代码的复用,因为它们可以被多个地方调用。

优点:

  1. 提高代码的复用性和灵活性:回调函数可以将一个函数作为参数传递给另一个函数,从而实现模块化编程,提高代码的复用性和灵活性。
  2. 解耦合:回调函数可以将不同模块之间的关系解耦,使得代码更易于维护和扩展。
  3. 可以异步执行:回调函数可以在异步操作完成后被执行,这样避免了阻塞线程,提高应用程序的效率。

回调函数的应用:

#include<stdio.h>
int add(int a,int b)
{
    return a+b;
}
void Write_to_Client1(const char *str)//发送给客户1
{
    printf("write to Client1 info: %s\n",str);
}
void Write_to_Client2(const char *str)//发送给客户2
{
    printf("write to Client2 info: %s\n",str);
}
void Func(void (*ptr)(const char *str))//传递发送字符,不指定送给谁
{
    const char *s ="hello";
    ptr(s);
}
int main()
{
    Func(Write_to_Client1);//在main中指定送给谁
    Func(Write_to_Client2);
    return 0;
}

4、应用一:交换两个变量的值

方法一、多变量

int swap(int a,int b)
{
    int c;
    c=a;
    a=b;
    b=c;
    printf("a : %d\n b : %d\n",a,b);
}

方法二、数轴法

int swap2(int a,int b)
{
    a=a-b;
	b=a+b;
	a=b-a;
	printf("a1 : %d\n b1 : %d\n",a,b);
}

但是数据在相减的时候在超出一定范围之后,无法保证两个数依然有效

方法三、位操作:所有的数在计算机里存储都是补码形式存在,所以~-1=0;-1原码为:1000 0001 补码为1111 1111 对其取反为0000 0000 所以对-1取反为0;对0取反为-1;

取反操作和去反码操作不同点:看是否带符号位运算,带符号位的为取反操作,不带符号位的为去反码操作。

char a[10] = {0};
while(~scanf("%s",&a))//代表正常输出能够进入循环,非法操作后跳出循环
{
    printf("%s\n",a)
}

^异或:相异为1,相同为0;

int swap3(int a,int b)
{
    a=a^b;
	b=a^b;
	a=a^b;
	printf("a2 : %d b2 : %d\n",a,b);
}
3、头文件重定义问题

在main.c 引用多个头文件时可能出现重定义的问题,如在main.c 中引用1.h和2.h 时,如果2.h中包含1.h,则main.c在预处理展开的时候会出现包含两个1.h的内容会出现重定义的问题;为了避免这种问题则在头文件中加入条件判断,这个判断要在每一个头文件中都要写;

//头文件预判断
#ifndef __MYHEAD_H__
#define __MYHEAD_H__
//真正头文件内容
#endif
4、指针
#include<stdio.h>
int swap(int a,int b)
{
    int c;
    c=a;
    a=b;
    b=c;
    printf("a : %d\n b : %d\n",a,b);
}
int main()
{
    int a=2,b=3;
    swap(a,b);
    printf("a1 : %d\n b1 : %d\n",a,b);
    return 0;
}//此时输出结果为 a:3 b:2	a1:2 b1:3主函数体的数值并没有发生改变

函数体调用并不会影响主函数的取值,所以引入指针指向数据的地址从而实现数据的互换。

指针 &:取地址 *:取值符号

#include<stdio.h>
int swap(int *a,int *b)
{
    int c;
    c = *a;
    *a = *b;
    *b = c;
    printf("*a : %d\n *b : %d\n",*a,*b);
}
int main()
{
    int a=2,b=3;
    int *ptr_a = &a;
    int *ptr_b = &b;
    swap(ptr_a,ptr_b);
    printf("a : %d\n b: %d\n",a,b);
    return 0;
}//输出结果为:*a=3,*b=2;a=3,b=2;因为函数传递的是值,指针传递的是地址
const int * ptr_a=&a;//变量指向的值不能修改	常量指针
int * const p=&a;//变量指向的地址不能修改	指针常量

2、函数指针

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

void (*p)(int,int);指向这一类void ()(int ,int)的函数指针

#include<stdio.h>
int add(int a,int b)
{
    return a+b;
}
int sub(int a,int b)
{
    return a-b;
}
int main()
{
    int (*p)(int,int)=NULL;
    p=add;
    printf("%d\n",p(1,2));
    p=sub;
    printf("%d\n",p(1,2));
    return 0;
}

封装:将数据或函数等集合在一个个的单元中(我们称之为类)。被封装的对象通常被称为抽象数据类型。封装解决了函数重名等问题;

优点:避免了函数体重名问题;可以由结构体成员直接调用对象.实现函数体的调用

缺点:1、极大地增加了结构体的内存损耗;

注意:指针函数:返回值为指针的函数 int * p(int,int)

函数指针函数:把函数指针作为返回值的函数

//函数指针函数使用场景:实现公式解析
#include<stdio.h>
typedef int (*Mathfunc)(int,int);
int add(int a,int b)
{
    return a+b;
}
int sub(int a,int b)
{
    return a-b;
}
Mathfunc GetMathFunc(char Symbol)
{
    switch(Symbol)
    {
        case '+': return add;
        case '-': return sub;
        default:
            return NULL;
    }
}
int main()
{
    char Symbol = '-';
    Mathfunc func = GetMathFunc(Symbol);
    if(func!=NULL)
        printf("%d\n",func(1,2));
    return 0;
}
5、数组

1、数组的定义:

一组相同数据类型的数据元素的集合,地址是连续的;int a[10]

注意:数组的大小一开始就要确定,不能使用变量;int b=5;int a[b]={0};非法输入

2、数组的初始化:

int a[]={1,2,3,4,5,6,7};int b[10]={1,2,3,4,5,6,7,8,9};

数组作为函数参数被传递的时候会被弱化成指针,无法知道具体大小,所以同时要加入数组的长度;但是字符数组除外,它作为字符串传入时,以’\0’结束。

#include<stdio.h>
#define ArrayLen(a) (sizeof(a))/sizeof(a[0])

void print(int *a,int len)
{
    for(int i=0;i<len;i++)
    {
        printf("%d ",a[i]);
    }
    printf("\n");
}
int main()
{
    int a[]={1,2,3,4,5,6,7,8};
    print(a,Arraylen(a));
    return 0;
}

&a : 整个数组的首地址,一个步长跨整个数组

&a[0]:数组首元素的地址,一个步长跨一个元素的长度

a:数组首元素的起始地址,一个步长跨一个元素的长度

#include<stdio.h>
int main()
{
    int a[4]={1,2,3,4};
    printf("%x ; %x ; %x \n",&a,&a[0],a);
    printf("%x ; %x ; %x \n",&a+1,&a[0]+1,a+1);
    return 0;
}//输出结果为
//65d025b0 ; 65d025b0 ; 65d025b0
//65d025c0 ; 65d025b4 ; 65d025b4
int a[4]={2016,2017,2018,2019};
int *ptr1=(int*)(&a+1);
int *ptr2=(int*)((int)a+1);
printf("%x,%x",ptr1[-1],*ptr2);
//输出ptr1[-1]为整个数组最后一个元素的起始地址,*ptr2为首元素内地址+1
#include<stdio.h>
int main()
{
    char arr[]={'a','b','c','d'};
    printf("%d\n",sizeof(arr));//输出整个数组大小:4个字节
    printf("%d\n",*(arr+0));//arr表示数组首元素地址,对其跨0个元素大小再取值,输出为'b'的大小:98
    printf("%d\n",sizeof(*arr));//输出为'a'的大小:1个字节
    printf("%d\n",sizeof(arr[1]));//输出第一个元素的大小:1个字节
    printf("%d\n",sizeof(&arr));//输出为整个数组首地址的大小,为8个字节
    printf("%d\n",sizeof(&arr+1));//输出为首个元素跨一个元素的地址的大小:8个字节
    printf("%d\n",sizeof(&arr[0]+1));//输出为首个元素跨一个元素的地址的大小:8个字节
}
  • 课堂测试一:

用数组输出2^10000超大整型数:模拟计算机计算的过程,将数组每一位看作位数,当超过10的时候向前进一位,前面一位+1,自己减10;直至结束;

#include<stdio.h>
int main()
{
    int a[100]={0};
    a[0]=1;
    for(int i=0;i<10000;i++)
    {
        for(int j=0;j<100;j++)
        {
            a[j]=a[j]*2;
        }
        for(int j=0;j<99;j++)
        {
            if(a[j]>=10)
            {
                a[j+1]=a[j+1]+1;
                a[j]=a[j]-10;
            }
        }
    }
    int flag=0;
	for(int i=99;i>=0;i--)
    {
        if(a[i]!=0)
        {
            flag =1;
        }
        if(flag==1)
        {
            printf("%d",a[i]);
        }
    }
    printf("\n");
    return 0;
}

课后习题:123456789 * 987654321 = ?

  • 课堂测试二:

    输出斐波那契数列:1 1 2 3 5 8 13 21

#include<stdio.h>
int main()
{
    int i;
    long a[50]={0};
    a[0]=1;a[1]=1;
    for(i=1;i<49;i++)
    {
        a[i+1]=a[i]+a[i-1];
    }
    for(i=0;i<50;i++)
    {
        printf("%ld\t",a[i]);
    }
    return 0;
}

**2、字符数组:**用来存放字符的数组

1、定义:

字符数组实际上是一系列字符的集合,也就是字符串(String)。在 C 语言中,没有专门的字符串变量,没有 string 类型,通常就用一个字符数组来存放一个字符串。在 C 语言中,字符串总是以’\0’作为结尾,所以’\0’也被称为字符串结束标志,或者字符串结束符。

2、字符数组的初始化:

  • 通过字符数组直接初始化 (直接把数组元素赋值给数组名是不行的)

    char str[10]="China";

    错误表达:char str[10]; str="string";

  • 使用strcpy函数进行初始化

    char str1[10]; str2[]="string";strcpy(str1,str2);

  • 通过指针赋值

    char *string="I love China";

3、string.h头文件所包含的常见的函数:

(1)、strlen:输出字符串的长度;

sizeof strlen的区别:

sizeof记录的是数组所占内存大小包括’\0’ ; sizeof是一个单目运算符,它的参数可以是数组、指针、类型、对象、函数等。

strlen记录的是字符串的长度;是函数

//自定义函数实现strlen的功能
int MyStrlen(char *s)
{
    int count=0;
    while(*s != '\0')
    {
        count++;
        s++;
    }
    return count;
}

(2)、strcmp(a,b):比较字符串是否相同,相同返回0,不同返回1

//自定义函数实现strcmp的功能
int MyStrCmp(char *s1,char *s2)
{
    if(MyStrlen(s1)!=MyStrlen(s2))//字符串的长度不相等直接说明两个字符串不同
        return 0;
    while(*s1!='\0' && *s2!='\0')
    {
        if(*s1!=*s2)//判断字符串的值是否相等
        {
            return 0;
        }
        s1++;
        s2++;
    }
    return 1;
}

(3)、strncmp(a,b,n):比较字符串前n个字符是否相同

(4)、strcpy(目标字符串,要被拷贝的字符串):复制拷贝字符串

//自定义函数实现strcpy的功能
void MyStrCpy(char *dest,char *src)
{
    if(MyStrlen(src)==0)//判断字符串长度是否为0,为0则无法赋值
        printf("string is NULL!error copy!\n");
    while(*src!='\0')
    {
        *dest = *src;
        dest++;
        src++;
    }
    *dest = '\0';
}

(5)、strncpy(目标字符串,要被拷贝的字符串,n):复制拷贝前n个字符

(6)、strcat(a,b):字符串的拼接,将后面的字符串拼接到前面一个字符串

(7)、strncat(a,b,n):n代表要拼接的长度,拼接n个字节长度的字符串

(8)、strstr(a,b):字符串匹配;返回值为指针,返回匹配成功的字符在原字符串中的地址

  • 课堂测试三:

    实现倒序输出字符串eg: i am from nanjing------>nanjing from am i

//思想:先将整条语句倒叙--->gnijnan morf ma i,再将每个单词倒叙
#include<stdio.h>
#include<string.h>
//倒序输出整句话
void Exchange(char *s,int len)
{
    char temp;
    for(int i=0;i<len/2;i++)
    {
        temp = s[i];
        s[i] = s[len-i-1];
        s[len-i-1] = temp;
    }
}
//倒序输出单词
void Exchange2(char *s)
{
    int len=0;
    for(int i=0;i<strlen(s);i++)
    {
        if(*(s+i)== ' '||*(s+i)=='\0')
        {
            Exchange((s+i-len),len);
            len=0;
		}
        else{
            len++;
        }
    }
}
int main()
{
    int a[]="I am from NanJing";
    Exchange(a,strlen(a));
    printf("%s\n",a);
    Exchange2(s);
    printf("%s\n",s);
    return 0;
}
  • 课堂测试四:

    将字符型转换成整数型,不能使用其他函数

#include<stdio.h>
int atoi(char *s)
{
    int result =0;
    int sign=1;
    while(*s!='\0')//判断字符串是否结束
    {
        if(*s=='-')
            sign = -1;//符号位为负数
        if(*s<='9' && *s>='0')//判断字符串元素是否处于9~0
        {
            result = result*10 +*s -'0';
        }
        s++;
    }
    return result*sign;
}
int main()
{
    char s[]="100";
    int a=atoi(s);
    printf("%d\n",a);
    return 0;
}

实现封装string里面的函数,创建一个结构体要先对其进行初始化

//MyString.h实现初始化和打印输出
#ifndef __MYSTRING_H_
#define __MYSTRING_H_
#define Max 1024
typedef struct String
{
    char string[Max];
    int size;
}MyString;

void Initalize(MyString *obj,const char *str);//不允许指针修改文字常量区的内容
void Print(MyString *obj);
int IsEqual(MyString *obj1,MyString *obj2);
int IsContains(MyString *dest,MyString *src);
void RemoveString(MyString *obj,const char *str);
void InsertString(MyString *dest,const char *str,int index);

#endif
//MyString.c
#include"MyString.h"
#include<string.h>
#include<stdio.h>
void Initialize(MyString *obj,const char *str)
{
    strcpy(obj->string,str);
    obj->size=strlen(str);
}
void Print(MyString *obj)
{
    printf("%s\n",obj->string)}
int IsEqual(MyString *obj1,MyString *obj2)
{
    if(strcpm(obj1->string,obj2->string)==o)
        return 1;
    else
        return 0;
}
int IsContains(MyString *dest,MyString *src)
{
    char *str=strstr(dest->string,src->string);
    if(str == NULL)
    {
        return 0;
    }
    else
    {
        return 1;
    }
}
void RemoveString(MyString *obj,const char *str)
{
    char *RMstr = strstr(dest->string,str);
    if(RMstr == NULL)
    {
        return;
    }
    else
    {
        char *destination = RMstr +strlen(str);//RMstr为指针,指向被查找字符串内匹配到的字符串的起始位置,向右移匹配到的字符串的长度
        while(*destination!='\0')
        {
            *RMstr = *destination;
            RMstr++;
            destination++;
        }
        *RMstr = '\0';
    }
}
void InsertString(MyString *dest,const char *str,int index)
{
    if(index < 0 ||index > dest->size)
        return 0;
    char new_str[Max]={0};
    strncpy(new_str,dest->string,index);
    strncpy(new_str+index,str,strlen(str));
    strcpy(new_str+index+strlen(str),dest->string+index);
    strcpy(dest->string,new_str);
    dest->size=strlen(dest->string);
}
//main.c
#include<stdio.h>
#include"MyString.h"
int main()
{
    MyString obj;
    Initialize(&obj,"helloworld");
    Print(&obj);
    
    MyString obj1;
    Initialize(&obj1,"helloword");
    
    MyString obj2;
    Initialize(&obj2,"owor");
    
    if(IsEqual(&obj,&obj1)==1)
    {
        printf("Two strings are the same!\n");
    }
    else
    {
        printf("Two strings are different!\n");
    }
    
    if(IsContains(&obj1,&obj2)==1)
    {
        printf("Is cantains!\n");
    }
    else
    {
        printf("Is not cantains!\n");
    }
    
    InsertString(obj1,"DEFINE",1);
    Print(&obj);
    return 0;
}

对其进行修改:将函数封装进结构体,可以实现直接使用 变量名.函数名 实现对函数的调用,但是初始化函数不能封装,而且初始化函数要对这些函数进行初始化。

//Mystring.h做出的修改
#ifndef __MYSTRING_H_//条件编译,避免头文件重复包含
#define __MYSTRING_H_
#define Max 1024

#define Init_MyString(obj,str) MyString obj;Initialize(&obj,str)
typedef struct String MyString;
struct  String
{
    char string[Max];
    int size;//数组长度,用空间换时间,用一块内存存储字符串的长度,方便下一次调用

    void (*print)(MyString *obj);//输出打印字符串
    int (*isEqual)(MyString *obj1,MyString *obj2);//判断字符串是否相等
    int (*isContains)(MyString *dest,MyString *scr);//判断是否包含
    int (*StrintSize)(MyString *obj);//判断字符串的大小
    void (*removeString)(MyString *dest,const char *str);//删除指定字符串
    void (*insertString)(MyString *dest,const char *str,int index);//在指定位置插入字符串
};

void Initialize(MyString *obj,const char *str);

#endif
//在Mystring.c中做出的修改
#include "MyString.h"
#include<string.h>
#include<stdio.h>
//前置声明
void Print(MyString *obj);
int IsEqual(MyString *obj1,MyString *obj2);
int IsContains(MyString *dest,MyString *src);
int Size(MyString *obj);
void RemoveString(MyString *dest,const char *str);
void InsertString(MyString *dest,const char *str,int index);

//对结构体定义的变量和函数指针进行初始化
void Initialize(MyString *obj, const char *str)
{
    strcpy(obj->string,str);
    obj->size = strlen(str);

    //func Init
    obj->print = Print;
    obj->isContains = IsContains;
    obj->isEqual =IsEqual;
    obj->StrintSize = Size;
    obj->insertString = InsertString;
    obj->removeString = RemoveString;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值