C Primer Plus第六版第十五章编程题目与参考答案⭐

1.编写一个函数,把二进制字符串转换为一个数值。例如,有下面的语句:
char * pbin = “01001001”;
那么把pbin作为参数传递给该函数后,它应该返回一个int类型的值25。

# include <stdio.h>
# include <limits.h>
# include <string.h>
# include <stdlib.h>
# include <ctype.h>
 
# define SIZE 33
 
int get_bstr(char * st , int n);
char * del_space(char * st);
int btoi(char * st);
 
int main(void)
{
	char binstr[SIZE];
	int num ;
 
	printf("请输入二进制数(输入空行退出)\n");
	while (get_bstr(binstr , SIZE) && binstr[0]) 
	{
		num = btoi(del_space(binstr));
		printf("%s 是十进制是 %d\n" , binstr , num);
		printf("请输入下一个二进制数(输入空行退出)\n");
	}
 
	return 0 ;
}
 
int get_bstr(char * st , int n)
{
	char ch ; 
	int i = 0;
 
	for (i = 0 ; i < n - 1 && (ch = getchar()) != '\n' && (isdigit(ch) || isspace(ch)) ; i++)
		st[i] = ch ;
	st[i] = '\0' ;
 
	if (ch != '\n')
		while (getchar() != '\n') ;
 
	return i ;
}
 
char * del_space(char * st)
{
	int i = 0 ;
	char * find = NULL ;
 
	while (find = strchr(st , ' '))
	{
		for (i = 0 ; find[i] ; i++)
			find[i] = find[i + 1] ;
	}
 
	return st ;
}
 
int btoi(char * st)
{
	int number = 0 ;
 
	for (int i = 0 ; st[i] ; i++)
	{
		number <<= 1 ;
		number |=  (st[i] - '0');
	}
 
	return number ;
}

2.编写一个程序,通过命令行参数读取两个二进制字符串,对这两个二进制数使用~运算符、&运算符、|运算符和^运算符,并以二进制字符串形式打印结果(如果尤法使用交互式让程序读取字符串)。

# include <stdio.h>
# include <limits.h>
# include <string.h>
# include <stdlib.h>
# include <ctype.h>
 
# define SIZE 33
 
int str_bstr(char * st);
char * del_space(char * st);
int btoi(char * st);
void print_bstr(unsigned int n);
 
//int main(int argc , char * argv[])
int main(void)
{
	char argv[3][SIZE] = {"a.exe" , "a0101b" , "341010"};
	int num1 , num2 ;
 
	/*
	if (argc != 3)
	{
		fputs("参数错误", stderr);
		exit(1);
	}
	*/
 
	str_bstr(argv[1]) ;
	str_bstr(argv[2]) ;
	
	num1 = btoi(del_space(argv[1]));
	num2 = btoi(del_space(argv[2]));
 
	printf("~%s == " ,argv[1]);
	print_bstr(~num1);
	putchar('\n');
 
	printf("~%s == " ,argv[2]);
	print_bstr(~num2);
	putchar('\n');
 
	printf("%s & %s == " , argv[1] , argv[2]);
	print_bstr(num1 & num2);
	putchar('\n');
 
	printf("%s | %s == " , argv[1] , argv[2]);
	print_bstr(num1 | num2);
	putchar('\n');
 
	printf("%s ^ %s == " , argv[1] , argv[2]);
	print_bstr(num1 ^ num2);
	putchar('\n');
 
	return 0 ;
}
 
int str_bstr(char * st)
{
	int cur = 0 ; 			//当前索引位置
	char ch ; 
 
	for (int i = 0 ; st[i]; i++)
		if((isdigit(st[i]) && st[i] < '2') || isspace(st[i]))
		{
			ch = st[cur] ;
			st[cur] = st[i] ;
			st[i] = ch ;
 
			cur++ ;
		}
 
	st[cur] = '\0' ;
 
	return cur ;
}
 
char * del_space(char * st)
{
	int i = 0 ;
	char * find = NULL ;
 
	while (find = strchr(st , ' '))
	{
		for (i = 0 ; find[i] ; i++)
			find[i] = find[i + 1] ;
	}
 
	return st ;
}
 
