练习10-1
修改 10.2节的栈示例使它存储字符而不是整数。接下来,增加 main函数,用来要求用户输入一串圆 括号或花括号,然后指出它们之间的嵌套是否正确: Enter parenteses and/or braces: ((){}{()}) Parenteses/braces are nested properly 提示:读入左圆括号或左花括号时,把它们像字符一样压入栈中。当读入右圆括号或右花括号时, 把栈顶的项弹出,并且检查弹出项是否是匹配的圆括号或花括号。(如果不是,那么圆括号或花括号 嵌套不正确。)当程序读入换行符时,检查栈是否为空。如果为空,那么圆括号或花括号匹配;如果 栈不为空(或者如果曾经调用过 stack_underflow 函数),那么圆括号或花括号不匹配。如果调用 stack_overflow 函数,程序显示信息 Stack overflow,并且立刻终止。
main.c
#include <stdio.h>
#include "stack.h"
int main(void) {
int ch;
while ((ch = getchar()) != '\n' && ch != EOF) {
if (ch == '{' || ch == '(')
push(ch);
else if (ch == '}' && pop() != '{' ||
ch == ')' && pop() != '(') {
printf("括号大括号嵌套错误!\n");
return 1;
}
}
if (!is_empty()) {
printf("括号大括号嵌套错误!\n");
return 1;
}
printf("括号大括号正确嵌套。\n");
return 0;
}
stack.h
#ifndef STACK_H
#define STACK_H
#include <stdbool.h>
void make_empty(void);
void push(int v);
int pop(void);
bool is_empty(void);
bool is_full(void);
#endif
stack.c
#include <stdbool.h>
#include <stdlib.h>
#include <stdio.h>
#define STACK_SIZE 100
char contents[STACK_SIZE];
int top = 0;
void stack_overflow() {
printf("堆栈溢出!\n");
exit(EXIT_FAILURE);
}
void stack_underflow() {
printf("括号大括号嵌套错误!\n");
exit(EXIT_FAILURE);
}
void make_empty(void) {
top = 0;
}
bool is_empty(void) {
return top == 0;
}
bool is_full(void) {
return top == STACK_SIZE;
}
void push(char i) {
if (is_full())
stack_overflow();
else
contents[top++] = i;
}
int pop(void) {
if (is_empty())
stack_underflow();
else
return contents [--top];
}
练习10-2
修改 10.5 节的 poker.c 程序,把数组 num_in_rank 和数组 num_in_suit 移到 main 函数中。main 函 数将把这两个数组作为实际参数传递给 read_cards 函数和 analyze_hand 函数。
main.c
#include "card.h"
int main(void) {
int num_in_rank[RANK_MAX];
int num_in_suit[SUIT_MAX];
for (;;) {
read_cards(num_in_rank, num_in_suit);
analyze_hand(num_in_rank, num_in_suit);
print_result();
}
}
card.h
#ifndef C_CARD_H
#define C_CARD_H
#define RANK_MAX 13
#define SUIT_MAX 4
#define CARD_NUM 5
void read_cards(int num_in_rank[], int num_in_suit[]);
void analyze_hand(int num_in_rank[], int num_in_suit[]);
void print_result(void);
#endif //C_CARD_H
card.c
#include "card.h"
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
static bool straight;
static bool flush;
static bool four;
static bool three;
static int pairs;
void read_cards(int num_in_rank[], int num_in_suit[]) {
bool hand[RANK_MAX][SUIT_MAX] = {false};
int rank, suit;
int i;
for (i = 0; i < SUIT_MAX; ++i) {
num_in_rank[i] = 0;
num_in_suit[i] = 0;
}
while (i < RANK_MAX)
num_in_rank[i++] = 0;
i = 0;
while (i < CARD_NUM) {
printf("输入第%d张牌:", i + 1);
switch (getchar()) {
case '2': rank = 0; break;
case '3': rank = 1; break;
case '4': rank = 2; break;
case '5': rank = 3; break;
case '6': rank = 4; break;
case '7': rank = 5; break;
case '8': rank = 6; break;
case '9': rank = 7; break;
case 't': case 'T': rank = 8; break;
case 'j': case 'J': rank = 9; break;
case 'q': case 'Q': rank = 10; break;
case 'k': case 'K': rank = 11; break;
case 'a': case 'A': rank = 12; break;
case '0': exit(EXIT_FAILURE);
default:
printf("坏牌,请重新输入。\n");
while (getchar() != '\n')
;
continue;
}
switch (getchar()) {
case 'c': case 'C': suit = 0; break;
case 'd': case 'D': suit = 1; break;
case 'h': case 'H': suit = 2; break;
case 's': case 'S': suit = 3; break;
default:
printf("坏牌,请重新输入。\n");
while (getchar() != '\n')
;
continue;
}
if (getchar() != '\n') {
while (getchar() != '\n')
;
printf("坏牌,请重新输入。\n");
continue;
}
if (hand[rank][suit]) {
printf("牌已存在,请重新输入。\n");
continue;
}
num_in_rank[rank] += 1;
num_in_suit[suit] += 1;
hand[rank][suit] = true;
i += 1;
}
}
void analyze_hand(int num_in_rank[], int num_in_suit[]) {
straight = false;
flush = false;
four = false;
three = false;
pairs = 0;
int i;
for (i = 0; i < SUIT_MAX; ++i)
if (num_in_suit[i] == CARD_NUM) {
flush = true;
break;
}
int n = 0;
for (i = 0; i < RANK_MAX; ++i) {
switch (num_in_rank[i]) {
case 4:
four = true;
return;
case 3:
three = true;
if (pairs == 1)
return;
break;
case 2:
pairs += 1;
if (pairs == 2)
return;
break;
case 1:
n += 1;
if (n == CARD_NUM) {
straight = true;
return;
}
break;
default:
if (n != 0)
n = 0;
}
}
}
void print_result(void) {
if (straight && flush) printf("同花顺\n");
else if (four) printf("四张\n");
else if (three && pairs) printf("葫芦\n");
else if (flush) printf("同花\n");
else if (straight) printf("顺子\n");
else if (three) printf("三张\n");
else if (pairs == 2) printf("两对\n");
else if (pairs) printf("对于\n");
else printf("其他牌\n");
}
练习10-3
把数组 num_in_rank、num_in_suit 和 card_exists 从 10.5 节的 poker.c 程序中去掉。程序改用 5×2 的数组来存储牌。数组的每一行表示一张牌。例如,如果数组名为 hand,则 hand[0][0]存储第一 张牌的点数,hand[0][1]存储第一张牌的花色。
main.c
#include "card.h"
int main(void) {
for (;;) {
read_cards();
analyze_hand();
print_result();
}
}
card.h
#ifndef C_CARD_H
#define C_CARD_H
#define RANK_MAX 13
#define SUIT_MAX 4
#define CARD_NUM 5
void read_cards(void);
void analyze_hand(void);
void print_result(void);
#endif //C_CARD_H
card.c
#include "card.h"
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
static bool straight;
static bool flush;
static bool four;
static bool three;
static int pairs;
static int hand[CARD_NUM][2];
void read_cards(void) {
int i;
int j;
for (i = 0; i < CARD_NUM; ++i) {
hand[i][0] = 0;
hand[i][1] = 0;
}
i = 0;
while (i < CARD_NUM) {
printf("输入第%d张牌:", i + 1);
switch (getchar()) {
case '2': hand[i][0] = 0; break;
case '3': hand[i][0] = 1; break;
case '4': hand[i][0] = 2; break;
case '5': hand[i][0] = 3; break;
case '6': hand[i][0] = 4; break;
case '7': hand[i][0] = 5; break;
case '8': hand[i][0] = 6; break;
case '9': hand[i][0] = 7; break;
case 't': case 'T': hand[i][0] = 8; break;
case 'j': case 'J': hand[i][0] = 9; break;
case 'q': case 'Q': hand[i][0] = 10; break;
case 'k': case 'K': hand[i][0] = 11; break;
case 'a': case 'A': hand[i][0] = 12; break;
case '0': exit(EXIT_FAILURE);
default:
printf("坏牌,请重新输入。\n");
while (getchar() != '\n')
;
continue;
}
switch (getchar()) {
case 'c': case 'C': hand[i][1] = 0; break;
case 'd': case 'D': hand[i][1] = 1; break;
case 'h': case 'H': hand[i][1] = 2; break;
case 's': case 'S': hand[i][1] = 3; break;
default:
printf("坏牌,请重新输入。\n");
while (getchar() != '\n')
;
continue;
}
if (getchar() != '\n') {
while (getchar() != '\n')
;
printf("坏牌,请重新输入。\n");
continue;
}
for (j = 0; j < i; ++j)
if (hand[j][0] == hand[i][0] && hand[j][1] == hand[i][1]) {
printf("牌已经存在,请重新输入。\n");
goto end; // 跳过自增i
}
i += 1;
end:; // 标号后面需要有语句
}
}
void analyze_hand(void) {
straight = false;
flush = true;
four = false;
three = false;
pairs = 0;
int num_in_rank[RANK_MAX] = {0};
int i;
for (i = 1; i < CARD_NUM; ++i) {
if (hand[i][1] != hand[i - 1][1])
flush = false;
num_in_rank[hand[i][0]] += 1;
}
num_in_rank[hand[0][0]] += 1;
int n = 0; // 统计连续数量
for (i = 0; i < RANK_MAX; ++i) {
switch (num_in_rank[i]) {
case 4:
four = true;
return;
case 3:
three = true;
if (pairs == 1)
return;
break;
case 2:
pairs += 1;
if (pairs == 2)
return;
break;
case 1:
n += 1;
if (n == CARD_NUM) {
straight = true;
return;
}
break;
default:
if (n != 0)
n = 0;
}
}
}
void print_result(void) {
if (straight && flush) puts("同花顺");
else if (four) puts("四张");
else if (three && pairs) puts("葫芦");
else if (flush) puts("同花");
else if (straight) puts("顺子");
else if (three) puts("三张");
else if (pairs == 2) puts("两对");
else if (pairs) puts("对于");
else puts("其他牌");
}
练习10-4
修改 10.5 节的 poker.c 程序,使其能识别牌的另一种类别——“同花大顺”(同花色的 A、K、Q、J 和 10)。同花大顺的级别高于其他所有的类别。
main.c
#include "card.h"
int main(void) {
for (;;) {
read_cards();
analyze_hand();
print_result();
}
}
card.h
#ifndef C_CARD_H
#define C_CARD_H
#define RANK_MAX 13
#define SUIT_MAX 4
#define CARD_NUM 5
void read_cards(void);
void analyze_hand(void);
void print_result(void);
#endif //C_CARD_H
card.c
#ifndef C_CARD_H
#define C_CARD_H
#define RANK_MAX 13
#define SUIT_MAX 4
#define CARD_NUM 5
void read_cards(void);
void analyze_hand(void);
void print_result(void);
#endif //C_CARD_H
练习10-5
修改 10.5 节的 poker.c 程序,使其能识别“小 A 顺”(即 A、2、3、4 和 5)。
main.c
#include "card.h"
int main(void) {
for (;;) {
read_cards();
analyze_hand();
print_result();
}
}
card.h
#ifndef C_CARD_H
#define C_CARD_H
#define RANK_MAX 13
#define SUIT_MAX 4
#define CARD_NUM 5
void read_cards(void);
void analyze_hand(void);
void print_result(void);
#endif //C_CARD_H
card.c
#include "card.h"
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
static int num_in_rank[RANK_MAX];
static int num_in_suit[SUIT_MAX];
static bool royal_flush;
static bool wheel_straight;
static bool straight;
static bool flush;
static bool four;
static bool three;
static int pairs;
void read_cards(void) {
bool hand[RANK_MAX][SUIT_MAX] = {false};
int rank, suit;
int i;
for (i = 0; i < SUIT_MAX; ++i) {
num_in_rank[i] = 0;
num_in_suit[i] = 0;
}
while (i < RANK_MAX)
num_in_rank[i++] = 0;
i = 0;
while (i < CARD_NUM) {
printf("输入第%d张牌:", i + 1);
switch (getchar()) {
case '2': rank = 0; break;
case '3': rank = 1; break;
case '4': rank = 2; break;
case '5': rank = 3; break;
case '6': rank = 4; break;
case '7': rank = 5; break;
case '8': rank = 6; break;
case '9': rank = 7; break;
case 't': case 'T': rank = 8; break;
case 'j': case 'J': rank = 9; break;
case 'q': case 'Q': rank = 10; break;
case 'k': case 'K': rank = 11; break;
case 'a': case 'A': rank = 12; break;
case '0': exit(EXIT_FAILURE);
default:
puts("坏牌,请重新输入。");
while (getchar() != '\n')
;
continue;
}
switch (getchar()) {
case 'c': case 'C': suit = 0; break;
case 'd': case 'D': suit = 1; break;
case 'h': case 'H': suit = 2; break;
case 's': case 'S': suit = 3; break;
default:
printf("坏牌,请重新输入。\n");
while (getchar() != '\n')
;
continue;
}
if (getchar() != '\n') {
while (getchar() != '\n')
;
printf("坏牌,请重新输入。\n");
continue;
}
if (hand[rank][suit]) {
printf("牌已存在,请重新输入。\n");
continue;
}
num_in_rank[rank] += 1;
num_in_suit[suit] += 1;
hand[rank][suit] = true;
i += 1;
}
}
void analyze_hand(void) {
royal_flush = false;
wheel_straight = false;
straight = false;
flush = false;
four = false;
three = false;
pairs = 0;
int i;
for (i = 0; i < SUIT_MAX; ++i)
if (num_in_suit[i] == CARD_NUM) {
flush = true;
break;
}
if (num_in_rank[0] && num_in_rank[1] && num_in_rank[2] && num_in_rank[3] && num_in_rank[12]) {
wheel_straight = true;
return;
}
if (num_in_rank[8] && num_in_rank[9] && num_in_rank[10] && num_in_rank[11] && num_in_rank[12]) {
royal_flush = true;
return;
}
int n = 0;
for (i = 0; i < RANK_MAX; ++i) {
switch (num_in_rank[i]) {
case 4: four = true; return;
case 3: three = true;
if (pairs == 1)
return;
break;
case 2: pairs += 1;
if (pairs == 2)
return;
break;
case 1: n += 1;
if (n == CARD_NUM) {
straight = true;
return;
}
break;
default:
n = 0;
}
}
}
void print_result(void) {
if (royal_flush) printf("皇家同花顺\n");
else if (wheel_straight) printf("小A顺\n");
else if (straight && flush) printf("同花顺\n");
else if (four) printf("四张\n");
else if (three && pairs) printf("葫芦\n");
else if (flush) printf("同花\n");
else if (straight) printf("顺子\n");
else if (three) printf("三张\n");
else if (pairs == 2) printf("两对\n");
else if (pairs) printf("对于\n");
else printf("其他牌\n");
}
练习10-6
有些计算器(尤其是惠普的计算器)使用逆波兰表示法(Reverse Polish Notation,RPN)来书写数学 表达式。在这一表示法中,运算符放置在操作数的后面而不是放在操作数中间。例如,在逆波兰表 示法中 1+2 将表示为 1 2 +,而 1+23 将表示为 1 2 3 * +。逆波兰表达式可以很方便地用栈求值。算 法从左向右读取运算符和操作数,并执行下列步骤。
(1) 当遇到操作数时,将其压入栈中。
(2) 当遇到运算符时,从栈中弹出它的操作数,执行运算并把结果压入栈中。
编写程序对逆波兰表达式求值。操作数都是个位的整数,运算符为+、-、、/和=。遇到运算符= 时,将显示栈顶项,随后清空栈并提示用户计算新的表达式。这一过程持续进行,直到用户输入一 个既不是运算符也不是操作数的字符为止:
Enter an RPN expression: 1 2 3 * + =
Value of expression: 7
Enter an RPN expression: 5 8 * 4 9 - / =
Value of expression: -8
Enter an RPN expression: q
如果栈出现上溢,程序将显示消息 Expression is too complex 并终止。如果栈出现下溢(例如遇 到表达式 1 2 + +),程序将显示消息 Not enough operands in expression 并终止。提示:把 10.2 节的栈代码整合到你的程序中。使用 scanf(" %c", &ch)读取运算符和操作数。
main.c
#include <stdio.h>
#include <ctype.h>
#include "stack.h"
int main(void) {
int pop2;
char c;
printf("输入 RPN 表达式:");
for (;;) {
scanf(" %c", &c);
switch (c) {
case ' ': break;
case '+': push(pop() + pop()); break;
case '*': push(pop() * pop()); break;
case '-': pop2 = pop(); push(pop() - pop2); break;
case '/': pop2 = pop(); push(pop() / pop2); break;
case '=': printf("结果是:%d\n输入 RPN 表达式:", pop()); break;
case 'q': return 0;
default: if (isdigit(c)) push(c - '0');
}
}
}
stack.h
#ifndef STACK_H
#define STACK_H
#include <stdbool.h>
void make_empty(void);
void push(int v);
int pop(void);
bool is_empty(void);
bool is_full(void);
#endif
stack.c
#include <stdbool.h>
#include <stdlib.h>
#include <stdio.h>
#define STACK_SIZE 100
int contents[STACK_SIZE];
int top = 0;
void stack_overflow() {
printf("表达太复杂!\n");
exit(EXIT_FAILURE);
}
void stack_underflow() {
printf("表达式中的操作数不足!\n");
exit(EXIT_FAILURE);
}
void make_empty(void) {
top = 0;
}
bool is_empty(void) {
return top == 0;
}
bool is_full(void) {
return top == STACK_SIZE;
}
void push(int i) {
if (is_full())
stack_overflow();
else
contents[top++] = i;
}
int pop(void) {
if (is_empty())
stack_underflow();
else
return contents [--top];
}
练习10-7
编写程序,提示用户输入一个数并显示该数,使用字符模拟七段显示器的效果: Enter a number: 491-9014 非数字的字符都将被忽略。在程序中用一个名为 MAX_DIGITS 的宏来控制数的最大位数,MAX_DIGITS 的值为 10。如果数中包含的数位大于这个数,多出来的数位将被忽略。提示:使用两个外部数 组,一个是 segments 数组(见第 8 章的练习题 6),用于存储表示数字和段之间对应关系的数据; 另一个是 digits 数组,这是一个 3行(因为显示出来的每个数字高度都是 3个字符)、MAX_DIGITS× 4 列(数字的宽度是 3 个字符,但为了可读性需要在数字之间增加一个空格)的字符数组。编写 4 个 函数:main、clear_digits_array、process_digit 和 print_digits_array。下面是后 3 个函数 的原型: void clear_digits_array(void); void process_digit(int digit, int position); void print_digits_array(void); clear_digits_array函数在digits数组的所有元素中存储空白字符。process_digit函数把digit 的七段表示存储到 digits 数组的指定位置(位置为 0~MAX_DIGITS1)。print_digits_array 函数 分行显示 digits 数组的每一行,产生的输出如示例图所示。
#include <stdio.h>
#include <ctype.h>
#include "../13/my_util.h"
#define MAX_DIGITS 10
#define MAX_WIDTH 4
const char char_digit[10][3][3] =
{
{' ', '_', ' ',
'|', ' ', '|',
'|', '_', '|'},
{' ', ' ', ' ',
' ', ' ', '|',
' ', ' ', '|'},
{' ', '_', ' ',
' ', '_', '|',
'|', '_', ' '},
{' ', '_', ' ',
' ', '_', '|',
' ', '_', '|'},
{' ', ' ', ' ',
'|', '_', '|',
' ', ' ', '|'},
{' ', '_', ' ',
'|', '_', ' ',
' ', '_', '|'},
{' ', '_', ' ',
'|', '_', ' ',
'|', '_', '|'},
{' ', '_', ' ',
' ', ' ', '|',
' ', ' ', '|'},
{' ', '_', ' ',
'|', '_', '|',
'|', '_', '|'},
{' ', '_', ' ',
'|', '_', '|',
' ', '_', '|'},
};
char digits[3][MAX_DIGITS * MAX_WIDTH];
void clear_digits_array(void);
void print_digits_array(void);
void process_digit(int digit, int position);
int main(void) {
int ch;
clear_digits_array();
printf("输入数字:");
for (int i = 0; i < MAX_DIGITS; ++i) {
while (!isdigit(ch = getchar()))
if (ch == '\n')
goto end; // 跳两层循环
process_digit(ch - '0', i);
}
end:
print_digits_array();
return 0;
}
void process_digit(int digit, int position) {
int i, j;
for (i = 0; i < 3; ++i)
for (j = 0; j < 3; ++j)
digits[i][position * MAX_WIDTH + j] = char_digit[digit][i][j];
}
void clear_digits_array(void) {
for (int j = 3 * MAX_DIGITS * MAX_WIDTH - 1; j >= 0; --j)
digits[0][j] = ' ';
}
void print_digits_array(void) {
int i, j;
for (i = 0; i < 3; ++i) {
for (j = 0; j < MAX_DIGITS * MAX_WIDTH; ++j)
putchar(digits[i][j]);
endl;
}
}