C语言顺序、选择、循环结构与预处理

C语言程序设计,知识一遍过

🔥温馨提示🔥:使用电脑端阅读,获取更好体验🚀

【参考】C 语言教程 | 菜鸟教程 (runoob.com)

文章目录

  • C语言程序设计,知识一遍过
    • 顺序结构
      • 表达式语句
      • 字符的输入与输出
      • 格式化输入/输出函数
    • 选择结构
      • 关系运算符和关系表达式
      • 逻辑运算符和逻辑表达式
      • if语句及其构成的选择结构
      • 条件运算符构成的选择结构
      • switch语句
      • switch语句和 break 语句组成的选择结构
    • 循环结构
      • while 和 do…while 循环结构
      • for 循环结构
      • 循环结构的嵌套
      • continue和break语句
    • 预处理指令
      • 宏定义
        • 不带参数的宏定义
        • 带参数的宏定义
      • 条件编译
      • 文件包含
        • C头文件
        • 引用头文件的语法
        • 引用头文件的操作
        • 只引用一次头文件
        • 有条件引用

顺序结构

表达式语句

  1. 表达式语句:在表达式后面添加分号“ ;”,构成表达式语句。
    执行完表达式语句后,表达式的值会被丢弃。因此,若表达式不修改操作数的值,表达式语句就没有什么实际意义。

  2. 复合语甸:把多个语句用花括号 “{ }” 括起来所构成的一个语句组。
    一个复合语句在语法上视为一条语句。
    比如:{i=1; i++; a=i;}

  3. 空语句:只有一个分号 “;” 组成的语句。
    比如:main() { ; } 其中的分号是一个空语句,但程序执行时不产生任何动作。

字符的输入与输出

  1. 字符输出函数 putchar()
    格式:putchar(ch),其作用是向终端输出一个字符。

  2. 字符输入函数 getchar( )
    功能:是从终端输人一个字符。

getchar() 函数没有参数,函数值就是从输入设备得到的字符。

格式化输入/输出函数

  1. 输出函数 printf()
    printf() 函数是C语言提供的标准输出函数,它的作用是向终端(或系统隐含指定的输出设备)按指定格式输出的若干数据。
    一般形式:printf(格式控制, 输出列表);
    格式控制:是用双引号括起来的字符串。它包括两种信息:

    • 格式转换说明:由 “%” 和格式字符组成。

    • 需要原样输出的字符也写在格式控制内。

    输出列表:需要输出的一些数,可以是常量、变量或表达式。输出列表中的各输出项用逗号隔开。

格式控制符描述示例
%d%i输出十进制整数printf("%d", 123); 输出 123
%u输出无符号十进制整数printf("%u", 456U); 输出 456
%o输出八进制整数printf("%o", 0123); 输出 123
%x%X输出十六进制整数(小写/大写)printf("%x", 0xabc); 输出 abc
%f输出浮点数(默认保留六位小数)printf("%f", 123.456); 输出 123.456000
%e%E以科学计数法输出浮点数printf("%e", 123.456); 输出 1.234560e+02
%g%G根据值的大小自动选择 %f%eprintf("%g", 123.456); 输出 123.456
%c输出一个字符printf("%c", 'A'); 输出 A
%s输出一个字符串printf("%s", "Hello"); 输出 Hello
%%输出一个 % 符号printf("%%"); 输出 %
%p输出指针的值以十六进制表示int a=3,*p=&a,printf("%p", p); 输出 变量a的地址
%zu输出 sizeof 的结果printf("%zu",sizeof(int)); 输出 4

此外,printf还允许通过额外的格式说明符来指定字段宽度、精度、对齐方式等,例如:

  • %10d:输出宽度为10的十进制整数,不足部分用空格填充。
  • %-10d:输出宽度为10的十进制整数,不足部分用空格填充,且左对齐。
  • %.2f:输出浮点数,保留两位小数。
  • %10.2f:输出宽度为10的浮点数,包括小数点和小数部分,总共保留两位小数,不足部分用空格填充。

这些格式控制符可以组合使用,以满足不同的输出需求。