int btoi(char * st)
{
	int number = 0 ;
 
	for (int i = 0 ; st[i] ; i++)
	{
		number <<= 1 ;
		number |=  (st[i] - '0');
	}
 
	return number ;
}
 
void print_bstr(unsigned int n)
{
	static long loop = 0;

	if (!loop && !n)
	{
		putchar('0');
		return ;
	}
 
	if (n)
	{
		loop++ ;
		print_bstr(n / 2) ;
	}
	else
		return  ;
 
	putchar('0' + (n % 2));
	if ((--loop % 4)== 0)
		putchar(' ');
}

3.编写一个函数,接受一个int 类型的参数,并返回该参数中打开位的数量。在一个程序中测试该函数。

# include <stdio.h>
 
int count_openbit(unsigned int n);
 
int main(void)
{
	int num ;
 
	puts("请输入您要查看打开位总数的整数:(q 退出)");
	while (scanf(" %d" , &num))
	{
		while (getchar() != '\n') ;
 
		printf("%d 中打开位一共有%d个\n" , num , count_openbit(num));
		puts("请输入您要查看打开位总数的整数:(q 退出)");
	}
 
	return 0 ;
}
 
int count_openbit(unsigned int n)
{
	int count = 0 ;
 
	while (n)
	{
		count += (n & 1) ;
		n >>= 1 ;
	}
 
	return count ;
}

4.编写一个程序,接受两个int类型的参数,一个是值,一个是位的位置。如果指定位的位置为1,该函数返回1,否则返回0。在一个程序中测试该函数。

# include <stdio.h>
# include <stdlib.h>
 
# define COUNT_BIT 8
 
int bit_value(int value , int n);
 
int main(void)
{
	int num ;
 
	printf("请输入您要查看打开位状况的数(q 退出);");
	while (scanf(" %d" , &num))
	{
		while (getchar() != '\n');
 
		for (int i = 0 ; i < COUNT_BIT ; i++ )
			printf("%d 第%d位打开状况: %s\n" ,num , i + 1 , bit_value(num , i + 1) ? "打开" : "关闭" );
 
		printf("请输入您要查看打开位状况的数(q 退出);");
	}
 
	return 0 ;
}
 
int bit_value(int value , int n)
{
	if (!n)
	{
		fputs("位置值不能为0.--bit_value" , stderr);
		exit(1);
	}
 
	return (value & (1 << (n - 1)));
}

5.编写一个函数,把一个 unsigned int类型值中的所有位向左旋转指定数量的位。例如, rotate_l(x,4)把×中所有位向左移动4个位置,而且从最左端移出的位会重新出现在右端。也就是说,把高阶位移出的位放入低阶位。在一个程序中测试该函数。

# include <stdio.h>
# include <limits.h>
# include <stdlib.h>
 
void print_bstr(unsigned int num);
unsigned int rotate_l(unsigned int num , int loop);
 
int main(void)
{
	int num ; 
	int loop ;
 
	puts("请输入您要循环左移的值(q 退出)");
	while (scanf(" %d" , &num))
	{
		while (getchar() != '\n');
 
		puts("请输入您要循环左移的次数(q 返回上一级菜单)");
		while (scanf(" %d" , &loop))
		{
			while (getchar() != '\n') ;
 
			puts("===============================");
			puts("您输入的值的二进制为");
			print_bstr(num);
			putchar('\n');
 
			puts("循环左移后的二进制为");
			print_bstr(rotate_l(num , loop));
			putchar('\n');
			puts("===============================");
 
			puts("请输入您要循环左移的次数(q 返回上一级菜单)");
		}
		while (getchar() != '\n')
 
		puts("请输入您要循环左移的值(q 退出)");
	}
 
	return 0 ;
}
 
unsigned int rotate_l(unsigned int num , int loop)
{
	for (int i = 0 ; i < loop ; i++)
		num = (num >> (CHAR_BIT * sizeof(unsigned int) - 1)) | (num << 1) ; 
 
	return num ;
}
 
void print_bstr(unsigned int num)
{
	static int n = 0 ;
 
	if (!num && !n)
		return ;
 
	if (num)
	{
		n++ ;
		print_bstr(num >> 1);
	}
	else
		return ;
 
	putchar('0' + (num & 1));
 
	if ((--n % 4) == 0)
		putchar(' ');
}

