ISO-ANSI C/C++ 预处理器测试文本

ISO-ANSI C/C++ 预处理器测试文本

http://blog.csdn.net/huyansoft/archive/2009/04/01/4039976.aspx

 

A test file for my ISO C/C++ Preprocessor
Copyright(c) 2008, HuYan
All rights reserved.

Trigraph------------------------------------------------------------------------------------------------------------------------

in normal text
int a ??( ??) = ??< 1, 2, 3 ??>;    //equivalent to next line
int a [ ] = { 1, 2, 3 };

in preprocessing directives
??=define MACRO_TRIGRAPH(a,b)  a ??=??= b    //equivalent to next line
#define MACRO_TRIGRAPH(a,b)  a ## b

in comments
/* ??=  ??)  ??!  ??(  ??'  ??>  ??/n  ??<  ??- */    //equivalent to next line
/* #  ]  |  [  ^  }  /n  {  ~ */

in backslash
ch??/
ar* s="string ??/
literal";

equivalent to
ch/
ar* s="string /
literal";

and equivalent to
char* s="string literal";

Backslash------------------------------------------------------------------------------------------------------------------------

in normal text
ch/
ar* s="string /
literal";

equivalent to
char* s="string literal";

in preprocessing directives
#/
defi/
ne MACRO_/
BACKSLASH/
(a,b)  a #/
# b

equivalent to
#define MACRO_BACKSLASH(a,b)  a ## b

in comments
//a single-/
line comment

//
/a single-line comment

//
*a multi-line comment*/
/

in the end of file
next backslash is unexpected if it is the last character of this file/

next backslash is unexpected if next newline is the end of this file/


Single-line comment-----------------------------------------------------------------------------------------------------------------------

in characters constant and string literal
'a//b'    //a characters constant, not syntax error
"a//b"    //4 characters string literal, not syntax error

between tokens (replacement to a space)
m=n//**/o     //equivalent to m=n , not equivalent to m=n/o

in header name (error)
#include <file.h//>    //undefined behavior, not equivalent to #include <file.h>
#include "file.h//"    //undefined behavior, not equivalent to #include <file.h>

attempt defined it as macro (error)
#define SINGLE_LINE_COMMENT //
SINGLE_LINE_COMMENT abc    //error, 'abc' not comment

#define GLUE(x,y) x##y
GLUE(/,/) abc     //error, 'abc' not comment

Multi-line comment------------------------------------------------------------------------------------------------------------------------

in characters constant and string literal
'a//b'    //a characters constant, not syntax error
"a/**/b"    //6 characters string literal, not syntax error

between tokens (replacement to a space)
/**/#/**/define/**/MACRO_COMMENT/**/replacement/**/list/**/    //equivalent to next line
 # define MACRO_COMMENT replacement list

within punctuator or identifier (error)
i+/**/+    //equivalent to i+ +, not equivalent to i++
#incl/**/ude <file.h>    //error, equivalent to #incl ude <file.h>

in header name (error)
#include <file.h/**/>    //undefined behavior, not equivalent to #include <file.h>
#include "file.h/**/"    //undefined behavior, not equivalent to #include <file.h>

nesting
/*****************************/    //ok
/* // */    //ok,a multi-line comment
/* /**/ */    //error, multi-line comments do not nest ('*/' found outside of comment)

//    /*
*/    //error, '*/' found outside of comment

//attempt defined it as macro (error)
#define BEGIN_COMMENT /*
#define END_COMMENT */
BEGIN_COMMENT abc END_COMMENT    //error, 'abc' not comment

Identifier----------------------------------------------------------------------------------------------------------------------------------

_ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz    //an identifier
/u1234_/uABCD_/U00001234_/U0000abcd    //an identifier (contains universal character names)

Preprocessing operator or punctuator--------------------------------------------------------------------------------------------------------

parsing
>>=++++    //three punctuators, '>>=' and two '++'
x+++++y    //parsed as x ++ ++ + y (although this expression is invalid), not x ++ + ++ y

reserved operator
#define delete DELETE   //error, 'delete' is an operator in C++, not an identifier

Preprocessing number------------------------------------------------------------------------------------------------------------------------