格式说明与输出项在类型上与个数上保持一致。

  1. 输入函数 scanf()
    一般形式:scanf(格式控制,地址表列);
    格式控制:同 printf() 函数。
    地址表列:由若干个变量地址组成,既可以是变量的地址,也可以是字符串的首地址。
    比如:scanf(“%d%f%If”,&a,&b,&e),输入整型变量a、单精度型变量b、双精度型变量c
    说明:

    • scanf() 函数中的格式说明也是以%开始,以一个格式字符结束,中间可以加入附加的字符。

    • 对 unsigned 型变量的数据,可以用%d、%o、%x格式输入。

    • 在 scanf() 函数中格式字符前可以用一个整数指定输入数据所占宽度,但对于输入实型数则不能指定其小数位的宽度。

    • 在格式控制串中,格式说明的个数应该与输入项的个数相等且要类型匹配。

  • scanf() 函数中的输入项只能是地址表达式,而不能是变量名或其他内容。

  • 如果在 "格式控制” 字符串中除了格式说明以外还有其他字符,则在输入数据时应输入与这些宇符相同的字符。

  • 在用 “%c” 格式输入字符时,空格字符和转义字符都可作为有效字符输入。

  • 在输入数据时,若实际输入数据少于输入项个数,scanf()函数会等待输入,直到满足条件或遇到非法字符才结束;若实际输入数据多于输入项个数,多余的数据将留在缓冲区备用,作为下一次输入操作的数据。

  • 在输入数据时,遇到以下情况将认为输人结束:“空格” “回车” 或 “跳格” ( “Tab” )键,上述字符统一可称为“间隔符”。

选择结构

关系运算符和关系表达式

  1. 关系运算符
    C语言提供了6种关系运算符:小于(<)、小于等于(<=)、大于等于( >=)、大于(>)、等于(==)、不等于(!=)。
    结合性:自左向右。
    优先级:前4种关系运算符的优先级别相同,后2种优先级相同。前4种优先级高于后种。关系运算符的优先级低于算术运算符,高于赋值运算符。

  2. 关系表达式
    由关系运算符构成的表达式,称为关系表达式。关系运算符两边的运算对象可以是C语言语句中任意合法的表达式。比如:(a>b)!=c,a>=b,(a1)<(b2)都是合法的关系表达式。

  • 关系运算符的值为 “逻辑值”,只有两种可能值:整数0或者整数1。
  • 关系运算符两边值的类型不一致时,系统将自动转换。

逻辑运算符和逻辑表达式

  1. 逻辑运算符
    C语言提供了3种逻辑运算符:逻辑与(&&)、逻辑或(|)、逻辑非(!)。其中,“&&” 和 “||” 是双目运算符,而 “!” 是单目运算符,要求必须出现在运算对象的左边。
    结合性:自左至右。
    优先级:“!” > “&&” > “||” (非与或)。
    “!” > 算术运算符 > 关系运算符 > “&&” > “||” > 赋值运算符。

  2. 逻辑表达式
    逻辑表达式由逻辑运算符和运算对象组成。
    参与逻辑运算的对象可以是一个具体的值,还可以是C语言中任意合法的表达式。
    逻辑表达式的运算结果为1(真)或者为0(假)。
    A&&B 运算中,只有 A、B 同为真时才为真。同真为真,剩余为假(一假则假)。
    A||B 运算中,只有 A、B 同为假时才为假。同假为假,剩余为真(一真则真)。

关系运算符不能连用。比如:1<x<5。在数学中可以使用,但在这里不能使用,可改写成1<x && x<5。
&&和||的短路原则,以&&为例,要使(表达式1)&&(表达式2)结果为真则要求表达式1和表达式2都为真,如果表达式1为假,则不计算表达式2

if语句及其构成的选择结构

  1. if(表达式) 语句
    if是C语言的关键字。表达式两侧的括号不可少,并且只能是圆括号。
    紧跟着的语句,称为 “子句“,如果在 “子句” 中需要多个语句,则应该使用大括号把一组语句括起来构成复合语句。

  2. if(表达式) 语句1
    else 语句2
    上述语句含义是:如果表达式成立,执行语句1;否则执行语句2。

  3. if(表达式1) 语句1
    else if(表达式2) 语句2
    else if(表达式3) 语句3

    else 语句n
    上述语句含义是:满足表达式1时,执行语句1,不满足表达式1,但满足表达式2时,执行语句2,表达式1和2都不满足执行语句3,如果所有表达式都不成立,则执行语句n。

