练习8-1
修改 8.1 节的程序 repdigit.c,使其可以显示出哪些数字有重复(如果有的话):
Enter a number: 939577
Repeated digit(s): 7 9
#include <stdio.h>
#include <stdbool.h>
int main(void) {
bool digit_repeat[10] = {false};
bool digit_seen[10] = {false};
long n;
printf("输入数字:");
scanf("%ld", &n);
do {
int t = n % 10;
if (digit_repeat[t])
digit_seen[t] = true;
else
digit_repeat[t] = true;
} while (n /= 10);
printf("显示重复:");
for (n = 0; n < 10; ++n)
if (digit_seen[n])
printf("%d ", n);
putchar('\n');
return 0;
}
练习8-2
修改 8.1 节的程序 repdigit.c,使其打印出一份列表,显示出每个数字在数中出现的次数:
Enter a number: 41271092
Digit: 0 1 2 3 4 5 6 7 8 9
Occurrences: 1 2 2 0 1 0 0 1 0 1
#include <stdio.h>
int main(void) {
int digit[10] = {0};
int i;
long n;
printf("输入数字:");
scanf("%ld", &n);
do {
++digit[n % 10];
} while (n /= 10);
printf("数字:");
for (i = 0; i < 10; ++i)
printf("%3d", i);
putchar('\n');
printf("次数:");
for (i = 0; i < 10; ++i)
printf("%3d", digit[i]);
putchar('\n');
return 0;
}
练习8-3
修改 8.1节的程序 repdigit.c,使得用户可以输入多个数进行重复数字的判断。当用户输入的数小于或 等于 0 时,程序终止。
#include <stdbool.h>
#include <stdio.h>
int main(void) {
bool digit_seen[10] = {false};
int digit;
long n;
for (;;) {
printf("请输入数字:");
scanf("%ld", &n);
if (n <= 0) return 0;
do {
digit = n % 10;
if (digit_seen[digit])
break;
digit_seen[digit] = true;
} while (n /= 10);
printf("%s\n\n", n ? "有重复数字" : "无重复数字");
}
}
练习8-4
修改 8.1 节的程序 reverse.c,利用表达式(int)(sizeof(a) / sizeof(a[0]))(或者具有相同值的宏) 来计算数组的长度。
#include <stdio.h>
#define SIZE (int) (sizeof(a) / sizeof(a[0]))
int main(void) {
int a[5], i;
printf("输入%d个数:", SIZE);
for (i = 0; i < SIZE; i++)
scanf("%d", &a[i]);
printf("按相反的顺序:");
for (i = SIZE - 1; i >= 0; i--)
printf(" %d", a[i]);
printf("\n");
return 0;
}
练习8-5
修改 8.1 节的程序 interest.c,使得修改后的程序可以每月整合一次利息,而不是每年整合一次利息。 不要改变程序的输出格式,余额仍按每年一次的时间间隔显示。
#include <stdio.h> #define NUM_RATES ((int) (sizeof(value) / sizeof(value[0]))) #define INITIAL_BALANCE 100.00 int main(void) { int i, j, low_rate, num_years, year; double value[5]; printf("输入利率:"); scanf("%d", &low_rate); printf("输入年数:"); scanf("%d", &num_years); printf("\nYears"); for (i = 0; i < NUM_RATES; i++) { printf("%6d%%", low_rate + i); value[i] = INITIAL_BALANCE; } printf("\n"); for (year = 1; year <= num_years; year++) { printf("%3d ", year); for (i = 0; i < NUM_RATES; i++) { for (j = 0; j < 12; ++j) value[i] += (low_rate + i) / 1200.0 * value[i]; printf("%7.2f", value[i]); } printf("\n"); } return 0; }
练习8-6
有一个名叫 B1FF 的人,是典型的网络新手,他有一种独特的编写消息的方式。下面是一条常见的 B1FF 公告: H3Y DUD3, C 15 R1LLY C00L!!! 编写一个“B1FF 过滤器”,它可以读取用户输入的消息并把此消息翻译成 B1FF 的表达风格: Enter message: Hey dude, C is rilly cool In B1FF-speak: H3Y DUD3, C 15 R1LLY C00L!!! 程序需要把消息转换成大写字母,用数字代替特定的字母(A→4、B→8、E→3、I→1、O→0、S→ 5),然后添加 10 个左右的感叹号。提示:把原始消息存储在一个字符数组中,然后从数组头开始逐 个翻译并显示字符。
#include <stdio.h>
#define MAX 100
int main(void) {
char msg[MAX + 1];
int ch;
int i;
for (i = 0; (ch = getchar()) != '\n' && i < MAX; ++i)
msg[i] = ch;
printf("转换后的:");
for (i = 0; msg[i]; ++i) {
switch (msg[i]) {
case 'A': case 'a': putchar('4'); break;
case 'B': case 'b': putchar('8'); break;
case 'E': case 'e': putchar('3'); break;
case 'I': case 'i': putchar('1'); break;
case 'O': case 'o': putchar('0'); break;
case 'S': case 's': putchar('5'); break;
default: putchar(msg[i]);
}
}
printf("!!!!!!!!!!\n");
return 0;
}
练习8-7
编写程序读取一个 5×5 的整数数组,然后显示出每行的和与每列的和。 Enter row 1: 8 3 9 0 10 Enter row 2: 3 5 17 1 1 Enter row 3: 2 8 6 23 1 Enter row 4: 15 7 3 2 9 Enter row 5: 6 14 2 6 0 Row totals: 30 27 40 36 28 Column totals: 34 37 37 32 21
#include <stdio.h>
int main(void) {
int arr[5][5];
int i, j;
for (i = 0; i < 5; ++i) {
printf("输入第%d行:", i + 1);
for (j = 0; j < 5; ++j)
scanf("%d", arr[i][j]);
}
int sum;
puts("行总和:");
for (i = 0; i < 5; ++i) {
sum = 0;
for (j = 0; j < 5; ++j)
sum += arr[i][j];
printf("%d ", sum);
}
putchar('\n')
puts("列总和:");
for (i = 0; i < 5; ++i) {
sum = 0;
for (j = 0; j < 5; ++j)
sum += arr[j][i];
printf("%d ", sum);
}
putchar('\n');
return 0;
}
练习8-8
修改编程题 7,使其提示用户输入每个学生 5 门测验的成绩,一共有 5 个学生。然后计算每个学生的 总分和平均分,以及每门测验的平均分、高分和低分。
#include <stdio.h>
int main(void) {
int arr[5][5];
int i, j;
for (i = 0; i < 5; ++i) {
printf("输入学生%d分数:", i + 1);
for (j = 0; j < 5; ++j)
scanf("%d", arr[i][j]);
}
int sum;
for (i = 0; i < 5; ++i) {
sum = 0;
for (j = 0; j < 5; ++j)
sum += arr[i][j];
printf("学生%d总分%d,平均分%.1f\n", i + 1, sum, sum / 5.0);
}
return 0;
}
练习8-9
编写程序,生成一种贯穿 10×10 字符数组(初始时全为字符’.')的“随机步法”。程序必须随机地 从一个元素 “走到”另一个元素,每次都向上、向下、向左或向右移动一个元素位置。已访问过的 元素按访问顺序用字母 A~Z 进行标记。下面是一个输出示例:
#include <stdio.h>
#include <time.h>
#include <stdlib.h>
#define MAX 100
#define UP 0
#define DOWN 1
#define LEFT 2
#define RIGHT 3
int main(void) {
char arr[10][10];
int directions[4];
int n;
int i, j;
int ch = 'A';
srand(time(NULL));
for (i = 0; i < 100; ++i)
arr[0][i] = '.';
i = 0; j = 0;
arr[0][0] = ch;
while (++ch <= 'Z') {
n = 0;
if (i - 1 >= 0 && arr[i - 1][j] == '.')
directions[n++] = UP;
if (i + 1 < 10 && arr[i + 1][j] == '.')
directions[n++] = DOWN;
if (j - 1 >= 0 && arr[i][j - 1] == '.')
directions[n++] = LEFT;
if (j + 1 < 10 && arr[i][j + 1] == '.')
directions[n++] = RIGHT;
if (n == 0) break;
switch (directions[rand() % n]) {
case UP: arr[--i][j] = ch; break;
case DOWN: arr[++i][j] = ch; break;
case LEFT: arr[i][--j] = ch; break;
default: arr[i][++j] = ch; break;
}
}
for (i = 0; i < 10; ++i) {
for (j = 0; j < 10; ++j)
printf("%c ", arr[i][j]);
putchar('\n');
}
return 0;
}
练习8-10
修改第 5 章的编程题 8,用一个数组存储航班起飞时间,另一个数组存储航班抵达时间。(时间用整 数表示,表示从午夜开始的分钟数。)程序用一个循环搜索起飞时间数组,以找到与用户输入的时间 最接近的起飞时间。
#include <stdio.h>
int main(void) {
const int departure_times[] = {480, 583, 679, 767, 840, 945, 1140, 1305};
const int arrival_times[] = {616, 712, 811, 900, 968, 1075, 1280, 1438};
int hour, minute;
int index;
printf("输入预计时间:");
scanf("%d :%d", &hour, &minute);
int time = hour * 60 + minute;
if (time > 1440 || time < 0)
return -1;
if (1440 - departure_times[7] > departure_times[0] && time > departure_times[7] + ((1440 - departure_times[7]) + departure_times[0]) / 2)
index = 7;
else if (time < departure_times[0] - ((1440 - departure_times[7]) + departure_times[0]) / 2)
index = 0;
else {
for (index = 0; index < 7; ++index)
if (time <= departure_times[index] + (departure_times[index + 1] - departure_times[index]) / 2)
break;
}
hour = departure_times[index] / 60;
minute = departure_times[index] % 60;
if (hour == 12)
printf("起飞时间是 12:%02d PM,", minute);
else if (hour == 0)
printf("起飞时间是 12:%02d AM,", minute);
else if (hour > 12)
printf("起飞时间是 %02d:%02d PM,", hour - 12, minute);
else
printf("起飞时间是 %02d:%02d AM,", hour, minute);
hour = arrival_times[index] / 60;
minute = arrival_times[index] % 60;
if (hour == 12)
printf("抵达时间是 12:%02d PM。\n", minute);
else if (hour == 0)
printf("抵达时间是 12:%02d AM。\n", minute);
else if (hour > 12)
printf("抵达时间是 %02d:%02d PM。\n", hour - 12, minute);
else
printf("抵达时间是 %02d:%02d AM。\n", hour, minute);
return 0;
}
练习8-11
修改第 7 章的编程题 4,给输出加上标签: Enter phone number: 1-800-COL-LECT In numeric form: 1-800-265-5328 在显示电话号码之前,程序需要将其(以原始格式或数值格式)存储在一个字符数组中。可以假定电话号码的 长度不超过 15 个字符。
#include <stdio.h>
#include <ctype.h>
#define MAX 15
int main(void) {
char value[26] = {
'2', '2', '2', '3', '3', '3', '4', '4', '4', '5', '5', '5', '6',
'6', '6', '7', '7', '7', '7', '8', '8', '8', '9', '9', '9', '9'
};
char msg[MAX + 1];
int ch;
int i;
printf("输入电话号:");
for (i = 0; (ch = getchar()) != '\n'; ++i)
msg[i] = ch;
msg[i] = '\0';
printf("以数字表示:");
for (i = 0; msg[i]; ++i) {
if (islower(msg[i]))
putchar(value[msg[i] - 'a']);
else if (isupper(msg[i]))
putchar(value[msg[i] - 'A']);
else
putchar(msg[i]);
}
putchar('\n');
return 0;
}
练习8-12
修改第 7 章的编程题 5,用数组存储字母的面值。数组有 26 个元素,对应字母表中的 26 个字母。例 如,数组元素 0 存储 1(因为字母 A 的面值为 1),数组元素 1 存储 3(因为字母 B 的面值为 3),等 等。每读取输入单词中的一个字母,程序都会利用该数组确定字符的拼字值。使用数组初始化器来 建立该数组。
#include <stdio.h>
#include <ctype.h>
int main(void) {
char values[26] = {
1, 3, 3, 2, 1, 4, 2, 4, 1, 8, 5, 1, 3,
1, 1, 3, 10, 1, 1, 1, 1, 4, 4, 8, 4, 10
};
int sum = 0;
int ch;
printf("输入:");
while ((ch = getchar()) != '\n')
if (islower(ch))
sum += values[ch - 'a'];
else if (isupper(ch))
sum += values[ch - 'A'];
printf("拼字值结果是:%d\n", sum);
return 0;
}
练习8-13
修改第 7 章的编程题 11,给输出加上标签: Enter a first and last name: Lloyd Fosdick You enered the name: Fosdick, L. 在显示姓(不是名)之前,程序需要将其存储在一个字符数组中。可以假定姓的长度不超过 20 个字符
#include <stdio.h>
#define MAX 20
int main(void) {
char first_name[MAX + 1];
int ch;
printf("输入名字和姓氏:");
// 跳过前面空格
while ((ch = getchar()) == ' ')
;
char last = ch;
// 跳过名字剩余内容
while (getchar() != ' ')
;
scanf("%s", first_name);
printf("你输入了这个名字:%s, %c.\n", first_name, last);
return 0;
}
练习8-14
编写程序颠倒句子中单词的顺序: Enter a sentence: you can cage a swallow can’t you? Reversal of sentence: you can’t swallow a cage can you? 提示:用循环逐个读取字符,然后将它们存储在一个一维字符数组中。当遇到句号、问号或者感叹 号(称为“终止字符”)时,终止循环并把终止字符存储在一个 char 类型变量中。然后再用一个循 环反向搜索数组,找到最后一个单词的起始位置。显示最后一个单词,然后反向搜索倒数第二个单 词。重复这一过程,直至到达数组的起始位置。最后显示出终止字符。
#include <stdio.h>
#define MAX 100
int main(void) {
char sentence[MAX + 1];
char end;
int i, j;
int k;
for (i = 0; i < MAX; ++i) {
if ((sentence[i] = getchar()) == '\n')
return 0; // 没有结束字符
if (sentence[i] == '.' || sentence[i] == '?' || sentence[i] == '!') {
end = sentence[i];
goto skip;
}
}
return 0; // 没有结束字符
skip:
// 记住改变的位置
sentence[i] == ' ';
k = i;
while (--i != -1) {
if (sentence[i] == ' ') {
for (j = i + 1; sentence[j] != ' '; ++j)
putchar(sentence[j]);
putchar(' ');
}
}
while (sentence[++i] != ' ')
putchar(sentence[i]);
printf("%c\n", end);
sentence[k] = end; // 还回字符
return 0;
}
练习8-15
目前已知的最古老的一种加密技术是恺撒加密(得名于 Julius Caesar)。该方法把一条消息中的每个字 母用字母表中固定距离之后的那个字母来替代。(如果越过了字母 Z,则会绕回到字母表的起始位 置。例如,如果每个字母都用字母表中两个位置之后的字母代替,那么Y就被替换为A,Z就被替换 为 B。)编写程序用恺撒加密方法对消息进行加密。用户输入待加密的消息和移位计数(字母移动的 位置数目)
#include <stdio.h>
#include <ctype.h>
#define MAX 80
int main(void) {
char str[MAX + 1];
int n;
int m;
printf("输入:");
for (n = 0; n < MAX; ++n)
if ((str[n] = getchar()) == '\n')
break;
str[n] = '\0';
printf("输入加密:");
scanf("%d", &m);
while (--n >= 0) {
if (islower(str[n]))
str[n] = (str[n] - 'a' + m) % 26 + 'a';
else if (isupper(str[n]))
str[n] = (str[n] - 'A' + m) % 26 + 'A';
}
printf("结果:%s\n", str);
return 0;
}
练习8-16
编程测试两个单词是否为变位词(相同字母的重新排列): Enter first word: smartest Enter second word: mattress The words are anagrams. Enter first word: dumbest Enter second word: stumble The words are not anagrams. 用一个循环逐个字符地读取第一个单词,用一个 26 元的整数数组记录每个字母的出现次数。(例 如,读取单词 smartest 之后,数组包含的值为 10001000000010000122000000,表明 smartest 包 含一个 a、一个 e、一个 m、一个 r、两个 s 和两个 t。)用另一个循环读取第二个单词,这次每读取 一个字母就把相应数组元素的值减 1。两个循环都应该忽略不是字母的那些字符,并且不区分大小 写。第二个单词读取完毕后,再用一个循环来检查数组元素是否为全 0。如果是全 0,那么这两个单 词就是变位词。提示:可以使用中的函数,如 isalpha 和 tolower。
#include <stdio.h>
#include <ctype.h>
int main(void) {
int character[26] = {0};
int ch;
printf("输入第一个单词:");
while ((ch = getchar()) != '\n')
if (isalpha(ch))
character[toupper(ch) - 'A'] += 1;
printf("输入第二个单词:");
while ((ch = getchar()) != '\n')
if (isalpha(ch))
character[toupper(ch) - 'A'] -= 1;
for (int i = 0; i < 26; ++i)
if (character[i] != 0) {
printf("不是变位词。\n");
return 0;
}
printf("是变位词。\n");
return 0;
}
练习8-17
编写程序打印 n×n 的幻方(1, 2, …, n2的方阵排列,且每行、每列和每条对角线上的和都相等)。由 用户指定 n 的值: This program creates a magic square of a specified size. The size must be an odd number between 1 and 99. Enter size of magic square: 5 17 24 1 8 15 23 5 7 14 16 4 6 13 20 22 10 12 19 21 3 11 18 25 2 9 把幻方存储在一个二维数组中。起始时把数 1 放在第 0 行的中间,剩下的数 2, 3, …, n2依次向上移动 一行并向右移动一列。当可能越过数组边界时需要“绕回”到数组的另一端。例如,如果需要把下 一个数放到第1 行,我们就将其存储到第 n1 行(最后一行);如果需要把下一个数放到第 n 列, 我们就将其存储到第 0 列。如果某个特定的数组元素已被占用,那就把该数存储在前一个数的正下 方。如果你的编译器支持变长数组,则声明数组有 n 行 n 列,否则声明数组有 99 行 99 列。
#include <stdio.h>
int main(void) {
int n;
printf("输入幻方边长:");
scanf("%d", &n);
int arr[n][n];
int i = 0, j = n / 2;
int b = n - 1; // 幻方边界值
int k; // 多功能索引
n *= n; // 减少重复计算
for (k = 0; k < n; ++k)
arr[0][k] = 0;
int count = -1;
for (k = 1; k <= n; ++k) {
arr[i][j] = k;
// 利用规律
if (++count == b) {
count = -1;
i = i + 1 > b ? 0 : i + 1;
} else {
i = i - 1 < 0 ? b : i - 1;
j = j + 1 > b ? 0 : j + 1;
}
}
for (i = 0; i <= b; ++i) {
for (j = 0; j <= b; ++j)
printf("%3d", arr[i][j]);
putchar('\n');
}
return 0;
}