familiar
123L   //valid preprocessing number, a long integer
.123   //valid preprocessing number, a floating number
0123   //valid preprocessing number, a octal number
0x123abc   //valid preprocessing number, a hexadecimal number
3.14e10   //valid preprocessing number
3.14e+10   //valid preprocessing number
3.14E-10   //valid preprocessing number

definition
123.456._.abc.ABC.e+.e-.E+.E-    //valid preprocessing number
.123.456._.abc.ABC.e+.e-.E+.E-    //valid preprocessing number
./u1234_/uABCD_/U00001234_/U0000abcd    //a preprocessing number (contains universal character names)

+123    //not a preprocessing number
-123    //not a preprocessing number
123+    //not a preprocessing number
123-    //not a preprocessing number

importance
#define X123  456
0X123    //'X123' is not an identifier and will not be expanded, because '0X123' is a preprocessing number

Header name-------------------------------------------------------------------------------------------------------------------------------

parsing
#include <file.h>=        //'>=' is not a punctuator because <file.h> is a header name
a < b && c > d        //< b && c > is not a header name
#if a < b && c > d        //< b && c > is not a header name
#endif

matching
#include <file.h    //error, missing matched separator
#include "file.h    //error, missing matched quotes
#include L"file.h"    //error, unexpected 'L'

unexpected characters in header names
#include <' / " // /* >    //undefined behavior
#include " ' /  // /* "    //undefined behavior

processing before string connecting
#define BASE_NAME "file"
#define EXTEND_NAME ".h"
#include BASE_NAME EXTEND_NAME    //equivalent to #include "file" ".h" , but not equivalent to #include "file.txt"

Character constant------------------------------------------------------------------------------------------------------------------------

empty character (error)
''    //error
L''    //error

matching
'    //error, missing matched single-quotes
L'    //error, missing matched single-quotes
'abc/'    //error, missing matched single-quotes, /' is only a escape sequence
L'abc/'    //error, missing matched single-quotes, /' is only a escape sequence

escape sequences
'/a /b /f /n /r /t /v /' /" /? //'    //valid escape sequences
'/c /d /e / /+'    //invalid escape sequences
' ? " '    //ok, may represented by ' /? /" '

value range of escape sequences
'/400'    //error, too large value of octal escape sequence
'/377'    //ok
L'/777'    //ok

'/x100'    //error, too large value of hexadecimal escape sequence
'/xff'   //ok
L'/x10000'    //error, too large value of escape hexadecimal sequence
L'/xffff'    //ok

String literal----------------------------------------------------------------------------------------------------------------------------

empty string
""    //ok
L""    //ok

matching
"    //error, missing matched quotes
L"    //error, missing matched quotes
"abc/"    //error, missing matched quotes, /" is only a escape sequence
L"abc/"   //error, missing matched quotes, /" is only a escape sequence

escape sequences
"/a /b /f /n /r /t /v /" /" /? //"    //valid escape sequences
"/c /d /e / /+"    //invalid escape sequences
" ? ' "    //ok, may represented by " /? /' "

value range of escape sequences
"/400"    //error, too large value of octal escape sequence
"/377"    //ok
L"/777"    //ok

"/x100"    //error, too large value of hexadecimal escape sequence
"/xff"   //ok
L"/x10000"    //error, too large value of escape hexadecimal sequence
L"/xffff"    //ok

Universal character name-----------------------------------------------------------------------------------------------------------------------

within identifier
/u1234_/uABCD_/U00001234_/U0000abcd    //valid

within character constant
'/u1234_/uABCD_/U00001234_/U0000abcd'    //valid

within string literal
"/u1234_/uABCD_/U00001234_/U0000abcd"    //valid

within preprocessing number
./u1234_/uABCD_/U00001234_/U0000abcd    //valid

value range
/u0065_/U00000065    //error, a universal character name cannot designates a basic source character (uncompleted)
'/u0065 /U00000065'    //error, a universal character name cannot designates a basic source character (uncompleted)
"/u0065 /U00000065"    //error, a universal character name cannot designates a basic source character (uncompleted)
./u0065_/U00000065    //error, a universal character name cannot designates a basic source character (uncompleted)