6.设计一个位子段结构以储存下面的信息。
字体ID: 0~255之间的一个数;
字体大小: 0~127之间的一个数;
对齐: 0~2之间的一个数,表示左对齐、居中、右对齐;
加粗: 开(1)或闭(0);
斜体: 开(1)或闭(0);
在一个程序中使用该结构来打印字体参数,并使用循环菜单来让用户改变参数。例如,该程序的一个运行示例如下:

在这里插入图片描述
该程序要使用按位与运算符(&)和合适的掩码来把字体ID和字体大小信息转换到指定的范围内。

# include <stdio.h>
# include <stdlib.h>
# include <string.h>
# include <ctype.h>
# include <windows.h>
 
struct 
{
	unsigned int ID : 		8 ;
	unsigned int SIZE : 		8 ;
	unsigned int ALIGNMENT : 	2 ;
	unsigned int BOLD :		1 ;
	unsigned int ITALIC : 		1 ;
	unsigned int UNDERLINE : 	1 ;
 
}font;
 
 
void out_menu(void);
void show_setting(void);
int menu_choice(void);
void end(void);
void change_font(void);
void change_size(void);
void change_alignment(void);
void toggle_bold(void);
void toggle_italic(void);
void toggle_underline(void);
 
int main(void)
{
	font.ID = 1 ;
	font.SIZE = 12 ;
	font.ALIGNMENT = 0 ;
	font.BOLD = 0 ;
	font.ITALIC = 0 ;
	font.UNDERLINE = 0 ;
 
	system("cls");
	while (menu_choice())
	{
		system("cls");
	}
 
 
	return 0 ;
}
 
void out_menu(void)
{
	static char * menu_str[] = 
	{
		"Please select the menu label you need",
	 	"f) change font		s) change size 		a) change alignment",
	 	"b) toggle bold		i) toggle italic	u) toggle underline",
	 	"q) quit"
	};
 
	for (int i = 0 ; i < sizeof(menu_str) / sizeof(menu_str[0]) ; i++)
		puts(menu_str[i]);
}
 
void show_setting(void)
{
	int x ;
	char tmp[10] ;
 
	static char * set[] = 
	{
		"ID" ,
		"SIZE" ,
		"ALIGNMENT" ,
		"B" ,
		"I" ,
		"U"
	};
 
	const int width = 15 ;
	
	for (int i = 0 ; i < sizeof(set) / sizeof(set[0]) ; i++)
		printf("%*c%s%*c " , (width - strlen(set[i])) / 2 ,' ' , set[i],
				 (width - strlen(set[i])) / 2 ,' ' );
	putchar('\n');
 
	
	printf("%*c%d%*c " , (width - sprintf(tmp , "%d" , font.ID)) / 2 ,' ' , font.ID ,(width - sprintf(tmp , "%d" , font.ID)) / 2 ,' ');
	printf("%*c%d%*c " , (width - sprintf(tmp , "%d" , font.SIZE)) / 2 ,' ' , font.SIZE ,(width - sprintf(tmp , "%d" , font.SIZE)) / 2 ,' ');
	switch (font.ALIGNMENT)
	{
		case 0 :	//left
			printf("%*c%s%*c " , (width - strlen("left")) / 2 ,' ' , "left" , (width - strlen("left")) / 2 ,' ');
			break ;
		case 1:		//center
			printf("%*c%s%*c " , (width - strlen("center")) / 2 , ' ' , "center" , (width - strlen("center")) / 2 , ' ');
			break ;
		case 2 :	//right
			printf("%*c%s%*c " , (width - strlen("right")) / 2 , ' ' , "right" , (width - strlen("right")) / 2 , ' ' );
			break ;
		default :
			printf("%*c%s%*c " , (width - strlen("Unknow type")) / 2 , ' ' , "Unknow type" , (width - strlen("Unknow type")) / 2 , ' ');
	}
	printf("%*c%s%*c " , (width - strlen(font.BOLD ? "on" : "off")) / 2 , ' ' , font.BOLD ? "on" : "off" ,  (width - strlen(font.BOLD ? "on" : "off")) / 2 , ' ');
	printf("%*c%s%*c " , (width - strlen(font.ITALIC ? "on" : "off")) / 2 , ' ' , font.ITALIC ? "on" : "off" ,  (width - strlen(font.ITALIC ? "on" : "off")) / 2 , ' ');
	printf("%*c%s%*c " , (width - strlen(font.UNDERLINE ? "on" : "off")) / 2 , ' ' , font.UNDERLINE ? "on" : "off", (width - strlen(font.UNDERLINE ? "on" : "off")) / 2 , ' ');
	
	puts("\n");
}
 
