C语言-函数

参考

https://cplusplus.com/reference/clibrary/

1. 概念

函数必须先定义后调用,所以main要放在最后
函数不能嵌套定义,没有闭包函数的概念

1.1 基础概念

1.1.1 形参

形参变量只有在函数被调用时才会分配内存,调用结束后,立即释放内存,所以形参变量之后在函数内部有效
中分配内存

1.2.1 实参

实参可以是常量、变量、表达式(表达式必须有返回值)、函数等,无论实参是何种类型的数据,在调用函数时都必须有确定的值
实参和形参在数量上、类型上,顺序上必须严格一致,可以自动进行类型转换
实参、形参可以同名,但他们之间是相互独立的,互不影响。(实参在函数外部有效,形参在函数内部有效)

1.3.1 函数返回值

有返回值 typename func(){return val};
无返回值 void func(){}; 可以不写return

1.3.1 回调

函数f1调用函数f2的时候,函数f1通过参数给函数f2传递了另一个函数f3的指针,在函数f2执行的过程中,函数f2调用了函数f3,这个过程就叫做callback
这个先被当做指针传入,后面又被回调的函数f3就是回调函数

1.4.1 函数声明

type func(type1 param1,type2 param2);
type func(type1,type2); // 声明时可以不写形参,只写数据类型

1.4.1.1 多文件定义

函数声明写在头文件中,链接阶段 找到函数体
一般函数都放在源文件中,这些源文件都已经提前编译好了,并以静态链接库或动态链接库的形式存在

1.2 变量

数据是放在内存中的,变量(以及指针、引用)是给这块内存起的名字,有了变量就可以找到并使用这份数据

1.2.1 左值/右值

左值:指向内存位置的表达式, 叫做左值表达式(存储在内存中,有明确存储地址(可寻址)的数据)
右值:内存中某些地址的数值(可以提供数据值的数据(不一定可以寻址,例如存储于寄存器中的数据))

1.2.2 全局变量

全局变量存储在内存分区中的全局数据区,这个区域中的数据在程序载入内存后被初始化为0,也就是全局变量默认的初始值为0
全局变量修改后,会影响其他函数
全局变量的作用范围不是从变量定义到该文件结束,在其他文件中也有效

  • static

静态变量,仅限于当前文件内部调用
内部链接

  • extern

外部存储变量,用于声明在当前文件中将要用到的其他文件中的变量
外部链接

1.2.3 局部变量

局部变量根据C++标准的不同,可能是随机值,也可能是0
main()函数中定义的变量也是局部变量
空链接

  • auto

自动变量,离开定义的函数立即消失

  • register

寄存器变量,离开定义的函数立即消失

  • static

静态变量,离开定义的函数仍然存在

1.2.4 作用域

全局变量和局部变量
全局变量:的作用域是全部的文件,包括.c和.h文件
全局变量的作用范围不是从变量定义到该文件结束,在其他文件中也有效

{code...} 单独的代码块也是一个作用域

static 全局变量 作用域变成了当前文件

for,while会引入新的作用域,(块级作用域,C/cpp的特性)

注意!!!

int main()
{
    printf("%d\n",c); // 这里是访问不到全局变量c的
}

int c=10;

注意2

#include <iostream>
using namespace std;

int a;

void func()
{
	a = 10;
}

int main()
{
	std::cout << a << endl; // 0
	func();
	std::cout << a << endl; // 10
	return 0;
}

1.3 常量

  • 数值常量
  • 字符常量/字符串常量
  • 枚举常量

常量可以是任何基本的数据类型,可以理解为字面量
常量的值在定义后 不能进行修改

const 修饰的变量,也可以是常量,实际上是只读变量(不能用于switch case)

定义常量

  • #define 预处理器
  • const 关键字
// #define
#define Variable value // 注意没有分号 可以定义字符型、字符串型的常量

// const
const type variable=value; // 注意有分号,且必须直接赋值

1.4 存储类进阶

https://www.runoob.com/cprogramming/c-storage-classes.html

作用域:块作用域、文件作用域
连接:内部链接、外部链接、空链接
存储时期:静态存储时期、动态存储时期

块作用域:局部变量
文件作用域:全局变量

