18 作用域(全局、局部、块级),标识符的默认值(零、垃圾值),同名标识符(就近原则、绕过遮蔽),extern 关键字

目录

1 作用域

2 全局作用域

2.1 概述

2.2 全局标识符的定义

2.3 全局标识符的默认值(零)

3 extern 关键字

3.1 功能介绍

3.2 对函数使用

3.3 对变量使用

4 局部作用域

4.1 概述

4.2 局部标识符的定义

4.3 局部标识符的默认值(垃圾值)

4.4 同名标识符

4.4.1 就近原则

4.4.2 输出变量的内存地址

4.4.3 绕过同名局部变量的遮蔽效应

5 块级作用域

5.1 概述

5.2 块级标识符的定义

6 作用域测试题


1 作用域

        作用域定义了程序中标识符(如变量、常量、数组等)的可见性和访问范围,即标识符在何处可被引用或访问。作用域可以分为几种类型:全局作用域、局部作用域以及块级作用域。

  • 全局作用域:在整个程序范围内均可访问的标识符具有全局作用域。
  • 局部作用域:仅在其声明的函数内部可见的标识符具有局部作用域。
  • 块级作用域:限于特定代码块内的标识符具有块级作用域。

        需要注意的是,在同一个作用域内不允许声明同名的标识符,以避免混淆和错误。


2 全局作用域

2.1 概述

        全局作用域指的是在所有函数和代码块(如分支语句、循环语句等)之外定义的变量、常量、数组等标识符。这些标识符在整个程序范围内都是可见和可访问的,通常被称为全局变量、全局常量、全局数组等。

        由于 C 语言不允许嵌套定义函数,因此函数本身默认具有全局作用域

        全局作用域内的变量在整个程序运行期间都保持有效,除非在某个局部作用域(如函数体内)中有同名的变量定义,这时局部变量会遮蔽同名的全局变量

2.2 全局标识符的定义

#include <stdio.h>

// 在所有函数外定义的变量具有全局作用域
// 全局变量定义
double money = 1.1;

// 全局常量定义
const double PI = 3.14;

// 全局数组定义
char msg[] = "Hello World";

// 全局函数定义,用于演示全局数据的访问和修改
// 前面我们提到过函数不能嵌套定义,所以对于函数而言,本身默认就是具有全局作用域
// 所以可以将函数拆分到多个源文件中进行编写,实现多文件编程
void func()
{
    printf("func 函数中使用全局数据:\n");
    printf("money=%.2f \n", money);
    printf("PI=%.2f \n", PI);
    printf("msg=%s \n", msg);

    // 修改全局变量 money 的值
    money += 100;

    printf("\n"); // 空行,增加可读性
}

// 主函数,程序的入口点
int main()
{
    // 首次调用 func 函数,展示全局数据初始状态
    printf("第一次调用 func():\n");
    func();

    printf("主函数中使用全局数据(展示func函数修改后的效果):\n");
    printf("money=%.2f \n", money); // 此时 money 的值已被 func 函数修改为 101.10
    printf("PI=%.2f \n", PI);       // PI 的值未变,PI=3.14
    printf("msg=%s \n\n", msg);     // msg 的内容未变,msg=Hello World

    // 第二次调用 func 函数
    printf("第二次调用 func():\n");
    func();

    return 0; // 程序正常结束
}

        输出结果如下所示:

2.3 全局标识符的默认值(零)

        对于全局变量,如果没有显式地指定初始值,它们会被自动初始化为零值(例如,整型变量为 0,浮点型为 0.000000,字符型为 '\0')

        对于全局数组,若没有显式初始化,则其所有元素也会自动初始化为零值。特别是对于字符数组,每个元素都会被初始化为空字符 '\0',使得该数组被视为一个以空字符结尾的字符串。

#include <stdio.h>

// 对于全局变量,如果没有显式地指定初始值,它们会被自动初始化为零值
// 整型变量为 0,浮点型为 0.000000,字符型为 '\0')。
int a;    // 自动初始化为 0
double b; // 自动初始化为 0.0
char c;   // 自动初始化为空字符 '\0'

// 对于全局数组,若没有显式初始化,则其所有元素也会自动初始化为零值
int arr[5];      // 所有元素自动初始化为 0
double array[7]; // 所有元素自动初始化为 0.000000
char msg[6];     // 所有元素自动初始化为'\0',因此 msg 是一个以 '\0' 结尾的空字符串