int menu_choice(void)
{
	const void (* menufun[])(void) = {end , change_font , change_size , change_alignment ,
		toggle_bold , toggle_italic , toggle_underline};
	const char * label = "qfsabiu" ;
	char choice ;
	char * find ;
	int cmd = 0 ;
 
	show_setting();
	out_menu();
	while (!scanf(" %c" , &choice) || !(find = strchr(label , tolower(choice))))
	{
		fputs("Invalid choice , please re-select" , stderr);
		while (getchar() != '\n') ;
	}
	while (getchar() != '\n') ;
 
	cmd = find - label;
	menufun[cmd]();
	
	return cmd ;
}
 
void end(void)
{
	puts("Bye !");
}
 
void change_font(void)
{
	unsigned int n = 0 ;
 
	puts("Please enter the ID of your favorite font");
	while (!scanf(" %d" , &n) || n >255)
	{
		if (n > 255)
			fputs("Err: ID cannot exceed 0~255\n" , stderr);
		else 
			fputs("Err: Illegal input,Please try again.\n" , stderr);
		while (getchar() != '\n') ;
	}
	while (getchar() != '\n') ;
 
	font.ID = n ;
}
 
void change_size(void)
{
	unsigned int n = 0 ; 
 
	puts("Please enter the font size you want to set");
	while (!scanf(" %d" , &n) || n > 127)
	{
		if (n > 127)
			fputs("Err: Font sizes exceed 0~127\n" , stderr);
		else
			fputs("Err: Illegal input,Please try again.\n" , stderr);
		while (getchar() != '\n') ;
	}
	while (getchar() != '\n') ;
 
	font.SIZE = n ;
}
 
void change_alignment(void)
{
	unsigned int n = 0 ; 
 
	puts("Please choose the alignment method");
	puts("1) left    2)center    3)right");
	while (!scanf(" %d" , &n) || n > 3)
	{
		if (n > 3)
			fputs("Err: Invalid selection, please re-enter the correct label\n" , stderr);
		else
			fputs("Err: Illegal input,Please try again.\n" , stderr);
		while (getchar() != '\n') ;
	}
	while (getchar() != '\n') ;
 
	font.ALIGNMENT = n - 1 ;
}
 
void toggle_bold(void)
{
	unsigned int n = 0 ; 
 
	puts("Whether to open bold or not");
	puts("0) close             1)open");
	while (!scanf(" %d" , &n) || n > 1)
	{
		if (n > 1)
			fputs("Err: Invalid selection, please re-enter the correct label\n" , stderr);
		else
			fputs("Err: Illegal input,Please try again.\n" , stderr);
		while (getchar() != '\n') ;
	}
	while (getchar() != '\n') ;
 
	font.BOLD = n ;
}
 
void toggle_italic(void)
{
	unsigned int n = 0 ; 
 
	puts("Whether to open italic or not");
	puts("0) close             1)open");
	while (!scanf(" %d" , &n) || n > 1)
	{
		if (n > 1)
			fputs("Err: Invalid selection, please re-enter the correct label\n" , stderr);
		else
			fputs("Err: Illegal input,Please try again.\n" , stderr);
		while (getchar() != '\n') ;
	}
	while (getchar() != '\n') ;
 
	font.ITALIC = n ;
}
 
void toggle_underline(void)
{
	unsigned int n = 0 ; 
 
	puts("Whether to open underline or not");
	puts("0) close             1)open");
	while (!scanf(" %d" , &n) || n > 1)
	{
		if (n > 1)
			fputs("Err: Invalid selection, please re-enter the correct label\n" , stderr);
		else
			fputs("Err: Illegal input,Please try again.\n" , stderr);
		while (getchar() != '\n') ;
	}
	while (getchar() != '\n') ;
 
	font.UNDERLINE = n ;
}