空链接+块作用域:局部变量
外部链接+文件作用域:全局变量 (其他文件也可以使用)
内部链接+文件作用域:静态全局变量 (只在当前文件使用)

具有文件作用域的变量 --> 具有静态存储时期 (全局变量、静态全局变量)
具有块作用域的静态变量 --> 具有静态存储时期 (静态局部变量)
具有块作用域的普通变量 --> 具有动态存储时期 (普通变量)

static 表明的是内部链接,而不是表明存储时期

// main.cpp
int a=1; // 外部链接+文件作用域:全局变量 (其他文件也可以使用)
static b=2; // 内部链接+文件作用域:静态全局变量 (只在当前文件使用)

int main()
{
	int c=3; // 空链接+块作用域:局部变量
}
  • 自动存储类 (普通局部变量) (块作用域、空链接)
  • 寄存器存储类 (register 局部变量) (块作用域、空链接)
  • 具有块作用域的静态存储类 (static 局部变量)(块作用域、内部链接)
  • 具有外部链接的静态存储类 (普通全局变量)(文件作用域、外部链接)
  • 具有内部链接的静态存储类 (static 全局变量)(文件作用域、内部链接)

1.4.1 初始化注意

https://www.cnblogs.com/iBoundary/p/15014843.html
https://blog.csdn.net/weixin_43609874/article/details/123903650
https://blog.csdn.net/u014552102/article/details/126494557

c语言中,全局变量、静态变量(全局静态变量、局部静态变量)不能用变量赋值,只能用常量赋值
注意:是在C语言中,在C++中是可以的!!!

例子1

int a=10;
int b=a; // initializer element is not constant 表达式必须含有常量值

int main()
{
    return 0;
}

例子2

int a=10;
static int b=a; // initializer element is not constant 表达式必须含有常量值

int main()
{
    return 0;
}

例子3

#include <stdio.h>
#include <stdlib.h>

int main()
{
    int a=10;
    static int b=a; // ERROR

    printf("%d\n",b);
    return 0;
}

例子4

下面这个是对的,&a 是常量,a是变量

#include <stdio.h>
#include <stdlib.h>

int a=10;
int *b=&a; // initializer element is not constant 表达式必须含有常量值

int main()
{
    printf("%d\n",*b);
    return 0;
}

2. 赋值与参数引用机制

形参-实参相当于赋值操作

2.1 赋值

不同于下面的参数引用机制(地址传递)

对于数值型

赋值修改,并不会改变内存地址
inplace 操作不会改变内存地址(inplace操作也包括自增自减)

#include <stdio.h>
int main()
{
    int a=1;
    printf("%p\n",&a); // 000000000061FE1C
    a=100;
    printf("%p\n",&a); // 000000000061FE1C,地址不变
	
	a+=100
	printf("%p\n",&a); // 000000000061FE1C,地址不变
	
    return 0;
}

2.2 参数引用机制/copy

形参与函数内的其他局部变量一样,在进入函数时被创建,退出函数时被销毁

函数内的局部参数、形参,只有在 上分配内存,也就是在函数声明和定义的时候参数没有被创建

2.2.1 值传递

把实际的值复制给函数的形参,不共享内存

值传递只传值 地址互相不干扰、永远不变

#include <stdio.h>
int main()
{
    int a=20;
    int b=10;
    int c=a;
    c+=1;
    printf("a %d\n",a); // 20
    printf("c %d\n",c); // 21
    printf("a %d\n",a); // 20

    return 0;
}

进阶

想当于复制,内存地址不一样
且修改B不会影响A

#include <stdio.h>
int main()
{
    int a=20;
    int b=10;
    int c=a;
    printf("%p\n",&a); // 000000000061FE18
    printf("%p\n",&c); // 000000000061FE14
    c+=1;
    printf("a %p\n",&a); // 000000000061FE18
    printf("c %p\n",&c); // 000000000061FE14

    return 0;
}

2.2.2 引用传递 (地址传递、指针传递)

形参为指向 实参地址 的指针,当对 形参的指向操作时,就相当于对 实参 本身进行的操作

通过指针传递地址到函数,实现在函数内部操作函数外部的数据
数组、字符串、动态分配的内存,不能通过一个参数直接传递,也就是不能进行值传递,必须通过地址指针传递,也不能进行引用传递
将指针作为形参,地址作为实参进行传递