else 必须与 if 配对,共同组成 if…else 语句,这在程序题中需要注意。例如:

#include<stdio.h>
int main() {
    int a,b;
    printf("please input a,b: ");
    scanf("%d,%d",&a,&b);
    if(a == b)
        printf("a=b\n");
    else if(a>b)printf("a>b\n");
    else printf("a<b");
}

为了避免嵌套的if-else语句二义性,C语言规定else总是与在其前尚未配对的最近的if组成配对关系。

条件运算符构成的选择结构

条件运算符:?:

条件表达式的一般形式:表达式1 ? 表达式2 : 表达式3

求解过程:先求表达式1 的值,当表达式1 的值是非 0 时,以表达式2 的值作为整个条件表达式的值;当表达式1的值是0时,以表达式3 的值作为整个条件表达式的值。

优先级:条件运算符高于赋值运算符,但低于逻辑运算符、关系运算符和算术运算符。

比如:a=t > 1? 2 : 3,如果判定 t>1为真,即非0时,条件表达式的值为2,并把2赋值给变a;如果判定t>1为假,即为0时,条件表达式的值为3,并把3赋值给变量a。

switch语句

switch 语句是C语言提供的多分支选择语句,用来实现多分支选择结构。

基本格式:

switch(表达式)
{
    case 常量表达式: 语句1
    case 常量表达式: 语句1
    ...
    case 常量表达式: 语句1
    default: 语句n+1
}

说明:

  • switch 后面用花括号括起来的部分是 switch 语句体

  • switch 后面括号内的 ”表达式”,可以是C语言中任意合法表达式,但表达式两侧的括号不能省略。

  • case 与其后面的常量表达式合称 case语句标号,常量表达式的类型必须与 switch 后面的表达式的类型相匹配,且各 case语句标号的值各不相同,不能重复

  • default 也是关键字,起标号的作用,代表除了以上所有 case 标号之外的那些标号,default标号可以出现在语句体中任何标号位置上,当然也可以没有。

  • case 语句标号后的语句1、语句2等,可以是一条语句,也可以是若干条;在必要时,case语句标号后的语句可以省略不写。

switch语句的执行过程:

  • 首先计算紧跟在 switch 后面的一对圆括号中的表达式的值,当表达式的值与某一个 case 后面的常量表达式的值相等时,就执行此 case 后面的语句体并将流程转移到下一个 case 继续执行,直至switch 语句的结束

  • 若所有的 case中的常量表达式的值都没有与表达式值匹配,又存在default,则执行default后面的语句,直至switch 语句结束。

  • 如果不存在 default ,则跳过 switch 语句体,什么也不做。

switch语句非常有用,但在使用时必须谨慎。
只能针对基本数据类型中的整型类型使用switch,这些类型包括int、char等。对于其他类型,则必须使用if语句。
switch()的参数类型不能为实型

switch语句和 break 语句组成的选择结构

在 switch语句中 case 之后的语句加上 break语句,程序执行到 break 时会立即跳出 switch 语句体。其中,break是C语言的关键词。switch语和break语句的联合使用,使得switch语句真正起到分支的作用。下面用一段程序说明:

#include<stdio.h>
int main() {
    int x=1,a=0,b=0;
    switch(x) {
        case 0: a++;
        case 1:b++;
        case 2:a++;b++;break;            // 试试加不加break的情况下,结果是否一致
        case 3: b++;
        default:x=99;
    }
    printf("a=%d,b=%d,x=%d",a,b,x);

}

执行上述程序后,输出结果为:a=1,b=2。
程序分析:switch后的表达式x的值为1。然后寻找与1相同的case1分支,执行其后的语句,此时a=0,b=1。接着开始执行case2后的语句,执行a++;b++后,a=1,b=2。执行到break语句,跳出switch 语句体,开始执行 printf函数。

循环结构