7.编写一个与编程练习6功能相同的程序,使用unsigned long类型的变量储存字体信息,并且来管理这些信息。
用按位运算符而不是位成员来管理这些信息。

# include <stdio.h>
# include <stdlib.h>
# include <string.h>
# include <ctype.h>
# include <windows.h>
 
//声明字体位段结构体
unsigned int font = 8576;
 
 
void out_menu(void);
void show_setting(void);
int menu_choice(void);
void end(void);
void change_font(void);
void change_size(void);
void change_alignment(void);
void toggle_bold(void);
void toggle_italic(void);
void toggle_underline(void);
 
int main(void)
{
	system("cls");
	while (menu_choice())
		system("cls");
 
	return 0 ;
}
 
void out_menu(void)
{
	static char * menu_str[] = 
	{
		"Please select the menu label you need",
	 	"f) change font		s) change size 		a) change alignment",
	 	"b) toggle bold		i) toggle italic	u) toggle underline",
	 	"q) quit"
	};
 
	for (int i = 0 ; i < sizeof(menu_str) / sizeof(menu_str[0]) ; i++)
		puts(menu_str[i]);
}
 
void show_setting(void)
{
	static char * set[] = 
	{
		"ID" ,
		"SIZE" ,
		"ALIGNMENT" ,
		"B" ,
		"I" ,
		"U"
	};
	char tmp[10] ;
	const int width = 15 ;
 
	// 已知 ID:8位 size:8位 alignment:2位 bold:1位 italic:1位 underline:1位
	// 一共是21位,int类型32位
	unsigned int f = font & ((~0) >> (CHAR_BIT * sizeof(unsigned int) - 21)) ;
	unsigned int id = f >> (21 - 8) ;
	unsigned int size = (f >> (21 - 16)) & 0xff ;
	unsigned int alignment = (f >> (21 - 18)) & 3 ;
	unsigned int bold = (f >> 2) & 1 ;
	unsigned int italic = (f >> 1) & 1 ;
	unsigned int underline = f & 1 ;
 
	for (int i = 0 ; i < sizeof(set) / sizeof(set[0]) ; i++)
		printf("%*c%s%*c " , (width - strlen(set[i])) / 2 ,' ' , set[i],
				 (width - strlen(set[i])) / 2 ,' ' );
	putchar('\n');
 
	
	printf("%*c%d%*c " , (width - sprintf(tmp , "%d" , id)) / 2 ,' ' , id ,(width - sprintf(tmp , "%d" , id)) / 2 ,' ');
	printf("%*c%d%*c " , (width - sprintf(tmp , "%d" , size)) / 2 ,' ' , size ,(width - sprintf(tmp , "%d" , size)) / 2 ,' ');
	switch (alignment)
	{
		case 0 :	//left
			printf("%*c%s%*c " , (width - strlen("left")) / 2 ,' ' , "left" , (width - strlen("left")) / 2 ,' ');
			break ;
		case 1:		//center
			printf("%*c%s%*c " , (width - strlen("center")) / 2 , ' ' , "center" , (width - strlen("center")) / 2 , ' ');
			break ;
		case 2 :	//right
			printf("%*c%s%*c " , (width - strlen("right")) / 2 , ' ' , "right" , (width - strlen("right")) / 2 , ' ' );
			break ;
		default :
			printf("%*c%s%*c " , (width - strlen("Unknow type")) / 2 , ' ' , "Unknow type" , (width - strlen("Unknow type")) / 2 , ' ');
	}
	printf("%*c%s%*c " , (width - strlen(bold ? "on" : "off")) / 2 , ' ' , bold ? "on" : "off" ,  (width - strlen(bold ? "on" : "off")) / 2 , ' ');
	printf("%*c%s%*c " , (width - strlen(italic ? "on" : "off")) / 2 , ' ' , italic ? "on" : "off" ,  (width - strlen(italic ? "on" : "off")) / 2 , ' ');
	printf("%*c%s%*c " , (width - strlen(underline ? "on" : "off")) / 2 , ' ' , underline ? "on" : "off", (width - strlen(underline ? "on" : "off")) / 2 , ' ');
	
	puts("\n");
}
 
