部分答案参考了官方题解和网上的答案,仅供参考,可能也有部分bug未发现或解决。代码在gitee上面。
exercise4-1#
编写函数 strindex(s, t),它返回字符串 t 在 s 中最右边出现的位置。如果 s 中不包含 t,则返回-1。
代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
| void getLine(char s[]) {
int c, i = 0;
while ((c = getchar()) != EOF && c != '\n')
s[i++] = c;
}
int strindex(char s[], char t[]) {
int i, j, k;
int lens = strlen(s), lent = strlen(t);
for (i = lens - 1; i >= 0; i--) {
for (j = lent - 1, k = i; j >= 0 && k >= 0 && s[k] == t[j]; j--, k--);
if (j < 0)
return k + 1;
}
return -1;
}
int main() {
char s[MAXLEN] = {0};
char t[MAXLEN] = {0};
printf("Please input the string s: \n");
getLine(s);
printf("Please input the string t: \n");
getLine(t);
printf("The rightmost position of '%s' in '%s' is: %d\n", t, s, strindex(s, t));
return 0;
}
|
运行:
1
2
3
4
5
| Please input the string s:
this is an idea, a good idea
Please input the string t:
idea
The rightmost position of 'idea' in 'this is an idea, a good idea' is: 24
|
exercise4-2#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
| #include <stdio.h>
#include <ctype.h>
#define MAXLEN 1024
double atof(const char s[]) {
double val, power;
int i, sign, expSign, exp;
for (i = 0; isspace(s[i]); i++) // skip white space
;
sign = (s[i] == '-') ? -1 : 1;
if (s[i] == '+' || s[i] == '-')
i++;
for (val = 0.0; isdigit(s[i]); i++)
val = 10.0 * val + (s[i] - '0');
if (s[i] == '.')
i++;
for (power = 1.0; isdigit(s[i]); i++) {
val = 10.0 * val + (s[i] - '0');
power *= 10.0;
}
val = sign * val / power;
if (s[i] == 'e' || s[i] == 'E') {
i++;
if (s[i] == '+' || s[i] == '-') {
expSign = (s[i] == '-') ? -1 : 1;
i++;
}
// 指数
for (exp = 0; isdigit(s[i]); i++)
exp = 10 * exp + (s[i] - '0');
if (expSign == 1)
while (exp-- > 0)
val *= 10;
else
while (exp-- > 0)
val /= 10;
}
return val;
}
int main() {
char s[MAXLEN] = {0};
printf("Please input the string s: \n");
scanf("%s", s);
printf("%g\n", atof(s));
return 0;
}
|
运行:
1
2
3
| Please input the string s:
123e-10
1.23e-08
|
exercise4-3#
在有了基本框架后,对计算器程序进行扩充就比较简单了。在该程序中加入取模(%)运算符,并注意考虑负数的情况。
若要能够处理负数的情况,则需要在getop()
的处理负号时进行进一步判断:需要判断下一个字符是空格还是数字,修改后的getop()
如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
| /* getop: get next character or numeric operand */
int getop(char s[])
{
int i, c, next;
while ((s[0] = c = getch()) == ' ' || c == '\t')
;
s[1] = '\0';
if (!isdigit(c) && c != '.' && c != '-')
return c; /* not a number */
i = 0;
if (c == '-') {
next = getch();
if (!isdigit(next) && next != '.') {
ungetch(next);
return c; /* not a number, return '-' */
}
c = next;
s[++i] = c;
}
if (isdigit(c)) /* collect integer part */
while (isdigit(s[++i] = c = getch()))
;
if (c == '.') /* collect fraction part */
while (isdigit(s[++i] = c = getch()))
;
s[i] = '\0';
if (c != EOF)
ungetch(c);
return NUMBER;
}
|
加入取模运算符,则比较简单,与处理 - 与 / 类似:
1
2
3
4
5
| /* +和*无需注意操作数的顺序,即a+b=b+a,但是-/%需要注意 */
case '%':
op2 = pop();
push((int)pop() % (int)op2);
break;
|
运行:
1
2
3
4
5
| Please input expression:
3 2 %
1
3 0 %
3
|
exercise4-4#
在栈操作中添加几个命令,分别用于在不弹出元素的情况下打印栈顶元素;复制栈顶元素;交换栈顶两个元素的值。另外增加一个命令用于清空栈。
代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
| /* print and return top value from stack without popping */
double top(void)
{
if (sp > 0) {
printf("Top of stack: %g\n", val[sp-1]);
return val[sp-1];
}
else {
printf("error: stack empty\n");
return 0.0;
}
}
/* duplicate top value to stack */
void duptop(void)
{
if (sp > 0)
push(val[sp-1]);
else
printf("error: stack empty\n");
}
/* swap top two values */
void swaptop(void)
{
if (sp > 1)
{
double temp = val[sp-1];
val[sp-1] = val[sp-2];
val[sp-2] = temp;
}
else
printf("error: not enough elements to swap\n");
}
|
exercise4-5#
给计算器程序增加访问 sin、exp 与 pow 等库函数的操作。有关这些库函数的详细信息,参见附录 B.4 节中的头文件<math.h>。
引入<math.h>
头文件,然后为了编译通过,参考了Undefined reference to pow( ) in C, despite including math.h [duplicate]的解决办法。在main函数中添加了对这三种运算的支持:
代码:
1
2
3
4
5
6
7
8
9
10
| case 's': // sin
push(sin(pop()));
break;
case 'e': // exp
push(exp(pop()));
break;
case 'p': // pow
op2 = pop();
push(pow(pop(), op2));
break;
|
运行:
1
2
3
4
5
6
7
| Please input expression:
3 e
ans is: 20.085537
2 5 p
ans is: 32
3.1415926 s
ans is: 5.3589793e-08
|
exercise4-6#
给计算器程序增加处理变量的命令(提供 26 个具有单个英文字母变量名的变量很容易)。增加一个变量存放最近打印的值。
为了增加处理变量,需要在代码中设置一个变量var
来存储上一次计算出来的结果,并使用一个flag
来验证该变量是否有效,因为第一次计算时,var
是不存在的。增加main函数中的处理
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
| #define VAR 'v'
int getop(char s[])
{
...
if (c == 'v') {
return VAR;
}
...
}
/* reverse Polish calculator */
int main()
{
...
switch (type)
{
// 变量
case VAR:
if (flag == 1)
push(var);
else
printf("error: no variable\n");
break;
// 数值
case NUMBER:
push(atof(s));
break;
// 操作符
...
// 结束输入
case '\n':
printf("ans is:\t%.8g\n", (var = pop()));
flag = 1;
break;
...
}
}
|
运行:
1
2
3
4
5
6
7
| Please input expression:
1 2 +
ans is: 3
3 v *
ans is: 9
v 3 p
ans is: 729
|
exercise4-7#
编写一个函数 ungets(s),将整个字符串 s 压回到输入中。ungets 函数需要使用 buf 和 bufp 吗?它能否仅使用 ungetch 函数?
不需要直接使用buf和bufp,可以直接调用ungetch函数:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| /* push entire string s back on input */
void ungets(char s[])
{
int len = strlen(s);
while (len > 0)
ungetch(s[--len]);
}
int main()
{
char s[] = "goood!";
int c;
ungets(s);
while ((c = getch()) != EOF)
putchar(c);
return 0;
}
|
运行:
exercise4-8#
假定最多只压回一个字符。请相应地修改 getch 与 ungetch 这两个函数。
代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| int getch(void)
{
int temp = buf;
buf = EOF;
if (temp == EOF)
temp = getchar();
return temp;
}
/* push character back on input */
void ungetch(int c)
{
if (buf != EOF)
printf("ungetch: too many characters\n");
else
buf = c;
}
|
exercise4-9#
以上介绍的 getch 与 ungetch 函数不能正确地处理压回的 EOF。考虑压回EOF 时应该如何处理?请实现你的设计方案。
exercise4-8的程序可以正确处理压回的EOF
exercise4-10#
另一种方法是通过 getline 函数读入整个输入行,这种情况下可以不使用 getch 与 ungetch 函数。请运用这一方法修改计算器程序。
代码(因完整代码过长,故只显示相对于exercise4-6的代码的增加及更改部分):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
| char line[MAXLINE]; /* input line */
/* getop: get next character or numeric operand */
int getop(char s[])
{
int i, c;
while ((s[0] = c = line[idx++]) == ' ' || c == '\t')
;
}
int Getline() {
int c, i;
for (i = 0; i < MAXLINE - 1 && (c = getchar()) != EOF && c != '\n'; i++) {
line[i] = c;
}
if (c == '\n')
line[i++] = c;
line[i] = '\0';
return i;
}
/* reverse Polish calculator */
int main()
{
int type;
double op2;
char s[MAXOP];
while (1)
{
printf("Please input expression:\n");
if (Getline() == 0)
break;
idx = 0;
while ((type = getop(s)) != '\0')
{
...
}
}
return 0;
}
|
运行:
1
2
3
4
5
6
7
| Please input expression:
1 2 +
ans is: 3
Please input expression:
1 v /
ans is: 0.33333333
Please input expression:
|
exercise4-11#
修改 getop 函数,使其不必使用 ungetch 函数。提示:可以使用一个 static 类型的内部变量解决该问题。
修改 getop.c,使用一个 static 类型的内部变量来存储多读的字符。如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
| #include <stdio.h>
#include <ctype.h>
#include "calc.h"
/* getop: get next character or numeric operand */
int getop(char s[])
{
/* record the character read last time */
static int lastc = ' ';
int i, c;
while ((s[0] = c = lastc) == ' ' || c == '\t')
lastc = getchar();
s[1] = '\0';
if (!isdigit(c) && c != '.' && c != '-')
return c; /* not a number */
i = 0;
if (c == '-') {
if (!isdigit(lastc = getchar()) && lastc != '.')
return c; /* not a number, return '-' */
s[++i] = c = lastc;
}
if (isdigit(c)) /* collect integer part */
while (isdigit(s[++i] = c = getchar()))
;
if (c == '.') /* collect fraction part */
while (isdigit(s[++i] = c = getchar()))
;
s[i] = '\0';
lastc = c;
return NUMBER;
}
|
exercise4-12#
运用 printd 函数的设计思想编写一个递归版本的 itoa 函数,即通过递归调用把整数转换为字符串。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
| void itoa(int n, char s[]) {
static int i = 0; // 静态变量,用于记录字符串的当前位置
if (n < 0) {
s[i++] = '-';
n = -n;
}
if (n / 10)
itoa(n / 10, s);
s[i++] = n % 10 + '0';
s[i] = '\0'; // 字符串结束符
}
int main() {
int n;
char str[MAXLEN];
printf("Please input n: ");
scanf("%d", &n);
itoa(n, str);
printf("ans is: %s\n", str);
}
|
exercise4-13#
编写一个递归版本的 reverse(s)函数,以将字符串 s 倒置。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| void reverse(char s[], int l, int r) {
if (l >= r)
return;
char temp = s[l];
s[l] = s[r];
s[r] = temp;
reverse(s, l + 1, r - 1);
}
int main() {
char str[MAXLINE];
printf("Please input str:\n");
scanf("%s", str);
reverse(str, 0, strlen(str) - 1);
printf("Reversed string: %s\n", str);
return 0;
}
|
exercise4-14#
定义宏 swap(t, x, y)以交换 t 类型的两个参数。(使用程序块结构会对你有所帮助。)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
|
#include <stddef.h>
#include <stdio.h>
// 一个字节一个字节进行交换
#define swap(t, x, y) \
do \
{ \
unsigned char *a = (unsigned char *)(&(x)); \
unsigned char *b = (unsigned char *)(&(y)); \
size_t ByteCount = sizeof(t); \
char temp; \
while (ByteCount--) \
{ \
temp = *a; \
*a = *b; \
*b = temp; \
a++; \
b++; \
} \
} while (0)
int main()
{
int ix, iy;
double dx, dy;
char *cpx, *cpy;
ix = 2;
iy = 4;
printf("integers before swap: %d and %d\n", ix, iy);
swap(int, ix, iy);
printf("integers after swap: %d and %d\n", ix, iy);
dx = 225.1;
dy = 417.2;
printf("doubles before swap: %g and %g\n", dx, dy);
swap(double, dx, dy);
printf("doubles after swap: %g and %g\n", dx, dy);
cpx = "gooood";
cpy = "idea";
printf("char pointers before swap: %s and %s\n", cpx, cpy);
swap(char *, cpx, cpy);
printf("char pointers after swap: %s and %s\n", cpx, cpy);
return 0;
}
|
1
2
3
4
5
6
| integers before swap: 2 and 4
integers after swap: 4 and 2
doubles before swap: 225.1 and 417.2
doubles after swap: 417.2 and 225.1
char pointers before swap: gooood and idea
char pointers after swap: idea and gooood
|