// 主函数
int main()
{
    // 输出全局变量的值
    printf("a=%d \n", a);   // 输出:a=0
    printf("b=%f \n", b);   // 输出:b=0.000000
    printf("c=%c \n\n", c); // 输出:c= (因为空字符'\0'不可见,所以这里可能看起来像是空白的)

    // 计算数组 arr 的长度
    int len = sizeof(arr) / sizeof(arr[0]);
    printf("整形数组 arr 各元素为: ");
    // 遍历数组 arr 并打印每个元素的值
    for (int i = 0; i < len; i++)
    {
        printf("%d ", arr[i]); // 所有元素都是 0
    }
    printf("\n");

    // 计算数组 array 的长度
    int length = sizeof(array) / sizeof(array[0]);
    printf("整形数组 array 各元素为: ");
    // 遍历数组 arr 并打印每个元素的值
    for (int i = 0; i < length; i++)
    {
        printf("%f ", array[i]); // 所有元素都是 0.000000
    }
    printf("\n");

    // 尝试打印字符数组 msg,但由于它只包含'\0',所以不会显示任何内容
    printf("字符数组(全是空白字符,无可见内容):%s\n", msg);

    return 0; // 程序正常结束
}

        输出结果如下所示:


3 extern 关键字

3.1 功能介绍

        在 C 语言中,extern 关键字用于声明一个变量或函数是在其他文件当前文件的其他地方定义的,表示它具有外部链接属性。这使得可以在多个源文件中共享同一个变量或函数的定义,但实际的定义(即初始化)只应在某一处出现

        这对于全局变量和函数特别有用,因为它们往往需要在程序的不同部分被访问或调用。使用 extern 可以避免重复定义,并允许多个源文件间共享同一个标识符。

3.2 对函数使用

        C 语言中不允许在一个函数内部定义另一个函数,但可以在一个函数内部调用另一个函数。因此,C 语言中的函数默认具有全局作用域,意味着它们可以在程序的任何地方或定义它们的文件之外被调用,前提是这些函数在被调用前已经被声明。

        虽然在声明函数时通常不需要使用 extern 关键字来表明函数在其他地方定义,但在多文件编程中,为了明确表示这一点,有时仍会看到 extern 的使用。然而,对于函数来说,extern 通常是可以省略的,因为默认情况下函数就是具有全局作用域的。

        新建一个 math.c 文件,代码如下:

int add(int a, int b)
{
    return a + b;
}

int sub(int a, int b)
{
    return a - b;
}

int mul(int a, int b)
{
    return a * b;
}

int div(int a, int b)
{
    if (b == 0)
    {
        return 0;
    }
    return a / b;
}

        新建一个 main.c 文件 ,代码如下

#include <stdio.h>

// 函数原型声明
// 使用 extern 关键字来声明函数
extern int add(int a, int b);
extern int sub(int a, int b);

// 也可以省略 extern 关键字,因为默认情况下,函数就是全局可见的
// 只需提供函数原型,编译器就能正确处理函数调用
int mul(int a, int b);
int div(int a, int b);

int main()
{
    printf("5 + 3 = %d\n", add(5, 3));
    printf("5 - 3 = %d\n", sub(5, 3));
    printf("5 * 3 = %d\n", mul(5, 3));
    printf("5 / 3 = %d\n", div(5, 3));

    return 0;
}

        在上面的例子中,extern 关键字用于声明 add 和 sub 函数,但实际上这并不是必需的。编译器在解析 main 函数中的函数调用时,会查找这些函数的声明(无论是否使用了 extern)。如果找到了函数声明(无论是否显式地使用了 extern),并且找到了相应的函数定义(在编译单元的其他地方,或者在其他编译单元中并通过链接器链接),那么编译器和链接器就会正确地处理这些函数调用。

        对于 mul 和 div 函数,没有使用 extern 关键字,但这并不影响它们的声明和后续的使用。编译器同样会查找这些函数的声明,并期望在编译单元的其他地方或链接时找到它们的定义。

        输出结果如下所示:

提示:

        extern 关键字在函数声明中通常是多余的,因为函数默认就是全局可见的。然而,在某些情况下,使用 extern 可以增加代码的可读性,表明这些函数是在其他地方定义的,或者是在当前文件的后面定义的。但在大多数情况下,省略 extern 关键字是更常见的做法。 

3.3 对变量使用

        与函数不同,extern 关键字在变量声明中更为常见和重要。extern 关键字常用于声明一个变量是在其他文件或当前文件的其他地方定义的,但在当前文件中需要使用。例如,在一个 .c 源文件中定义了一个全局变量,并希望在另一个 .c 源文件中访问它,那么必须在另一个 .c 源文件中使用 extern 来声明这个变量

        新建一个 vars.c 文件,代码如下:

// 定义一些全局变量,在其他文件中使用它们
int globalVar = 42; 
double num = 3.1415;
char arr[] = "Thanks_ks";

        新建一个 main.c 文件,代码如下:

#include <stdio.h>

// 使用 extern 声明变量是在其他文件中定义的
// 这样就可以在当前文件中访问这些变量的值
extern int globalVar;
extern double num;
extern char arr[];

int main()
{
    printf("Global variable value: %d \n", globalVar);
    printf("Global variable value: %lf \n", num);
    printf("Global variable value: %s \n", arr);
    return 0;
}

        输出结果如下所示:

提示:

        通常,我们会将常量定义在全局作用域中,以便在多个文件或模块间共享不变的数据。而对于变量,我们倾向于将其作用域限制在单个源文件或函数内部,以减少内存消耗并提高代码的可维护性。这样不仅可以防止其他部分意外修改这些变量,还能增强程序的安全性,因为全局变量可以被程序中的任何部分访问和修改,从而可能导致难以追踪的副作用和程序不稳定。


4 局部作用域

4.1 概述

        局部作用域指的是在函数内部定义的变量、常量、数组等标识符的作用范围。这些标识符只在函数内部被访问和使用。这意味着,局部作用域内的变量(通常称为局部变量)在离开定义它们的函数后就不可见了。

        需要注意的是,函数的形式参数(形参)也被视为局部变量

        局部作用域限定了标识符的可见性和生命周期,有助于避免命名冲突和提高代码的可维护性。

4.2 局部标识符的定义

#include <stdio.h>

// 定义 add 函数,该函数接受一个整型参数 a
void add(int a)
{
    // 局部变量 b,仅在 add 函数内部可见
    int b = 20;
    // 局部常量 PI,同样仅在 add 函数内部可见
    const double PI = 3.14;
    // 局部数组 nums,也仅在 add 函数内部可见
    int nums[] = {10, 20, 30};

    // 使用局部变量和局部常量进行计算,并打印结果
    printf("(a+b+nums[0])*PI=%f \n", (a + b + nums[0]) * PI);
}

int main()
{
    // 调用 add 函数,传入参数 100
    add(100); // 输出: (a+b+nums[0])*PI=408.280000

    // 以下尝试在 add 函数外部访问局部变量的代码会报错,因为它们在 add 函数外部是不可见的
    // printf("%d \n", a);  // 报错:'a' undeclared(因为 a 是 add 函数的局部变量)
    // printf("%d \n", b);  // 报错:'b' undeclared(因为 b 是 add 函数的局部变量)
    // printf("%f \n", PI); // 报错:'PI' undeclared(因为 PI 是 add 函数的局部常量)
    // printf("%d \n", nums[0]); // 报错:'nums' undeclared(因为 nums 是 add 函数的局部数组)

    return 0;
}

         输出结果如下所示:

        尝试在 add 函数外部访问局部变量的代码会报错,因为它们在 add 函数外部是不可见的 。

4.3 局部标识符的默认值(垃圾值)

        对于局部变量和局部数组,如果未进行显式初始化,其初始值将是未定义的,即这些变量所占的内存空间中存储的是之前遗留的数据值(通常称为 “垃圾值”)。这种不确定性可能导致程序行为不可预测,甚至引发错误。因此,初始化这些局部变量变得尤为重要。

        初始化不仅能防止程序因使用未知值而出现不可预测的行为,还增强了程序的健壮性。通过为变量赋予初始值,可以确保它们在首次使用前具有明确且合理的状态,减少了出错的风险。此外,初始化变量也提高了代码的可读性和可维护性,因为它清晰地表明了变量的预期用途和初始状态。

        值得注意的是,全局变量和静态变量(包括静态局部变量和静态全局变量,使用 static 定义,下一章节讲解)与局部变量和局部数组不同,它们在作用域开始之前就已经被自动初始化为零(0、0.000000、'\0')或空(null,对于指针和聚合类型)。这一区别进一步强调了为局部变量和局部数组显式初始化的必要性。