int menu_choice(void)
{
	const void (* menufun[])(void) = {end , change_font , change_size , change_alignment ,
		toggle_bold , toggle_italic , toggle_underline};
	const char * label = "qfsabiu" ;
	char choice ;
	char * find ;
	int cmd = 0 ;
 
	show_setting();
	out_menu();
	while (!scanf(" %c" , &choice) || !(find = strchr(label , tolower(choice))))
	{
		fputs("Invalid choice , please re-select" , stderr);
		while (getchar() != '\n') ;
	}
	while (getchar() != '\n') ;
 
	cmd = find - label;
	menufun[cmd]();
	
	return cmd ;
}
 
void end(void)
{
	puts("Bye !");
}
 
void change_font(void)
{
	unsigned int n = 0 ;
 
	puts("Please enter the ID of your favorite font");
	while (!scanf(" %d" , &n) || n >255)
	{
		if (n > 255)
			fputs("Err: ID cannot exceed 0~255\n" , stderr);
		else 
			fputs("Err: Illegal input,Please try again.\n" , stderr);
		while (getchar() != '\n') ;
	}
	while (getchar() != '\n') ;
 
	font = (font & ~((0xff) << (21 - 8))) | (n << (21 - 8));
}
 
void change_size(void)
{
	unsigned int n = 0 ; 
 
	puts("Please enter the font size you want to set");
	while (!scanf(" %d" , &n) || n > 127)
	{
		if (n > 127)
			fputs("Err: Font sizes exceed 0~127\n" , stderr);
		else
			fputs("Err: Illegal input,Please try again.\n" , stderr);
		while (getchar() != '\n') ;
	}
	while (getchar() != '\n') ;
 
	font =(font & ~((0xff) << (21 - 16))) | (n << (21 - 16));
}
 
void change_alignment(void)
{
	unsigned int n = 0 ; 
 
	puts("Please choose the alignment method");
	puts("1) left    2)center    3)right");
	while (!scanf(" %d" , &n) || n > 3)
	{
		if (n > 3)
			fputs("Err: Invalid selection, please re-enter the correct label\n" , stderr);
		else
			fputs("Err: Illegal input,Please try again.\n" , stderr);
		while (getchar() != '\n') ;
	}
	while (getchar() != '\n') ;
 
	font = (font & ~(3 << 3)) | ((n - 1) << 3) ;
}
 
void toggle_bold(void)
{
	unsigned int n = 0 ; 
 
	puts("Whether to open bold or not");
	puts("0) close             1)open");
	while (!scanf(" %d" , &n) || n > 1)
	{
		if (n > 1)
			fputs("Err: Invalid selection, please re-enter the correct label\n" , stderr);
		else
			fputs("Err: Illegal input,Please try again.\n" , stderr);
		while (getchar() != '\n') ;
	}
	while (getchar() != '\n') ;
 
	font = (font & ~(1 << 2)) | (n << 2) ;
}
 
void toggle_italic(void)
{
	unsigned int n = 0 ; 
 
	puts("Whether to open italic or not");
	puts("0) close             1)open");
	while (!scanf(" %d" , &n) || n > 1)
	{
		if (n > 1)
			fputs("Err: Invalid selection, please re-enter the correct label\n" , stderr);
		else
			fputs("Err: Illegal input,Please try again.\n" , stderr);
		while (getchar() != '\n') ;
	}
	while (getchar() != '\n') ;
 
	font = (font & ~(1 << 1)) | (n << 1) ;
}
 
void toggle_underline(void)
{
	unsigned int n = 0 ; 
 
	puts("Whether to open underline or not");
	puts("0) close             1)open");
	while (!scanf(" %d" , &n) || n > 1)
	{
		if (n > 1)
			fputs("Err: Invalid selection, please re-enter the correct label\n" , stderr);
		else
			fputs("Err: Illegal input,Please try again.\n" , stderr);
		while (getchar() != '\n') ;
	}
	while (getchar() != '\n') ;
 
	font = (font & ~(1)) | n ;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值