while 和 do…while 循环结构

  1. while 语句
    一般形式:while(表达式) 循环体
    while 是C语言中的关键字,表达式是循环条件,由它来控制循环体是否执行。
    执行过程(先对表达式进行条件判断,后执行语句):

    • 计算while 后括号中表达式的值。当表达式的值非0时,执行while语句中的循环体内嵌语句;当表达式值为0时,跳过该while语句,执行该while 结构后的其他语句。

    • 执行循环体内嵌语句。

    • 返回去继续执行while语句,直到条件不满足,退出循环。

  2. do…while 语句
    基本格式:

    do {
        循环体语句
    } while(表达式);
    
    • do 是C语言的关键字,必须和 while 联合使用,不能单独出现。

    • do…while 循环由 do 开始,用 while 结束。

    • while 后面的圆括号中的表达式,可以是C语言中任意合法的表达式,由它控制循环是否执行,且圆括号不可丢。

    • 在语法上,在 do 和 while 之间只能是一条语句,如需要执行多条语句时,可使用复合语句。

    • while(表达式)后的分号不可丢。

    执行过程(先执行循环体一次,然后判断循环条件是否成立):

    • 先执行一次指定的循环体语句。

    • 执行完后,判别 while 后面的表达式的值,当表达式的值为非零(真)时,程序流程返回去重新执行循环体语句。

    • 如此反复,直到表达式的值等于零为止,此时循环结束,

for 循环结构

基本格式:for(表达式1; 表达式2; 表达式3) 循环体

比如:for(i=0; i<10; i+ +) { }

  • 圆括号中通常是3个表达式,用于for循环的控制。for 语句中的表达式可以部分或者全部省略,但两个分号是不可省略的。

  • 各个表达式之间用分号隔开,且圆括号不可省略。

  • 按照语法规则,循环体只能是一条语句,如需要完成多项操作,须使用复合语句。

执行过程:

  • 求表达式1的值。

  • 求表达式2的值,若其值为真(非0),则执行for语句中指定的循环内嵌语句,后执行求表达式3的值,若其值为假(0),则退出循环,执行 for 语句以下的其他语句。

  • 重复步骤2。

循环结构的嵌套

循环嵌套的定义:在一个循环体内又完整地包含另一个循环结构。内循环还可以嵌套循环,形成多层循环结构。外循环成立的条件下,才可以执行内循环。

while 循环、do…while 循环和for循环都可以嵌套,但每一层循环在逻辑上必须完整。

continue和break语句

  1. continue 语句
    作用:结束本次循环,即跳过本次循环体中continue语句后面的语句,立刻进行下一次的循环条件判断,然后决定循环是否继续进行。例如:

    #include<stdio.h>
    int main() {
        int i, sum = 0;
        for (i = 1; i <= 5; i++) {
            if (i == 3) continue;
            sum = sum + i;
        }
        printf("i=%d,sum=%d\n", i, sum);
        return 0;
    }
    

    程序输出结果:i=6,sum=12。
    程序分析:当程序进行到第三次循环,即当i=3时,由 if语句和 continue 语句可知本次循环结束,i的值不加到 sum 中,然后接着开始第四次循环,即从i=4 开始,直到循环结束。

  2. break 语句
    作用:可以使程序流程跳出 switch 语句体,也可用 break语句在循环结构中终止本层循环体,从而提前结束本层循环。
    当 break 出现在循环体中的 switch 语句体内时,其作用只是跳出该 switch 语句体,并不能中止循环体的执行。若想强行中止循环体的执行,可以在循环体中,但并不在switch语句中设break语句,满足某种条件则跳出本层循环体。例如:

    #include<stdio.h>
    int main() {
        int i, sum = 0;
        for (i = 1; i <= 5; i++) {
            if (i == 3) break;                // 如果i的值为3,跳出for循环,然后执行printf函数
            sum = sum + i;                    // 把i的值加到sum中
        }
        printf("i=%d,sum=%d\n", i, sum);
        return 0;
    }
    

    程序输出结果:i=3,sum=3。
    程序分析:当程序进行到第三次循环,即当i=3时,由 i 语句和 break 语句可知此时应跳出依次存 for 循环,执行 for 循环后的语句,即执行 printf 函数输出语句。

预处理指令