#include <stdio.h>

int main()
{
    // 定义局部变量不进行初始化赋值
    int a; // 局部变量 a 未初始化,其值不确定

    // 定义局部数组不进行初始化
    int arr[5]; // 局部数组 arr 未初始化,其元素值不确定

    // 输出局部变量 a 的值
    printf("未初始化的局部变量 a 的值(垃圾值)为:%d \n", a); 

    // 计算数组长度
    int len = sizeof(arr) / sizeof(arr[0]); // 计算数组长度
    printf("局部数组的各元素为(垃圾值):", a); 

    // 遍历数组 arr 并输出每个元素的值
    for (int i = 0; i < len; i++)
    {
        printf("%d ", arr[i]); // 输出 arr[i] 的值,可能是任意值
    }

    printf("\n");

    return 0;
}

        输出结果如下所示:

4.4 同名标识符

4.4.1 就近原则

        如果在局部作用域中定义了与全局作用域中同名的标识符,那么在局部作用域内将优先使用本地定义的标识符(即就近原则。这意味着局部作用域内的同名标识符会遮蔽相同名称的全局标识符

#include <stdio.h>

// 全局变量
int a = 100;
int b = 200;

// 函数 add,其中定义了局部变量 a
void add()
{
    int a = 300; // 局部变量 a,与全局变量 a 同名
    // 优先使用本作用域中定义的数据
    a += 10;     // 修改局部变量 a

    b += 10;     // 修改全局变量 b

    printf("函数 add 内部: a=%d, b=%d \n", a, b);
}

int main()
{
    // 调用函数 add
    add(); // 输出:函数 add 内部: a=310, b=210

    // 输出全局变量 a 和 b 的值
    printf("函数 add 外部: a=%d, b=%d \n", a, b); // 输出:函数 add 外部: a=100, b=210

    return 0;
}

        输出结果如下所示:

        结果分析: 

  • 函数 add 内部:输出 a=310, b=210,这里 a 是局部变量,b 是全局变量。
  • 函数 add 外部:输出 a=100, b=210,这里 a 是全局变量,b 也在全局作用域中,但被函数 add 修改了。

4.4.2 输出变量的内存地址

        为了证明局部变量 a 和全局变量 a 不是同一个变量,我们可以在函数 add 中打印局部变量 a 的地址,并在 main 函数中打印全局变量 a 的地址。

        在 C 语言中,打印指针或地址通常使用 %p 格式符,并结合取地址符 & 来获取变量的地址

#include <stdio.h>

// 全局变量
int a = 100;
int b = 200;

// 函数 add,其中定义了局部变量 a
void add()
{
    int a = 300; // 局部变量 a,与全局变量 a 同名
    // 优先使用本作用域中定义的数据
    a += 10; // 修改局部变量 a

    b += 10; // 修改全局变量 b

    printf("函数 add 内部: a=%d, b=%d, 地址=%p \n", a, b, &a);
}

int main()
{
    // 调用函数 add
    add(); // 输出:函数 add 内部: a=310, b=210, 地址=...

    // 输出全局变量 a 和 b 的值
    printf("函数 add 外部: a=%d, b=%d, 地址=%p \n", a, b, &a); // 输出:函数 add 外部: a=100, b=210, 地址=...

    return 0;
}

        输出结果如下所示:

        可以看出,所打印出的同名变量(局部和全局)的地址是不同的,因此它们实际上是不同的变量。

4.4.3 绕过同名局部变量的遮蔽效应

        在 C 程序中,全局变量在程序的所有部分都是可见的,但如果在某个函数内部定义了与全局变量同名的局部变量,那么在该函数内部,该局部变量将遮蔽全局变量。这意味着在该局部变量的作用域内,所有对同名变量的引用都将指向局部变量,而非全局变量

        为了在一个函数内部访问被局部变量遮蔽的全局变量,可以在一个更小的作用域(如使用大括号 {} 定义的块级作用域)中,通过 extern 关键字声明该全局变量。extern 关键字不会定义新的变量,而是指示编译器该变量是在其他地方(通常是全局作用域)定义的,因此应引用那个全局变量。这样,即使在局部变量的遮蔽下,也能直接访问和操作全局变量

#include <stdio.h>

// 全局变量
int x = 10;

// 函数 func,其中定义了局部变量 x
void func()
{
    // 局部变量 x,与全局变量 x 同名,遮蔽了全局变量 x
    int x = 20;

    // 在这里尝试访问全局变量 x,但由于局部变量的存在
    // 优先使用本作用域中定义的数据,这会访问到局部变量 x
    printf("在func内部,直接访问x: %d, 地址=%p\n", x, (void *)&x); // 输出: 20, 地址=...

    // 可以在一个更小的作用域(如使用大括号 {} 定义的块级作用域)中
    // 通过 extern 关键字声明该全局变量
    // 这样,即使在局部变量的遮蔽下,也能直接访问和操作全局变量。
    // 块级作用域
    {
        // 这里的 extern 声明告诉编译器,想要引用的是全局变量 x
        extern int x;
        printf("在func内部,通过extern访问全局x: %d, 地址=%p\n", x, (void *)&x); // 输出: 10, 地址=...
    }
}

int main()
{
    // 直接访问全局变量 x
    printf("在main中访问全局x: %d, 地址=%p\n", x, (void *)&x); // 输出: 10, 地址=...

    func(); // 调用 func 函数

    return 0;
}

        在上面的代码中,当在 func 函数内部定义一个名为 x 的局部变量时,它遮蔽了全局变量 x。但在 func 函数内部的一个额外作用域(由大括号 {} 界定)中,通过 extern int x; 的声明,我们能够绕过局部变量的遮蔽效应,直接访问和打印全局变量 x 的值及其地址。

        输出结果如下所示:​​​​​​​


5 块级作用域

5.1 概述

        块级作用域是 C99 标准引入的一个重要概念,它指的是在由花括号 {} 包围的代码块(如函数体、if 语句、for 循环等)中定义的变量、常量、数组等元素的可见性和生命周期。这些元素仅在该代码块内部有效,被称为块级变量、块级常量或块级数组,它们具有与函数内部定义的局部变量相同的特性

        每个由花括号 {} 界定的代码块都创建了一个新的作用域,其中声明的变量和常量在该块内是可见的,但一旦离开该块作用域,这些变量和常量便不再可访问。尝试在块外部访问这些变量或常量将导致编译错误,因为编译器在外部作用域中无法识别这些名称。

        块级作用域有助于减少命名冲突,因为它限制了变量的可见范围,同时也使得代码更加清晰和易于维护。通过使用块级作用域,程序员可以更加精确地控制变量的生命周期和可见性,从而提高代码的安全性和效率。

5.2 块级标识符的定义

#include <stdio.h>

int main()
{
    // 第一个代码块,具有块级作用域
    {
        // 在这个块内定义的变量 a 和常量 PI 只在这个块内可见
        int a = 20;             // 块级变量
        const double PI = 3.14; // 块级常量

        // 使用块级变量和常量进行计算并打印
        printf("a*PI=%f \n", a * PI);
    }

    // 第二个块,由 if 语句引入,同样具有块级作用域
    if (1)
    {
        // 在这个 if 块内定义的数组 nums 只在这个块内可见
        int nums[] = {10, 20, 30}; // 局部数组

        // 遍历并打印数组元素
        printf("%d %d %d \n", nums[0], nums[1], nums[2]);
    }

    // 第三个块,由 for 循环引入,for 循环的初始化部分声明的变量 i 具有块级作用域
    for (int i = 0; i < 5; i++)
    {
        // 变量 i 只在这个 for 循环的块内可见
        printf("%d ", i);
    }

    // 尝试访问已超出其作用域的变量或常量会导致编译错误
    // printf("%d \n", a);       // 报错:'a' undeclared(因为 a 在第一个块内定义)
    // printf("%f \n", PI);      // 报错:'PI' undeclared(因为 PI 在第一个块内定义)
    // printf("%d \n", nums[0]); // 报错:'nums' undeclared(因为 nums 在 if 块内定义)
    // printf("%d \n", i);       // 报错:'i' undeclared(因为 i 在 for 循环的块内定义)

    return 0;
}

        输出结果如下所示:

        尝试访问已超出其作用域的变量或常量会导致编译错误:


6 作用域测试题

1. 思考:下面的代码输出什么内容?

#include <stdio.h>

// 在全局作用域中声明并初始化一个整型变量 num,其值为 100
int num = 100;

// 定义一个函数 func,该函数不接受任何参数,也不返回任何值(void 类型)
void func()
{
    // 在这个函数内部,对全局变量 num 进行操作,将其值增加 20
    num += 20;
    printf("func in num:%d \n", num);
}

int main()
{
    // 在main函数内部,声明并初始化一个局部变量 num,其值为 50
    // 注意:这个局部变量 num 会遮蔽全局变量 num
    // 在 main 函数内部只能访问到这个局部变量
    int num = 50;

    // 调用 func 函数,该函数会修改全局变量 num 的值
    // 但由于 main 函数内部的局部变量遮蔽了全局变量
    // 因此 main 函数内部的 num 值不会受到影响
    func();

    // 打印 main 函数内部的局部变量 num 的值,由于 func 函数只修改了全局变量 num
    // 所以这里打印的值仍然是 50
    printf("main in num:%d \n", num);

    return 0;
}

        输出结果如下所示,具体解释请参见代码中的注释:


2. 思考:下面的代码输出什么内容?

#include <stdio.h>

// 定义了一个全局变量 price 并初始化为 200.0
double price = 200.0;

// 函数 test01 打印全局变量 price 的当前值
void test01()
{
    printf("%.2f\n", price);
}

// 函数 test02 修改全局变量 price 的值为 250.0,并打印修改后的值
void test02()
{
    price = 250.0;
    printf("%.2f\n", price);
}

// 主函数
int main()
{
    // 打印全局变量 price 的初始值
    printf("main price=%.2f\n", price); // 输出: main price=200.00

    // 调用 test01,打印全局变量 price 的当前值(未变)
    test01(); // 输出: 200.00

    // 调用 test02,该函数修改了全局变量 price 的值并打印
    test02(); // 输出: 250.00

    // 再次调用 test01,此时全局变量 price 的值已被 test02 修改为 250.0
    // 因此,打印的是修改后的值
    test01(); // 输出: 250.00

    return 0;
}

        输出结果如下所示,具体解释请参见代码中的注释:


3. 思考:下面的代码输出什么内容?

#include <stdio.h>

int n = 10; // 全局变量 n,初始化为 10

void func1()
{
    int n = 20;                 // 局部变量 n,仅在 func1 函数内有效,且隐藏了全局变量 n
    printf("func1 n: %d\n", n); // 打印 func1 内的局部变量 n,输出 20
}

void func2(int n)
{
    // 函数参数 n,仅在 func2 函数内有效,与全局变量 n 和 func1 的局部变量 n 都无关
    printf("func2 n: %d\n", n); // 打印 func2 的参数 n,输出调用时传递的值
}

void func3()
{
    // 在这里,没有显式定义局部变量 n,因此访问的是全局变量 n
    printf("func3 n: %d\n", n); // 打印全局变量 n,输出 10
}

int main()
{
    int n = 30;                // 局部变量 n,仅在 main 函数内有效,且隐藏了全局变量 n
    printf("main n: %d\n", n); // 打印 main 内的局部变量 n,输出 30

    func1();  // 调用 func1,打印 func1 内的局部变量 n,输出 20
    func2(n); // 调用 func2,打印 func2 的参数 n,此时 n 为 main 内的局部变量 n,输出 30
    func3();  // 调用 func3,打印全局变量 n,输出 10

    {
        int n = 40;                 // 局部作用域内的变量 n,仅在该作用域内有效
        printf("block n: %d\n", n); // 打印该作用域内的局部变量 n,输出 40
    }
    // 离开上述作用域后,上诉块作用域内的变量 n 不再可访问

    printf("main n: %d\n", n); // 打印 main 内的局部变量 n,输出 30

    return 0;
}

        输出结果如下所示,具体解释请参见代码中的注释:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Thanks_ks

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值