/u0019_/u007f_/u009f_/U00000019_/U0000007f_/U0000009f    //error, a universal character name cannot less than 0x20, or in the range 0x7F-0x9F
'/u0019 /u007f /u009f /U00000019 /U0000007f /U0000009f'    //error, a universal character name cannot less than 0x20, or in the range 0x7F-0x9F
"/u0019 /u007f /u009f /U00000019 /U0000007f /U0000009f"    //error, a universal character name cannot less than 0x20, or in the range 0x7F-0x9F
./u0019_/u007f_/u009f_/U00000019_/U0000007f_/U0000009f    //error, a universal character name cannot less than 0x20, or in the range 0x7F-0x9F

Preprocessing directive and normal text--------------------------------------------------------------------------------------------------------

null directive
#    //no effect

normal text
#define EMPTY
EMPTY #include <file.h>    //a text line, will expend to #include <file.h> but not a preprocessing directive

#define INCLUDE1 #include <file.h>
INCLUDE1    //a text line, will expend to #include <file.h> but not a preprocessing directive

#define INCLUDE2 #include
INCLUDE2 <file.h>    //a text line, will expend to #include <file.h> but not a preprocessing directive

directive name
#define INCLUDE3 include <file.h>
#INCLUDE3    //error, nonexistent directive name

#define INCLUDE4 include
#INCLUDE4 <file.h>    //error, nonexistent directive name

#define pragma error
#pragma "abc"    //the directive name is 'pragma' but not 'error', because the replacement was not occured

Source file inclusion---------------------------------------------------------------------------------------------------------------------------

#define MAKE_HEADER(s) #s
#include MAKE_HEADER(file.h)     //ok, macro expanded, the header name is "file.h"

//#include "stdio.h"    //working but warning

//#line __LINE__ "new.h"    //next line is error even if current line is reserved
#include "test.txt"    //error, cannot include itselt, though current file name was modified to "new.h" by last line

#include "b.txt"    //begin a recursive inclusion, b.txt and c.txt will include each other

Conditional inclusion---------------------------------------------------------------------------------------------------------------------------

syntax checking in skipped groups
#if 0                            //checking carefully and responsibly even if in skipped groups
    "abc                         //fogot a quotes
    '/400'                       //too large value of octal escape sequence
    #include <d:/file.h>         //unexpected '/' character
    #define PI3.14               //forgot spaces after PI
    #define MAX(a,a)  a>b?a:b    //careless spelling of second parameter
    #define defined otherwise    //do not know identifier 'defined' is reserved
    #line "file.h" 100           //two arguments reversed
#elsc                            //careless spelling, follow lines before #endif will be ignored unecpectly
    #define PI 3.14              //ignored unecpectly
#endif

unreplaced macros
#define UNREPLACED something

#ifdef UNREPLACED    //UNREPLACED will not be expanded
#endif

#ifndef UNREPLACED    //UNREPLACED will not be expanded
#endif

#if defined(UNREPLACED)    //UNREPLACED will not be expanded
#elif defined(UNREPLACED)    //UNREPLACED will not be expanded
#endif

matching
#endif    //error, missing matched 'if' branch
#else    //error, missing matched 'if' branch
#elif 1    //error, missing matched 'if' branch

#if 0
#elif 0
#else
//#elif 0    //error if this line reserved, 'elif' branch shall not occur after 'else' branch
#endif    //dismatch if this line is removed

branch testing
#define A
#ifndef A
 #ifdef B
  a if
 #elif 0
  a elif a
 #elif 1
  a elif b
 #else
  a else
 #endif
#elif 0
 #ifdef b
  b if
 #elif 0
  b elif a
 #elif 1
  b elif b
 #else
  b else
 #endif
#elif 1
 #ifdef c
  c if
 #elif 0
  c elif a
 #elif 1
  c elif b
 #else
  c else
 #endif
#else
 #ifdef d
  d if
 #elif 1
  d elif a
 #elif 1
  d elif b
 #else
  d else
 #endif
#endif

Macro definition-----------------------------------------------------------------------------------------------------------------------------