C语言的预处理是编译过程的一个早期阶段,它发生在实际的编译之前。预处理阶段主要处理源代码中的预处理指令,这些指令以#开头。预处理指令告诉预处理器在实际编译之前如何修改源代码。

C语言提供了三种预处理功能,这三种预处理包括宏定义文件包含条件编译

指令描述
#define定义宏
#include包含一个源代码文件
#undef取消已定义的宏
#ifdef如果宏已经定义,则返回真
#ifndef如果宏没有定义,则返回真
#if如果给定条件为真,则编译下面代码
#else#if 的替代方案
#elif如果前面的 #if 给定条件不为真,当前条件为真,则编译下面代码
#endif结束一个 #if……#else 条件编译块
#error当遇到标准错误时,输出错误消息
#pragma使用标准化方法,向编译器发布特殊的命令到编译器中

宏定义

宏定义又称宏代换、宏替换,简称宏。

它指的是用一个宏名(名字)来代表一个字符串,或者根据一系列预定义的规则替换一定的文本模式。

宏定义最常见的用法是定义代表某个值的全局符号,例如,#define PI 3.14159。另一种用法是定义带参数的宏,这样的宏可以像函数一样被调用,并在调用语句处展开宏,用调用时的实际参数来代替定义中的形式参数。

不带参数的宏定义

定义形式:#define 宏名 替换文本

         或 #define 宏名

说明:

  • 再define宏名和宏替换文本之间用空格隔开。

  • 同一个宏名不能重复定义。

  • 可以用#undef命令终止宏定义作用域。

  • 在进行宏定义时,可以引用已定义的宏名。

  • 宏可以在源代码中多次使用,并在预处理阶段被其定义的值替换。

例如:

#define PI 3.14159            // 定义一个宏
#undef PI                    // 取消一个先前定义的宏。
带参数的宏定义
  1. 带参数的宏定义
    定义形式:#define 宏名(参数表) 字符串
    宏定义不止进行简单的字符串替换,话可以进行参数替换。

  2. 带参数的宏调用
    一般形式:宏名(实参列表)
    例如:

    #define total(x) x*x+x+1            // 宏定义
    a=total(2);                        // 宏调用
    

    解析:在宏调用过程,用实参2取代替形参x,经预处理宏展开后的语句为 a=2+2+2+1。

    在带参宏定义中,不会为形式参数分配内存,因此不必指明数据类型。而在宏调用中,实参包含了具体的数据,要用它们去代换形参,因此必须指明数据类型。同时要特别注意实参是表达式时的调用。
    比如:#define total(x) xx // 宏定义
    a=total(x+y); // 宏调用
    解析:经预处理宏展开后的语句为x+y
    x+y。
    比如:#define total(x) (x)(x) // 宏定义
    a = total(x +y); // 宏调用
    解析:经预处理宏展开后的语句为(x+y)
    (x+y)。

条件编译

条件编译指令允许程序员根据特定的条件包含或排除代码块。这些条件可以是编译时定义的宏、编译器的特定标志,或者其他任何可以在预处理阶段确定的条件( 条件编译指令,用于根据条件包含或排除代码块。)。