#include <stdio.h>
#include <string.h>

// 要是返回一个指针,则要用dataType* 的方式进行定义
char *strlong(char* s1,char* s2){
    if (strlen(s1)>=strlen(s2)){
        return s1;
    }
    else{
        return s2;    
    }
}

int main(){
    char s1[30]="hello";
    char s2[30]="world";
    char *s3;
    s3="ex";

    s3=strlong(s1,s2);
    printf("%s\n",s3);

    return 0;
}

注意事项: 地址传递的地址(地址)依然是副本(深拷贝、地址传递)
https://blog.csdn.net/llm_hao/article/details/108432323 进阶例子,看算法与数据结构dsa-单链表
通过地址(指针)传递可以修改指向的内容,但是指针本身是值传递
(当把一个指针作为参数传递给一个函数或方法时,其实是把指针的副本copy传递给了函数,即把指针的值本身传递到ptr,因此当在函数或方法内部修改指针(注意,不是修改指针所指向的值)时,其实修改的是函数内部指针的副本,而非外部的指针本身)

void func(int *arr1)
{
	
}

int arr[3]={1,2,3};
func(a);

2.2.3 引用传递和引用符号&的区别

https://blog.csdn.net/llm_hao/article/details/108432323
https://blog.csdn.net/L_fengzifei/article/details/128313713!!!

