GCC-3.4.6源代码学习笔记(110)

5.12.3.2.1.2.1.2.2.            完成阶段

函数 cp_parser_function_body 只是调用 cp_parser_compound_statement 来解析复合语句。这里我们跳过解析复合语句的细节,不过记住每个语句都会构建节点并链入语句链(树)。注意到作为子语句链(树)根节点的对应的 COMPOUND_STMT 节点被保存在 cp_parser_ctor_initializer_opt_and_function_body 11476 行的 body 里。

 

10791 void

10792 finish_function_body (tree compstmt)                                                              in decl.c

10793 {

10794   /* Close the block.  */

10795   finish_compound_stmt (compstmt);

10796

10797   if (processing_template_decl )

10798     /* Do nothing now.  */ ;

10799   else if (DECL_CONSTRUCTOR_P (current_function_decl ))

10800     finish_constructor_body ();

10801   else if (DECL_DESTRUCTOR_P (current_function_decl ))

10802     finish_destructor_body ();

10803

 

函数体实际上是一个复合语句, finish_compound_stmt 被调用。注意到参数 compound_stmt 也是在 begin_compound_stmt 中构建的 body

 

1029   tree

1030   finish_compound_stmt (tree compound_stmt)                                     in semantics.c

1031   {

1032     tree r;

1033     tree t;

1034  

1035     if (COMPOUND_STMT_NO_SCOPE (compound_stmt))

1036       r = NULL_TREE;

1037     else

1038       r = do_poplevel ();

 

因为到达该复合语句的末尾时,需要进入上一级作用域。回忆我们已经看过从名字空间及类的作用域中退出;不过从函数作用域退出是不太相同的。

 

336    tree

337    do_poplevel (void)                                                                            in semantics.c

338    {

339      tree block = NULL_TREE;

340   

341      if (stmts_are_full_exprs_p ())

342      {

343        tree scope_stmts = NULL_TREE;

344   

345        block = poplevel (kept_level_p (), 1, 0);

 

当从局部作用域退出时,对应的 cxx_scope 节点将被回收,而在该作用域中声明的名字,标签等等,不在随后的名字查找中可见。但是对于这些名字,标签的节点,如果没有另外的记录,也将消失。为了记住这些项,一个新的 BLOCK 节点将被构建作为缓存树的根节点。不过,对于空的局部作用域,这个节点是不必要的,下面的 kept_level_p 可以告知这个节点是否需要。

 

1492   bool

1493   kept_level_p (void)                                                                          in name-lookup.c

1494   {

1495     return ( current_binding_level ->blocks != NULL_TREE

1496           || current_binding_level->keep

1497           || current_binding_level->kind == sk_cleanup

1498           || current_binding_level->names != NULL_TREE

1499           || current_binding_level->type_decls != NULL);

1500   }

 

因此,在下面如果参数 keep 是非 0 值,一个 BLOCK 节点将被构建来记录这些局部声明。而 functionbody 如果非 0 ,表示这一级是一个函数体。不过注意到当由 do_poplevel 来调用时, functionbody 0 —这是因为我们退出当前作用域后,即将进入 sk_function_parms 作用域——这才是语义意义上的“函数体”。事实上,在 finish_function 中从“函数体”退出时, poplevel 将被再一次调用。

 

423    tree

424    poplevel (int keep, int reverse, int functionbody)                                               in decl.c

425    {

426      tree link;

427      /* The chain of decls was accumulated in reverse order.

428        Put it into forward order, just for cleanliness.  */

429      tree decls;

430      int tmp = functionbody;

431      int real_functionbody;

432      tree subblocks;

433      tree block = NULL_TREE;

434      tree decl;

435      int leaving_for_scope;

436      scope_kind kind;

437   

438      timevar_push (TV_NAME_LOOKUP);

439   

440      my_friendly_assert ( current_binding_level ->kind != sk_class, 19990916);

441   

442      real_functionbody = ( current_binding_level ->kind == sk_cleanup

443                        ? ((functionbody = 0), tmp) : functionbody);

444      subblocks = functionbody >= 0 ? current_binding_level ->blocks : 0;

445   

446      my_friendly_assert (! current_binding_level ->class_shadowed,

447                        19990414);

448   

449      /* We used to use KEEP == 2 to indicate that the new block should go

450        at the beginning of the list of blocks at this binding level,

451        rather than the end. This hack is no longer used.  */

452      my_friendly_assert (keep == 0 || keep == 1, 0);

453   

454      if ( current_binding_level ->keep)

455        keep = 1;

456 

457      /* Any uses of undefined labels, and any defined labels, now operate

458         under constraints of next binding contour.  */

459      if (cfun && !functionbody)

460      {

461         struct cp_binding_level *level_chain;

462        level_chain = current_binding_level->level_chain;

463        if (level_chain)

464         {

465           struct named_label_use_list *uses;

466           struct named_label_list *labels;

467           for (labels = named_labels; labels; labels = labels->next)

468             if (labels->binding_level == current_binding_level)

469             {

470                tree decl;

471                if (current_binding_level->kind == sk_try)

472                  labels->in_try_scope = 1;

473                if (current_binding_level->kind == sk_catch)

474                   labels->in_catch_scope = 1;

475                for (decl = labels->names_in_scope; decl;

476                      decl = TREE_CHAIN (decl))

477                   if (decl_jump_unsafe (decl))

478                    labels->bad_decls = tree_cons (NULL_TREE, decl,

479                                              labels->bad_decls);

480              labels->binding_level = level_chain;

481               labels->names_in_scope = level_chain->names;

482             }

483 

484           for (uses = named_label_uses; uses; uses = uses->next)

485             if (uses->binding_level == current_binding_level)

486             {

487                uses->binding_level = level_chain;

488               uses->names_in_scope = level_chain->names;

489             }

490        }

491      }

 

C++ 里,标签( label )只会出现在函数体内。在上面 467 行, named_labels cfun 用来记录函数中定义的标签。 475 named_label_list names_in_scope 域,是个巧妙的设计,例如,在复合语句:

{

D1; D2;

{

D31;

goto Label1;       // inner 1

D32;

Label1:

D33;

goto Label1;       // inner 2

goto Label2;       // inner 3

}

D4;

Label2:

goto Label1;

}

在解析标签 Label1 时,其对应的 named_label_list 节点的 names_in_scope 域将被赋值为 current_binding_level names 域的值(这时只包含 D32 D31 。从这里可以看到把 current_binding_level names 域用作栈的理由)。注意在到达里层复合语句的闭大括号时,一样要调用 poplevel (通过 do_poplevel ),因为这个函数服务于作用域的退出。那么在上面 475 行的 FOR 循环中,需要检查若跳转到 Label1 时,是否有潜在的危险,并记录导致危险的声明。比如如果 D31 D32 是带有初始化值的( initializer ),跳到标签 Label1 可能使得 D31 D32 得不到指定的初始化。 GCC 将禁止这样的行为。

 

2206 static int

2207 decl_jump_unsafe (tree decl)                                                                             in decl.c

2208 {

2209   if (TREE_CODE (decl) != VAR_DECL || TREE_STATIC (decl))

2210     return 0;

2211

2212   if (DECL_INITIAL (decl) == NULL_TREE

2213       && pod_type_p (TREE_TYPE (decl)))

2214     return 0;

2215

2216   /* This is really only important if we're crossing an initialization.

2217     The POD stuff is just pedantry; why should it matter if the class

2218     contains a field of pointer to member type?  */

2219   if (DECL_INITIAL (decl)

2220       || (TYPE_NEEDS_CONSTRUCTING (TREE_TYPE (decl))))

2221     return 2;

2222   return 1;

2223 }

 

注意只有非静态变量才是考虑的对象。当变量声明中没有初始值( DECL_INITIAL 为空),或者其类型是 POD plain old data ),跳过它才是安全的。注意!上面里层的标记为“ inner 2 ”的 goto 语句不需要考虑 D31 D32

 

1763 int

1764 pod_type_p (tree t)                                                                                    in cp/tree.c

1765 {

1766   t = strip_array_types (t);

1767

1768   if (t == error_mark_node)

1769     return 1;

1770   if (INTEGRAL_TYPE_P (t))

1771     return 1;  /* integral, character or enumeral type */

1772   if (FLOAT_TYPE_P (t))

1773     return 1;

1774   if (TYPE_PTR_P (t))

1775     return 1; /* pointer to non-member */

1776   if (TYPE_PTR_TO_MEMBER_P (t))

1777     return 1; /* pointer to member */

1778   if (TREE_CODE (t) == VECTOR_TYPE)

1779     return 1; /* vectors are (small) arrays of scalars */

1780  

1781   if (! CLASS_TYPE_P (t))

1782     return 0; /* other non-class type (reference or function) */

1783   if (CLASSTYPE_NON_POD_P (t))

1784     return 0;

1785   return 1;

1786 }

 

可以看到引用类型 包含了 protected private 或引用类型的数据成员的类 对应的陈述 CLASSTYPE_NON_POD_P 为非 0 不是 POD 类型。

回到上面的例子。当退出到外层的复合语句时, 480 481 行的代码把 Label1 的作用域改变为外层作用域,并把其 names_in_scope 域更新为包含 D1 D2 。因为此时跳转只能来自该层,比如外层的 goto 语句。注意!这条 goto 语句实际上只受 D31 D32 的影响,它只考虑里层中由 decl_jump_unsafe 检查出来的危险声明。

而在 484 行的 named_label_uses 记录的是对尚未定义的标签的使用,比如在解析上面例子中标记为“ inner 1 ”的 goto 语句时,这个 Label1 的引用将被放入 named_label_uses 。同时在这个节点( named_label_use_list 类型)中,其 names_in_scope 将是那时作用域中已有的声明(即 D31 )。在解析 Label1 时,这个悬而未决的引用将从 named_label_uses 中移除,同时使用 decl_jump_unsafe 检查这个 goto 语句到标签之间的声明(参见 check_previous_goto_1 )。

因此在这里 484 行的 FOR 循环中, named_label_uses 记录的是还没有看到定义的标签。毫无疑问,这些标签的定义只能出现在外层代码。比如上面例子中标记为“ inner 3 ”的 goto 语句,在它及 Label2 标签之间插有声明 D4 。在这个 FOR 循环中,把这个对标签的引用的 named_label_uses 更新为 D1 D2 ,亦把这个引用的作用域设置为外层作用域。这样它的处理和标记为“ inner 1 ”的 goto 语句没有什么不同。

 

poplevel (continue)

 

493      /* Get the decls in the order they were written.

494         Usually current_binding_level->names is in reverse order.

495        But parameter decls were previously put in forward order.  */

496 

497      if (reverse)

498        current_binding_level->names

499          = decls = nreverse (current_binding_level->names);

500      else

501        decls = current_binding_level ->names;

502 

503      /* Output any nested inline functions within this block

504         if they weren't already output.  */

505      for (decl = decls; decl; decl = TREE_CHAIN (decl))

506         if (TREE_CODE (decl) == FUNCTION_DECL

507            && ! TREE_ASM_WRITTEN (decl)

508           && DECL_INITIAL (decl) != NULL_TREE

509            && TREE_ADDRESSABLE (decl)

510           && decl_function_context (decl) == current_function_decl )

511      {

512         /* If this decl was copied from a file-scope decl

513           on account of a block-scope extern decl,

514            propagate TREE_ADDRESSABLE to the file-scope decl.  */

515         if (DECL_ABSTRACT_ORIGIN (decl) != NULL_TREE)

516           TREE_ADDRESSABLE (DECL_ABSTRACT_ORIGIN (decl)) = 1;

517        else

518         {

519           push_function_context ();

520           output_inline_function (decl);

521           pop_function_context ();

522         }

523      }

524 

525      /* When not in function-at-a-time mode, expand_end_bindings will

526        warn about unused variables. But, in function-at-a-time mode

527        expand_end_bindings is not passed the list of variables in the

528        current scope, and therefore no warning is emitted. So, we

529        explicitly warn here.  */

530      if (!processing_template_decl )

531        warn_about_unused_variables (getdecls ());

532   

533      /* If there were any declarations or structure tags in that level,

534        or if this level is a function body,

535        create a BLOCK to record them for the life of this function.  */

536      block = NULL_TREE;

537      if (keep == 1 || functionbody)

538        block = make_node (BLOCK);

539      if (block != NULL_TREE)

540      {

541        BLOCK_VARS (block) = decls;

542        BLOCK_SUBBLOCKS (block) = subblocks;

543      }

544   

545      /* In each subblock, record that this is its superior.  */

546      if (keep >= 0)

547         for (link = subblocks; link; link = TREE_CHAIN (link))

548           BLOCK_SUPERCONTEXT (link) = block;

 

do_poplevel 来调用时, poplevel 的参数 reverse 1 ,其他情况下都是 0 。前面看到 current_binding_level names 域总是记录了在该作用域中声明的名字,而且用作栈。现在是时候恢复其声明时的次序了。

GCC 支持嵌套函数,不过被嵌套的函数必须是内联的。 505 行的 FOR 循环对这些函数产生汇编代码(彼时其 TREE_ASM_WRITTEN 将被设为 1 )。

上面 542 行的 subblocks 来自 current_binding_level->blocks ,为当前作用域中所出现的语句块。注意 541 行的 decls ,它来自已恢复声明时次序的 current_binding_level->names 。上面构建了块与子块间的关系。

 

poplevel (continue)

 

550     /* We still support the old for-scope rules, whereby the variables

551        in a for-init statement were in scope after the for-statement

552         ended. We only use the new rules if flag_new_for_scope is

553         nonzero.  */

554      leaving_for_scope

555         = current_binding_level->kind == sk_for && flag_new_for_scope == 1;

556 

557      /* Remove declarations for all the DECLs in this level.  */

558      for (link = decls; link; link = TREE_CHAIN (link))

559      {

560        if (leaving_for_scope && TREE_CODE (link) == VAR_DECL

561           && DECL_NAME (link))

562        {

563             cxx_binding *outer_binding

564             = IDENTIFIER_BINDING (DECL_NAME (link))->previous;

565          tree ns_binding;

566 

567          if (!outer_binding)

568              ns_binding = IDENTIFIER_NAMESPACE_VALUE (DECL_NAME (link));

569          else

570            ns_binding = NULL_TREE;

571 

572          if (outer_binding

573               && outer_binding->scope == current_binding_level->level_chain)

574             /* We have something like:

575 

576              int i;

577              for (int i; ;);

578 

579                and we are leaving the `for' scope. There's no reason to

580                keep the binding of the inner `i' in this case.  */

581            pop_binding (DECL_NAME (link), link);

582          else if ((outer_binding

583                      && (TREE_CODE (outer_binding->value) == TYPE_DECL))

584                || (ns_binding && TREE_CODE (ns_binding) == TYPE_DECL))

585             /* Here, we have something like:

586 

587                typedef int I;

588 

589              void f () {

590                for (int I; ;);

591                }

592 

593                We must pop the for-scope binding so we know what's a

594              type and what isn't.  */

595              pop_binding (DECL_NAME (link), link);

596          else

597          {

598              /* Mark this VAR_DECL as dead so that we can tell we left it

599              there only for backward compatibility.  */

600            DECL_DEAD_FOR_LOCAL (link) = 1;

601 

602              /* Keep track of what should have happened when we

603                popped the binding.  */

604            if (outer_binding && outer_binding->value)

605               DECL_SHADOWED_FOR_VAR (link) = outer_binding->value;

606 

607            /* Add it to the list of dead variables in the next

608              outermost binding to that we can remove these when we

609              leave that binding.  */

610              current_binding_level->level_chain->dead_vars_from_for

611                = tree_cons (NULL_TREE, link,

612                           current_binding_level->level_chain->

613                           dead_vars_from_for);

614 

615            /* Although we don't pop the cxx_binding, we do clear

616              its SCOPE since the scope is going away now.  */

617              IDENTIFIER_BINDING (DECL_NAME (link))->scope = NULL;

618            }

619        }

620        else

621        {

622          /* Remove the binding.  */

623          decl = link;

624          if (TREE_CODE (decl) == TREE_LIST)

625            decl = TREE_VALUE (decl);

626          if (DECL_P (decl))

627            pop_binding (DECL_NAME (decl), decl);

628          else if (TREE_CODE (decl) == OVERLOAD)

629            pop_binding (DECL_NAME (OVL_FUNCTION (decl)), decl);

630          else

631            abort ();

632        }

633      }

634 

635      /* Remove declarations for any `for' variables from inner scopes

636         that we kept around.  */

637      for (link = current_binding_level->dead_vars_from_for;

638           link; link = TREE_CHAIN (link))

639        pop_binding (DECL_NAME (TREE_VALUE (link)), TREE_VALUE (link));

 

C++ 里, FOR 循环与 WHILE DO WHILE 循环的不同之处,在于它有 for-init-statement 部分。 GCC 在处理 FOR 循环时,首先为 FOR 循环构建一个 sk_for 的作用域,随后在看到“(”时,再加入一个 sk_block 作用域用于 FOR 循环体。那么 sk_for 作用域中 names 所记录的即是在 for-init-statement 部分中声明的变量。因此, 560 行的 IF 块,就是处理这样的变量声明。 572 行及 582 行条件所筛选的情形,其注释中已经显示得很清楚。而 596 行的 ELSE 块,对应的是, 1 )其名字不出现在外部作用域; 2 )其名字出现在外部作用域,但不是紧邻的这个。这里还将其保存 DECL_SHADOWED_FOR_VAR 中,是因为在 ISO 标准出来之前, for-init-statement 中声明的变量的作用域是 FOR 循环所在作用域,从声明点开始。考虑:

int i;

void g() {

for (int i = 0; i < 10; ++i) {}

extern void f(int j = i);

}

编译器将给出如下警告:

:4: warning: name lookup of `i' changed

:1: warning:   matches this `i' under ISO standard rules

:3: warning:   matches this `i' under old rules

DECL_SHADOWED_FOR_VAR 是编译器给出警告的根据。

另外,为了使得这些声明不为名字查找所见,关联的 cxx_binding 节点都被移除。

 

poplevel (continue)

 

641      /* Restore the IDENTIFIER_TYPE_VALUEs.  */

642      for (link = current_binding_level ->type_shadowed;

643          link; link = TREE_CHAIN (link))

644        SET_IDENTIFIER_TYPE_VALUE (TREE_PURPOSE (link), TREE_VALUE (link));

645 

646      /* Restore the IDENTIFIER_LABEL_VALUEs for local labels.  */

647      for (link = current_binding_level->shadowed_labels;

648           link;

649           link = TREE_CHAIN (link))

650         pop_label (TREE_VALUE (link), TREE_PURPOSE (link));

651 

652      /* There may be OVERLOADs (wrapped in TREE_LISTs) on the BLOCK_VARs

653        list if a `using' declaration put them there. The debugging

654        back-ends won't understand OVERLOAD, so we remove them here.

655        Because the BLOCK_VARS are (temporarily) shared with

656        CURRENT_BINDING_LEVEL->NAMES we must do this fixup after we have

657        popped all the bindings.  */

658      if (block)

659      {

660        tree* d;

661   

662        for (d = &BLOCK_VARS (block); *d; )

663        {

664          if (TREE_CODE (*d) == TREE_LIST)

665            *d = TREE_CHAIN (*d);

666          else

667            d = &TREE_CHAIN (*d);

668        }

669      }

670   

671      /* If the level being exited is the top level of a function,

672        check over all the labels.  */

673      if (functionbody)

674      {

675        /* Since this is the top level block of a function, the vars are

676          the function's parameters. Don't leave them in the BLOCK

677           because they are found in the FUNCTION_DECL instead.  */

678        BLOCK_VARS (block) = 0;

679        pop_labels (block);

680      }

681   

682      kind = current_binding_level ->kind;

683   

684      leave_scope ();

685      if (functionbody)

686        DECL_INITIAL (current_function_decl ) = block;

687      else if (block)

688        current_binding_level ->blocks

689          = chainon ( current_binding_level ->blocks, block);

690   

691      /* If we did not make a block for the level just exited,

692        any blocks made for inner levels

693        (since they cannot be recorded as subblocks in that level)

694        must be carried forward so they will later become subblocks

695        of something else.  */

696      else if (subblocks)

697         current_binding_level ->blocks

698          = chainon ( current_binding_level ->blocks, subblocks);

699   

700      /* Each and every BLOCK node created here in `poplevel' is important

701        (e.g. for proper debugging information) so if we created one

702        earlier, mark it as "used".  */

703      if (block)

704        TREE_USED (block) = 1;

705   

706      /* Take care of compiler's internal binding structures.  */

707      if (kind == sk_cleanup)

708      {

709        tree scope_stmts;

710   

711         scope_stmts

712             = add_scope_stmt (/*begin_p=*/ 0, /*partial_p=*/ 1);

713        if (block)

714        {

715          SCOPE_STMT_BLOCK (TREE_PURPOSE (scope_stmts)) = block;

716          SCOPE_STMT_BLOCK (TREE_VALUE (scope_stmts)) = block;

717        }

718   

719        block = poplevel (keep, reverse, functionbody);

720      }

721   

722      POP_TIMEVAR_AND_RETURN (TV_NAME_LOOKUP, block);

723    }

 

注意 678 行,如果 functionbody 不为 0 ,我们退出的是 sk_function_parms 作用域,在这个作用域中只有函数参数,这些项不需要记录在 BLOCK 中,因为通过 FUNCTION_DECL 可以找到它们。在 684 行,执行 level_scope ,使得 current_binding_level 指向上一级作用域。那么看到在 686 行, FUNCTION_DECL DECL_INITIAL 域,保存了在其中的块结构。最后这个块结构被返回,注意如果是空函数,这个块可能是 NULL

 

do_poplevel (continue)

 

346        if (!processing_template_decl )

347        {

348          /* This needs to come after the poplevel so that partial scopes

349             are properly nested.  */

350          scope_stmts = add_scope_stmt (/*begin_p=*/ 0, /*partial_p=*/ 0);

351          if (block)

352          {

353            SCOPE_STMT_BLOCK (TREE_PURPOSE (scope_stmts)) = block;

354            SCOPE_STMT_BLOCK (TREE_VALUE (scope_stmts)) = block;

355          }

356        }

357      }

358   

359      return block;

360    }

 