white separator
#define PI3.14    //invalid directive format, forgeting space after macro name
#define PI()3.14    //invalid directive format, forgeting space after ')'
#define MACRO_MAX (a,b) ((a)>(b)?(a):(b))    //will be defined as object-like macro, not function-like macro

redefinition
#define ERR_PARA(a,a)  something        //error, parameter 'a' redefined

#define OBJECT_LIKE something
#define OBJECT_LIKE() something    //error, cannot redefine object-like macro as function-like macro

#define FUNCTION_LIKE() something
#define FUNCTION_LIKE something    //error, cannot redefine function-like macro as object-like macro

#define OBJ_LIKE (1)
#define OBJ_LIKE (0)    //error, redefine with different token sequence in replacement list

#define FUNC_LIKE(a) something
#define FUNC_LIKE(b) something    //error, redefine with different parameter list

#define MACRO_CONNECT(a,b,c) a##b##c
#define MACRO_CONNECT( a , b , c )  a ## b ## c    //ok, the same definition to MACRO_CONNECT (uncompleted)

Macro invocation and replacement-----------------------------------------------------------------------------------------------------------------------------

#define MULTI_ARGS(a,b) b,a
MULTI_ARGS    //valid text, but not a macro invocation

arguments number
MULTI_ARGS()    //warning, not enough arguments
MULTI_ARGS(1,)    //valid, the second argument is empty
MULTI_ARGS(,2)    //valid, the first argument is empty
MULTI_ARGS(,)    //valid, both arguments are empty
MULTI_ARGS(1,2,3,4)    //valid, though arguments too much

whites (spaces, tabs and newlines) within arguments
MULTI_ARGS (    //valid, though whites involved
  1  , 
  2  )

quotes matching
//MULTI_ARGS(a,b    //error, missing matched ')'
//MULTI_ARGS( isdigit(32), isdigit(65)    //error, missing matched ')'
MULTI_ARGS( isdigit(32), isdigit(65) )    //valid, four middle parentheses skipped
MULTI_ARGS( MULTI_ARGS(1,2), MULTI_ARGS(3,4) )    //valid, nesting invocation (will expand to 4,3,2,1)
MULTI_ARGS(
    1,
    #define PI 3.14    //error, preprocessing directives cannot occur within a macro invocation
    )

replacement time
#define MACROX 2
#define MACROY MACROX    //MACROX does not replaced to 2 now
#undef  MACROX
#define MACROX 3
int i=MACROY;    //replacement occurs, MACROY will expanded to 3 but not 2

The # operator (string making)----------------------------------------------------------------------------------------------------------------

definition
#define ERR_JIN1(a) #    //error, # should be followed by a parameter in the replacement list of a function-like macro (uncompleted)
#define ERR_JIN2(a) a#b    //error, # should be followed by a parameter in the replacement list of a function-like macro (uncompleted)

whites (spaces, tabs and newlines) within arguments
#define MKS(s) #s
MKS(abc)    //expands to "abc"
MKS(  abc  )    //expands to "abc", whites before the first token and after the last token within the argument is deleted
MKS(
abc
)    //expands to "abc", (uncompleted)
MKS(  a 
  b 
  c  )    //expands to "a b c", each white within the argument becomes a single space

arguments substitution
#define S1(s) #s
#define S2(s) S1(s)
#define MYNAME huyan
S1( MYNAME )    //expands to "MYNAME"
S2( MYNAME )    //expands to "huyan"

#define FORMAT(x) #x "=%d/n",x
#define MYNUM 100
printf( FORMAT(MYNUM) )    //expands to printf( "MYNUM" "=%d/n",100 ), not printf( "100" "=%d/n",100 )