c语言中没有引用传递
引用传递是基于指针的
引用符号&相当于别名,并不是取地址,即共享一段内存(引用传递数值,会修改原始的数据的

2.3.4 参数传递扩展 c/python

python的传值方式是按照c++中传指针的方式传值的,即不是引用也不是值。如果对象是可变的,那么操作是在传入对象上操作的,如果是不可变的,那么操作后相当于这个标识符指向了另一个对象
(c++传指针本质上也是值传递,只不过传的是地址值,这时候由于是值传递地址值不会被修改,但是存放在地址里的实参的值是可修改的,这时候对应于python传入对象可变的话相当于就在原对象地址上修改这个对象。如果对象本身不可变,相当于传递的是指针常量,自然不能修改这个对象)

python的赋值

a=1
a=2
# 前后两次id(a)不同

2.3 返回值

注意函数的return的值,与函数声明返回值的类型要对应,否则会发生类型转换

对于返回值是结构体和类对象时(值传递),为了防止局部对象被销毁,也为了防止通过返回值修改原来的局部对象,编译器并不会返回这个对象,而是根据这个对象先创建一个临时对象(匿名对象)以拷贝的方式进行,然后再将这个临时对象返回

2.3.1 返回值与局部变量!!!

https://blog.csdn.net/szm1234/article/details/120864801
https://www.cnblogs.com/xuhj001/p/3436175.html

一般的来说,函数是可以返回局部变量的。 局部变量的作用域只在函数内部,在函数返回后,局部变量的内存已经释放了。因此,如果函数返回的是局部变量的值,不涉及地址,程序不会出错。但是如果返回的是局部变量的地址(指针)的话,程序运行后会出错。因为函数只是把指针复制后返回了,但是指针指向的内容已经被释放了,这样指针指向的内容就是不可预料的内容,调用就会出错。准确的来说,函数不能通过返回指向栈内存的指针(注意这里指的是栈,返回指向堆内存的指针,或指向全局数据区、常量区的地址是可以的)

错误使用局部变量

数组定义在栈上,是局部变量,函数要返回一个数组的地址,但是当函数结束时,局部变量在内存中的数据被释放了,所以是错误的

int* func()
{
    int a[3]={1,2,3};
	return a;
}

int main()
{
    int *b=func();
    
    for (int i=0;i<3;i++)
    {
        cout<<b[i]<<endl;
    }
	return 0;
}

解决方法

  • 外部初始化数组,然后再传递
  • 定义静态局部变量,存储在全局静态数据区的内存,不会因为函数的结束而被释放
  • 使用结构体包裹,返回结构体,返回时结构体进行了深拷贝(但是如果是返回的结构体的指针,依然是不行的!!!)
#include <iostream>
using namespace std;

struct Arr
{
    int arr[3];
};

int* func1(int *arr)
{
    arr[0]=1;
    arr[1]=2;
    arr[2]=3;
    // static int a[3]={1,2,3};
	return arr;
}
int* func2()
{
    // int a[3]={1,2,3};
    static int arr[3]={1,2,3}; // 虽然是静态变量,但是变量arr本身只能在func2中使用
	return arr;
}

struct Arr func3()
{
    struct Arr arr;
    arr.arr[0]=1;
    arr.arr[1]=2;
    arr.arr[2]=3;

    return arr; // 结构体变量arr本身,只能在func中使用
}

int main()
{
    // version1 函数外初始化
    int arr[3];  
    int *b1=func1(arr);

    // version2 使用静态局部数据 
    int *b2=func2();

    struct Arr b3=func3();
    
    for (int i=0;i<3;i++)
    {
        cout<<b1[i]<<" "<<b2[i]<<" "<<b3.arr[i]<<endl;
    }
	return 0;
}

例子2

这个是可以的,因为字符串属于常量,存在常量区,不会因为是局部变量而释放!!!

char *func()
{
    char *s="hello world";
    return s;
}

int main()
{
    char *str=func();
    cout<<str<<endl;

    return 0;
}

下面这个是不可以的,因为字符串被当成了字符数组,所以是局部变量被放在了栈中,函数结束,内存会被释放掉!!!

char *func()
{
    char s[]="hello world";
    return s;
}

int main()
{
    char *str=func();
    cout<<str<<endl;

    return 0;
}

例子3

堆内存中的数据由程序员释放,函数结束不会被释放,除非整个程序结束

char *func()
{
    char *p=(char *)malloc(sizeof(char)*10);
    memset(p,0,10);
    for (int i=0;i<10;i++)
    {
        p[i]=0x20+i;
    }
    return p;
}

int main()
{
    char *p=func();
    for (int i=0;i<10;i++)
    {
        cout<<p[i]<<endl;
    }

    return 0;
}

例子补充

void func(int *arr, int n) {
    arr = (int*)malloc(n * sizeof(int)); // 动态分配内存空间
    for (int i = 0; i < n; i++) {
        arr[i] = i; // 对内存空间进行赋值
    }
}

int main() {
    int *arr = NULL;
    func(arr, 5); // 调用函数分配内存空间
    for (int i = 0; i < 5; i++) {
        printf("%d ", arr[i]); // 输出内存空间中的值
    }
    free(arr); // 释放内存空间
    return 0;
}

3. 函数范例

返回类型(return_type):一个函数可以返回一个值;不返回值得时候return_typevoid
参数(parameter_list):形参列表

return_type function_name(parmeter_list)
{
	...;
}

3. 函数声明与定义

函数声明会告诉 编译器 函数名称 及 如何调用函数
函数的实际主体可以单独定义
形参的名称并不重要,只有参数的类型是必须的

// 方法1
int max(int num1,int num2)
{
	...
}

// 方法2
int max(int num1,int num2); //注意是分号

// 方法3
int max(int,int)

3.1 多文件的函数调用与声明

在A文件中声明和定义,在B文件中调用,则需要在调用函数的文件B的顶部声明函数

static/extern

跨文件调用的时候,要加上extern,虽然有些情况下不写extern仍然可以执行(函数不用extern可以,变量不可以)(因为对于函数声明的extern 加不加都是等价的)
static修饰静态函数,表示该函数只能在当前源文件中被调用去

4. 函数传递

4.1 函数传递数组

#include <stdio.h>
#include <string.h>

// version 1 这里a变成了指针
void func(int a[],int n)
{
    for (int i=0;i<n;i++)
    {
        printf("%d\n",a[i]);
    }
}


// version 2 直接使用指针
void func2(int *a,int n)
{
    for (int i=0;i<n;i++)
    {
        printf("%d\n",a[i]);
    }
}

int main()
{
    int a[10]={1,2,3};
    func(a,sizeof(a)/sizeof(int));
    func2(a,sizeof(a)/sizeof(int));
}

二维数组

// version 1
void func(int a[][3],int m,int n)
{
    for (int i=0;i<m/n;i++)
    {
        
        for (int j=0;j<n;j++)
        {
            printf("%d,%d,%d\n",a[i][j],*(a[i]+j),*(*(a+i)+j));
        }
    }
}

// version 2
void func(int (*a)[3],int m,int n)
{
    for (int i=0;i<m/n;i++)
    {
        
        for (int j=0;j<n;j++)
        {
            printf("%d,%d,%d\n",a[i][j],*(a[i]+j),*(*(a+i)+j));
        }
    }
}

int main()
{
    int a[2][3]={1,2,3,4,5,6};
    func(a,sizeof(a)/sizeof(int),sizeof(a[0])/sizeof(int));
    // func2(a,sizeof(a)/sizeof(int));
}

数组与局部变量

注意局部变量作为指针函数的返回值情况 https://www.cnblogs.com/xuhj001/p/3436175.html

函数运行结束后会销毁在他内部定义的所有局部数据,包括局部变量、局部数组和形式参数,函数返回的指针请尽量不要指向这些数据,C语言没有任何机制来保证这些数据会一直有效,它们会在后续使用过程中可能引发运行时错误

// 下面的例子,有的编译器直接报错,提示 返回局部变量的指针
int *func()
{
    int n=100;
    return &n;
}

int main()
{
    int *p=func(),n;
    n=*p;
    printf("%d\n",n);

    return 0;
}

这个例子很重要https://blog.csdn.net/szm1234/article/details/120864801

4.2 函数传递结构体

// 值传递
// strcut1=struct2 可以进行赋值,但是相当于副本,二者没有任何关系
void func(struct struct_name struct_param)
{
    /*code*/
}
struct struct_name struct_param;
func(strct_param); // 只是传递的副本,并不会修改外部参数,相当于值传递

//地址传递
void func(struct struct_name* struct_param)
{
    /*code*/
}
struct struct_name struct_param;
func(&strct_param); // 会修改外部参数,相当于地址传递

#include <stdio.h>

#define MaxSize 10
typedef struct
{
    int data[MaxSize];
    int length;
}SqList;

void InitList(SqList *L)
{
    for (int i=0;i<MaxSize;i++)
    {
        L->data[i]=0;
    }
    L->length=0;
}

int main()
{
    SqList L; //等价于Struct L
    InitList(&L);

    for (int i=0;i<MaxSize;i++)
    {
       printf("%d\t",L.data[i]);
    }
    printf("\n%d\n",L.length);
    return 0;
}

例子2

struct Arr
{
    int arr[3];
};

struct Arr func3()
{
    struct Arr arr;
    arr.arr[0]=1;
    arr.arr[1]=2;
    arr.arr[2]=3;

    return arr;
}

int main()
{
    struct Arr b3=func3();
    
    for (int i=0;i<3;i++)
    {
        cout<<b3.arr[i]<<endl;
    }
	return 0;
}

例子3

#include <iostream>
using namespace std;

#define FUNDLEN 50

double sum(double x,double y);
double sum2(const struct funds *stan);
double sum3(struct funds stan);

struct funds
{
    char bank[FUNDLEN];
    double bankfund;
    char save[FUNDLEN];
    double savefund;
};

double sum(double x,double y)
{
    return (x+y);
}

double sum2(const struct funds *stan)
{
    return (stan->bankfund+stan->savefund);
}

double sum3(struct funds stan)
{
    return (stan.bankfund+stan.savefund);
}

int main()
{
    struct funds stan={
        "l bank",
        20.1,
        "w save",
        20.2
    };

    cout<<sum(stan.bankfund,stan.savefund)<<endl;
    cout<<sum2(&stan)<<endl;
    cout<<sum3(stan)<<endl;

    return 0;
}

5.指针与函数

5.1 函数名与函数名地址 – 5.3 函数指针

函数名:可以理解为地址,但是实际上不是地址。函数的首地址,是void()类型
&函数名:表示以一个指向该函数这个对象的地址 相当于一个指针(指向一个整体)void(*)()类型
这两个地址值是相等的

指针函数

指针作为函数的返回值,指针就是一个地址
引用传递地址传递
注意局部变量作为指针函数的返回值情况 https://www.cnblogs.com/xuhj001/p/3436175.html

函数运行结束后会销毁在他内部定义的所有局部数据,包括局部变量、局部数组和形式参数,函数返回的指针请尽量不要指向这些数据,C语言没有任何机制来保证这些数据会一直有效,它们会在后续使用过程中可能引发运行时错误

// 下面的例子,有的编译器直接报错,提示 返回局部变量的指针
int *func()
{
    int n=100;
    return &n;
}

int main()
{
    int *p=func(),n;
    n=*p;
    printf("%d\n",n);

    return 0;
}

这个例子很重要https://blog.csdn.net/szm1234/article/details/120864801

5.3 函数指针 – 还要看

https://stackoverflow.com/questions/9552663/function-pointers-and-address-of-a-function!!!
https://stackoverflow.com/questions/6893285/why-do-function-pointer-definitions-work-with-any-number-of-ampersands-or-as!!!
https://stackoverflow.com/questions/37501982/address-operator-with-pointer-to-member-function!!!
https://blog.csdn.net/L_fengzifei/article/details/128118034

在编译时,每个函数都有一个入口地址,该入口地址就是函数指针所指向的地址,可以利用该指针变量调用函数
函数在内存中具有物理地址,该地址能够赋给指针变量

函数名:可以理解为地址,但是实际上不是地址。函数的首地址,是void()类型
&函数名:表示以一个指向该函数这个对象的地址 相当于一个指针(指向一个整体)void(*)()类型
这两个地址值是相等的
函数名在 非&函数名 的 表达式中被隐式转换成函数指针void(*)()类型

实际上 下面两种C标准都是可以的:

// 标准1
void func1(char *);
void (*ptr1)(char*);
ptr1=func1;
(*ptr1)("li")

// 标准2
void func2(char *);
void (*ptr2)(char*);
ptr2=func2;
ptr2("li")

例子重要!!!

#include <iostream>
using namespace std;

int func(int x)
{
    return x*x;
}

int main()
{
    // cout<<"hello world"<<endl;

    int (*pfunc1)(int)=func;
    int (*pfunc2)(int)=&func;

    printf("0x func %#p\n",func); // 0x func 0x4015a4
    printf("0x &func %#p\n",&func); // 0x &func 0x4015a4
    printf("0x pfunc1 %#p\n",pfunc1); // 0x pfunc1 0x4015a4
    printf("0x *pfunc1 %#p\n",*pfunc1); // 0x *pfunc1 0x4015a4
    printf("0x func2 %#p\n",pfunc2); // 0x func2 0x4015a4
    printf("0x *func2 %#p\n",*pfunc2); // 0x *func2 0x4015a4

    cout<<"func:"<<func(10)<<endl; // func:100
    cout<<"*func:"<<(*func)(10)<<endl; // *func:100
    cout<<"*pfunc1:"<<(*pfunc1)(10)<<endl; // *pfunc1:100
    cout<<"pfunc1:"<<pfunc1(10)<<endl; // pfunc2:100
    cout<<"*pfunc2:"<<(*pfunc2)(10)<<endl; // *pfunc2:100
    cout<<"pfunc2:"<<pfunc2(10)<<endl; // pfunc2:100

    return 0;
}

函数指针数组
https://blog.csdn.net/lzh201864031/article/details/129785640

char (*ptr())[5]; // ptr是一个函数指针,指向的函数 返回值 是一个由五个char元素构成的数组 ???

// 区别
char (*ptr)[5]; // ptr是一个普通的指针,指向一个由五个char元素构成的数组

常用函数

字符串/数值型转换

把数字存储为字符串就是存储数字字符
123 -> ‘1’ ‘2’ ‘3’ ‘\0’

字符串转数值型

  • atoi

字符串转整型

// 下面两种字符串都可以
char s[]="123";
char *s="123";
int var=atoi(s);
printf("%d\n",var);
  • atof

字符串转整型

// 下面两种字符串都可以
// char s[]="123";
char *s="123.456";
double var=atof(s);
printf("%f\n",var);

数值型转字符串

  • itoa()
#include<stdlib.h>
int a=10;
char s[10]={0};
itoa(a,s);
printf("%s\n",s)

char a='a';
char buffer[10];
itoa(a,buffer,2); // 2表示转换基数
  • sprintf
// 整型转字符串
int a=120;
char str[20];
memset(str,0,sizeof(str));
sprintf(str,"%d",a);
printf("%s\n",str);
printf("%d\n",sizeof(str));

exit

exit(0); // 正常退出
exit(1); // 异常退出,里边的参数 1 会传给操作系统,主要不为0就表示异常退出
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值