回到 do_poplevel ,注意 351 行的 block 即是 poplevel 返回在 722 行的 block 。在该函数的 350 行,封闭了 do_pushlevel 所引入的域。看到这个作用域的内容就是 block

 

finish_compound_stmt (continue)

 

1040     RECHAIN_STMTS (compound_stmt, COMPOUND_BODY (compound_stmt));

1041  

1042     /* When we call finish_stmt we will lose LAST_EXPR_TYPE. But, since

1043       the precise purpose of that variable is store the type of the

1044       last expression statement within the last compound statement, we

1045       preserve the value.  */

1046     t = last_expr_type;

1047     finish_stmt ();

1048     last_expr_type = t;

1049  

1050     return r;

1051   }

 

上面的宏 RECHAIN_STMTS 重新安排 COMPOUND_STMT 节点,把属于它的语句链表从 chain 域移到 operands[0] 域。

 

315    #define RECHAIN_STMTS (stmt, substmt)              /                           in c-common.h

316      do {                                     /

317        substmt = TREE_CHAIN (stmt);              /

318        TREE_CHAIN (stmt) = NULL_TREE;            /

319        last_tree = stmt;                        /

320      } while (0)

 

很奇怪 1046 1048 行的代码实际上不做任何事。

 

11252 void

11253 finish_stmt (void)                                                                                          in decl.c

11254 {

11255   /* Always assume this statement was not an expression statement. If

11256     it actually was an expression statement, its our callers

11257     responsibility to fix this up.  */

11258   last_expr_type = NULL_TREE;

11259 }

 

此时,下列节点将为所解析的函数体所创建。此处忽略了函数体内语句对应的节点,它们在图形中由‘ ’代表。不过,它们的形式与这个最外层的 COMPOUND_STMT 类似。以后我们会看到这些节点的细节。

点此打开

95 :为解析函数体构建的节点

类模板的方法与非类模板方法的区别是很大的。对于非类模板的方法,看到对于函数中的每一个作用域都会构建一对 SCOPE_STMT ,一个 tree_list 将持有它们来记录这个作用域的有效范围。另外, current_scope_stmt_stack 记录保存栈中的作用域。 finish_compound_stmt 的每次调用将对栈进行弹出查找,进入上一级作用域。

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值