making valid string literal
#define S3(s) #s
printf("%s", S3(a/nb "a/nb" // '//' /n '/n'));    //expands to next line
printf("%s", "a/nb /"a//nb/" // '' /n '//n'");    //not next sick line
//printf("%s", "a/nb "a/nb" // '//' /n '/n'");    //error, dismatched quotes

The ## operator (token connection)------------------------------------------------------------------------------------------------------------

definition
#define ERR_JINJIN1(a) ## a    //error, ## cannot occur at the beginning of the replacement list
#define ERR_JINJIN2(a) a ##    //error, ## cannot occur at the end of the replacement list

#define hash_hash1 ##    //error, cannot define ## to a macro directly
#define hash_hash2 # ## #    //ok
hash_hash2    //expands to ##

arguments substitution
#define CONNECT(x) I ## x
#define CONNECT2(x) CONNECT(x)
#define S(a) a
#define IS(a) a
int I2=5;
CONNECT( S(1) )    //expands to IS(1), and further expands to 1
CONNECT2( S(2) )    //expands to CONNECT2( 2 ), further expands to CONNECT( 2 ), and last expands to I2

example from ISO C Standard
#define hash_hash # ## #
#define mkstr(a) # a
#define in_between(a) mkstr(a)
#define join(c, d) in_between(c hash_hash d)
char p[] = join(x, y);    //equivalent to char p[] = "x ## y";

Complex macro replacement (rescanning and further replacement)---------------------------------------------------------------------------------

#define MACROS(a) a
#define MACROT(b) b*b
MACROS ( MACROT ) ( 3 )    //expands to 3*3

int M1=3;
#define M1 M2
#define M2 M1*M1    //recursive definition
printf("%d/n",M1);    //expands to M1*M1 and output 9

#define Puts(s)  printf("%s is ",#s); Puts(s)
char translator[]={-70,-6,-47,-27,0};
Puts(translator);    //expands to  printf("%s is","translator"); Puts(translator);

example from ISO C Standard
#define X       3
#define F(a)    F(X  *  (a))
#undef  X
#define X       2
#define G       F
#define Z       Z[0]
#define H       G(~
#define M(a)    a(W)
#define W       0,1
#define T(a)    a
#define P()     int
#define Q(x)    x
#define R(x,y)  x ## y
#define STR(x)  # x

F(y+1) + F(F(Z)) % T(T(G)(0) + T) (1);                //expands to F(2 * (y+1)) + F(2 * (F(2 * (Z[0])))) % F(2 * (0)) + T(1);
G(X+(3,4)-W) | H 5) & M(F)^M(M);                      //expands to F(2 * (2+(3,4)-0,1)) | F(2 * (~ 5)) & F(2 * (0,1))^M(0,1);
P() i[Q()] = { Q(1), R(2,3), R(4,), R(,5), R(,) };    //expands to int i[] = { 1, 23, 4, 5,  };
char c[2][6] = { STR(hello), STR() };                 //expands to char c[2][6] = { "hello", "" };

#define Str(s) # s
#define Xstr(s) Str(s)
#define Debug(s, t) printf("x" # s "= %d, x" # t "= %s", x ## s, x ## t)
#define INCFILE(n) vers ## n
#define Glue(a, b) a ## b
#define Xglue(a, b) Glue(a, b)
#define HIGHLOW "hello"
#define LOW LOW ", world"

Debug(1, 2);                                                      //expands to printf("x" "1" "= %d, x" "2" "= %s", x1, x2);
fputs(Str(strncmp("abc/0d", "abc", '/4') == 0) Str(: @/n), s);    //expands to fputs("strncmp(/"abc//0d/", /"abc/", '//4') == 0" ": @/n",s);
#include Xstr(INCFILE(2).h)                                       //expands to #include "vers2.h"
Glue(HIGH, LOW);                                                  //expands to "hello";
Xglue(HIGH, LOW)                                                  //expands to "hello" ", world"

Macro with variable arguments-----------------------------------------------------------------------------------------------------------------------

definition
#define ERR_VAM1  __VA_ARGS__    //error, unexpected __VA_ARGS__ in the replacement list of normal macro
#define ERR_VAM2(a,b,c) __VA_ARGS__     //error, unexpected __VA_ARGS__ in the replacement list of normal macro
#define ERR_VAM3(a,b,c,...) a,b,c       //warning, missing __VA_ARGS__ in the replacement list of variable arguments macro

#define ERR_VAM4(__VA_ARGS__) something     //error, the parameter cannot be __VA_ARGS__
#define ERR_VAM5(__VA_ARGS__,...) __VA_ARGS__     //error, the parameter cannot be __VA_ARGS__

#define __VA_ARGS__  otherwise    //error, the identifier '__VA_ARGS__' is reserved
#undef __VA_ARGS__    //error, the identifier '__VA_ARGS__' is reserved

#define VAM1(...)  __VA_ARGS__    //ok
#define VAM2(a,b,c,...) __VA_ARGS__    //ok

arguments substitution
#define VAM3(...)  #__VA_ARGS__
VAM3()    //expands to ""
VAM3(1)    //expands to "1"
VAM3(a,b,c)    //expands to "a,b,c"

#define VAM4(a,b,...) a b #__VA_ARGS__
VAM4(1)    //warning, not enough arguments
VAM4(1,2)    //expands to 1 2 ""
VAM4(1,2,3,4,5)    //expands to 1 2 "3,4,5"
VAM4( ((a), (b)), ((c), (d)), ((e),(f)), ((g),(h)) )    //expands to ((a), (b)) ((c), (d)) "((e),(f)), ((g),(h))"

example from ISO C Standard
#define Debug2(...) fprintf(stderr, __VA_ARGS__)
#define Showlist(...) puts(#__VA_ARGS__)
#define Report(test, ...) ((test)?puts(#test):printf(__VA_ARGS__))

Debug2("Flag");                                   //expands to fprintf(stderr, "Flag" );
Debug2("x = %d/n", x);                            //expands to fprintf(stderr, "x = %d/n", x );
Showlist(The first, second, and third items.);    //expands to puts( "The first, second, and third items." );
Report(x>y, "x is %d but y is %d", x, y);         //expands to ((x>y)?puts("x>y"):printf("x is %d but y is %d", x, y));

Predefined macro names and reserved identifiers------------------------------------------------------------------------------------------------

following predefined macros will be expanded
__DATE__
__FILE__
__LINE__
__STDC__
__STDC_HOSTED__
__STDC_VERSION__
__TIME__

redefine and undefine (error)
#define __DATE__  otherwise       //error, cannot redefine predefined macros
#define defined  otherwise        //error, the identifier 'defined' is reserved

#undef __DATE__       //error, cannot undefine predefined macros
#undef defined        //error, the identifier 'defined' is reserved

Line control-----------------------------------------------------------------------------------------------------------------------------------

//#line 100    //set next line number to 100
//#line 200 "file1.h"    //set next line number to 200 and current file name to "file1.h"

expanded macro arguments
#define LINE_NUMBER1 300
//#line LINE_NUMBER1    //set next line number to 300 (the macro LINE_NUMBER1 expanded)

#define LINE_NUMBER2 400
//#line LINE_NUMBER2 "file2.h"    //set next line number to 400 and current file name to "file2.h"

#define FILE_NAME "file3.h"
//#line 500 FILE_NAME    //set next line number to 500 and current file name to "file3.h"

#define LINE_FILE 600 "test.h"
//#line LINE_FILE    //set next line number to 600 and current file name to "test.h"

arguments format
#line 0    //error, the line number cannot less than 1
#line 2147483648    //error, the line number cannot more than 2147483647
#line +100    //error, the line number need not prefixed by '+' or '-'
#line "file.h" 100    //error, the first argument shall be a number (after macro replacement)
#line 100  L"file.h"    //error, the file name need not prefixed by 'L' (cannot be wide string literal)
//#line 100  "file.h/"    //error, missing matched quotes, the /" is only a escape sequence

counting of line number
//#line 100    //set next line number to 100, but next broken '__LINE__' will expands to 101
__LINE/
__ ;

Error directive------------------------------------------------------------------------------------------------------------------------------

//#error    //stop preprocessing
//#error message1    //stop preprocessing and output 'message1'

expanded macro arguments
#define MESSAGE2 message2
//#error MESSAGE2    //macro expanded, stop preprocessing and output 'message2'

#define MAX_VALUE 100
//#error the "MAX_VALUE" cannot more than MAX_VALUE    //stop preprocessing and output 'the "MAX_VALUE" cannot more than 100'

Pragma directive-----------------------------------------------------------------------------------------------------------------------------

#pragma some_instructions    //valid, but all #pragma directives will be ignored and removed

_Pragma operator (uncompleted)
_Pragma ("string literal")    //it will regard as normal text

[The End]

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值