以下是常用的条件编译指令:

  1. #ifdef#ifndef
    这两个指令用于检查是否定义了一个特定的宏。如果定义了宏(对于#ifdef),或者没有定义宏(对于#ifndef),则编译器会包含随后的代码块,直到遇到#endif为止。

    #ifdef DEBUG
        // 这部分代码仅在定义了DEBUG宏时编译
    #endif
    
    #ifndef NDEBUG
        // 这部分代码在NDEBUG宏`未定义`时编译
    #endif
    
  2. #if#elif#else#endif
    这些指令提供了更复杂的条件编译功能,类似于C语言中的if-else语句。#if后面可以跟随一个常量表达式,如果表达式的值为非零,则编译器会包含随后的代码块#elif(相当于else if)和#else提供了额外的条件分支。

    #if defined(OS_WINDOWS)
        // Windows平台特有的代码
    #elif defined(OS_LINUX)
        // Linux平台特有的代码
    #else
        // 其他平台共有的代码
    #endif
    
  3. #defined
    #if#ifdef指令后面都可以使用defined操作符来检查宏是否定义。defined操作符返回一个整数值,如果宏定义了,则为非零值;否则为零。

    #if defined(FEATURE_A) && !defined(FEATURE_B)
        // 当FEATURE_A定义了,而FEATURE_B未定义时编译
    #endif
    
  4. #undef
    这个指令用于取消之前定义的宏。这可以用来在特定条件下移除之前定义的宏。

    #define MY_MACRO 1
    // ... 一些代码 ...
    #undef MY_MACRO // 取消MY_MACRO的定义
    
  5. #error
    当条件编译的某个条件不满足时,可以使用#error指令生成一个编译时错误。这有助于在编译阶段捕获配置错误或不支持的平台。

    #if !defined(OS_SUPPORTED)
    #error "Unsupported operating system!"
    #endif
    
  6. #line
    这个指令可以改变预处理器生成的行号和文件名,通常用于复杂的宏定义或代码生成器。

    #line 100 "newfile.c"
    
  7. #pragma
    这是一个编译器特定的预处理指令,用于向编译器传递额外的信息或指令。不同的编译器可能支持不同的#pragma指令。

    #pragma once // 防止头文件被多次包含,某些编译器支持这个指令
    

文件包含

C头文件

头文件是扩展名为 .h 的文件,包含了 C 函数声明和宏定义,被多个源文件中引用共享。有两种类型的头文件:程序员编写的头文件和编译器自带的头文件。

在程序中要使用头文件,需要使用 C 预处理指令 #include 来引用它。前面我们已经看过 stdio.h 头文件,它是编译器自带的头文件。

引用头文件相当于复制头文件的内容,但是我们不会直接在源文件中复制头文件的内容,因为这么做很容易出错,特别在程序是由多个源文件组成的时候。

建议把所有的常量、宏、系统全局变量和函数原型写在头文件中,在需要的时候随时引用这些头文件。

引用头文件的语法

使用预处理指令 #include 可以引用用户和系统头文件。它的形式有以下两种:

#include <file.h>

这种形式用于引用系统头文件。它在系统目录的标准列表中搜索名为 file 的文件。

#include "file.h"
#include "file.c"

这种形式用于引用用户头文件。它在包含当前文件的目录中搜索名为 file 的文件。

引用头文件的操作

#include 指令会指示 C 预处理器浏览指定的文件作为输入。预处理器的输出包含了已经生成的输出,被引用文件生成的输出以及 #include 指令之后的文本输出。例如,如果您有一个头文件 header.h,如下:

char *test (void);

和一个使用了头文件的主程序 program.c,如下:

int x;
#include "header.h"

int main (void)
{
   puts (test ());
}

编译器会看到如下的代码信息:

int x;
char *test (void);

int main (void)
{
   puts (test ());
}
只引用一次头文件

如果一个头文件被引用两次,编译器会处理两次头文件的内容,这将产生错误。为了防止这种情况,标准的做法是把文件的整个内容放在条件编译语句中,如下:

#ifndef HEADER_FILE
#define HEADER_FILE

the entire header file file

#endif

这种结构就是通常所说的包装器 #ifndef。当再次引用头文件时,条件为假,因为 HEADER_FILE 已定义。此时,预处理器会跳过文件的整个内容,编译器会忽略它。

有条件引用

有时需要从多个不同的头文件中选择一个引用到程序中。例如,需要指定在不同的操作系统上使用的配置参数。您可以通过一系列条件来实现这点,如下:

#if SYSTEM_1
   # include "system_1.h"
#elif SYSTEM_2
   # include "system_2.h"
#elif SYSTEM_3
   ...
#endif

但是如果头文件比较多的时候,这么做是很不妥当的,预处理器使用宏来定义头文件的名称。这就是所谓的有条件引用。它不是用头文件的名称作为 #include 的直接参数,您只需要使用宏名称代替即可:

#define SYSTEM_H "system_1.h"
 ...
 #include SYSTEM_H

SYSTEM_H 会扩展,预处理器会查找 system_1.h,就像 #include 最初编写的那样。SYSTEM_H 可通过 -D 选项被您的 Makefile 定义。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

childish_tree

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

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

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

打赏作者

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

抵扣说明:

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

余额充值