C 程序设计语言第二版——第四章练习题
- 1. Write the function strindex(s,t) which returns the position of the rightmost occurrence of t in s, or -1 if there is none.
- 2. Extend atof to handle scientific notation of the formwhere a floating-point number may be followed by e or E and an optionally signed exponent.
- 3. Given the basic framework, it's straightforward to extend the calculator. Add the modulus (%) operator and provisions for negative numbers.
- 4. Add the commands to print the top elements of the stack without popping, to duplicate it, and to swap the top two elements. Add a command to clear the stack.
- 5. Add access to library functions like sin, exp, and pow.
- 6. Add commands for handling variables. (It's easy to provide twenty-six variables with single-letter names.) Add a variable for the most recently printed value.
- 7. Write a routine ungets(s) that will push back an entire string onto the input. Should ungets know about buf and bufp, or should it just use ungetch?
- 8. Suppose that there will never be more than one character of pushback. Modify getch and ungetch accordingly.
- ?9. Our getch and ungetch do not handle a pushed-back EOF correctly. Decide what their properties ought to be if an EOF is pushed back, then implement your design.
- 10. An alternate organization uses getline to read an entire input line; this makes getch and ungetch unnecessary. Revise the calculator to use this approach.
- 11. Modify getop so that it doesn't need to use ungetch. Hint: use an internal static variable.
- 12. Adapt the ideas of printd to write a recursive version of itoa; that is, convert an integer into a string by calling a recursive routine.
- 13. Write a recursive version of the function reverse(s), which reverses the string s in place.
- 14. Define a macro swap(t,x,y) that interchanges two arguments of type t. (Block structure will help.)
1. Write the function strindex(s,t) which returns the position of the rightmost occurrence of t in s, or -1 if there is none.
#include<stdio.h>
#include<string.h>
#define LEN 100
char *s_gets(char *s, int n);
int strindex(char *s, char *t);
int main(void)
{
char pattern[LEN];
char s[LEN];
int index;
printf("Please input the search string:\n");
s_gets(pattern, LEN);
printf("Please enter strings to be tested (empty line to quit):\n");
while(s_gets(s, LEN) && s[0] != '\0')
if ((index = strindex(s, pattern)) != -1)
printf("%s\n\n", s+index);
return 0;
}
char *s_gets(char *s, int n)
{
char *ret, *find;
ret = fgets(s, n, stdin);
if(ret)
{
find = strchr(s, '\n');
if(find)
*find = '\0';
else
while(getchar() != '\n')
continue;
}
return ret;
}
int strindex(char *s, char *t)
{
int i, j, k;
int len1 = strlen(s);
int len2 = strlen(t);
for(i = len1-1; i >= 0; i--)
{
for(j = i, k = len2-1; k >= 0 && j >= 0 && s[j] == t[k]; j--, k--)
;
if (k < 0)
return (j+1);
}
return -1;
}
2. Extend atof to handle scientific notation of the formwhere a floating-point number may be followed by e or E and an optionally signed exponent.
#include<stdio.h>
#include<string.h>
#include<math.h>
#include<ctype.h>
#define LEN 40
char *s_gets(char *s, int n);
double myatof(const char *s);
int main(void)
{
char s[LEN];
double num;
puts("Enter strings, enter a newline to quit:");
while(s_gets(s, LEN) && s[0] != '\0')
{
num = myatof(s);
printf("String: %s, corrsponding number: %f.\n",s, num);
}
puts("Bye!");
return 0;
}
char *s_gets(char *s, int n)
{
char *ret, *find;
ret = fgets(s, n, stdin);
if(ret)
{
find = strchr(s, '\n');
if(find)
*find = '\0';
else
while(getchar() != '\n')
continue;
}
return ret;
}
double myatof(const char *s)
{
int sign, sign_e;//数值的符号,指数部分的符号
int hasdot = 0;
int hase = 0;
double intpart = 0.0;//小数的整数部分
double decpart = 0.0; //小数的小数部分
int decdigit = 1; //小数的小数位数
int exp = 0; //指数部分
double ret;
int i;
//跳过开头的空格
for (i = 0; isspace(s[i]); i++)
;
//判断符号,如有,跳过
sign = (s[i] == '-') ? -1 : 1;
if (s[i] == '-' || s[i] == '+')
i++;
//判断浮点数
//第一部分:
for (; s[i] != '\0'; i++)
{
if (isdigit(s[i])) //数字
intpart = 10 * intpart + s[i] - '0';//计算小数的整数部分
else if (s[i] == '.') //小数点
{
hasdot = 1;
i++;
break;
}
else if (s[i] == 'e' || s[i] == 'E') //科学计数符
{
hase = 1;
i++;
break;
}
else //非法字符
return sign * intpart;
}
/*第一部分结束,有如下情况:
1. 扫描数字知道非法字符或字符串结尾,2d 234
2. 数字加小数点 2.
3. 小数点 .
4. 数字加科学计数符 3e
5. 科学计数符 e 这种情况是非法表示,但最终计算的结果为0,
因此可当作正常计算,不单独列出
6. 非法字符, 直接退出
*/
//第二部分,接着上述情况扫描
//能进入下面循环,排除遇到字符串结尾,非法字符
//因此只能遇到点号或科学计数符
for (; s[i] != '\0'; i++)
{
//第一种:.3 或 3.4,均为合法,计算小数的小数部分
if (hasdot && isdigit(s[i]))
decpart += (s[i] - '0') / pow(10, decdigit++);
//第二种:.e 或 2.e 或 .2e 或 3.3e 第一种非法,但计算结果为0
else if (hasdot && (s[i] == 'e' || s[i] == 'E'))
{
hase = 1;
i++;
break;
}
//第三种:第一部分以e结束,3e e
else if (hase)
break;
//第四种:第一部分以点号结束,现在扫描非数字,非科学计数符的其他非法字符
else
return sign * (intpart + decpart);
}
/*第三部分
从第二部分退出后继续后面的程序,有如下情况:
以科学计算符 e 结束第二部分,前面有小数点或者没有
小数部分计算完,下面讨论指数部分
*/
//判断指数部分符号
sign_e = (s[i] == '-') ? -1 : 1;
if (s[i] == '+' || s[i] == '-')
i++;
for(; s[i] != '\0'; i++)
{
if(isdigit(s[i]))
exp = exp * 10 + s[i] - '0';
else
break;
}
ret = sign * ((intpart + decpart) * pow(10, sign_e * exp));
return ret;
}
3. Given the basic framework, it’s straightforward to extend the calculator. Add the modulus (%) operator and provisions for negative numbers.
分析:与书中的例子相比,加上一个求余计算符,识别负数。区别负号和减号的方法是负号后直接跟数字或小数点,减号与操作数之间有空格。
参考:
Reverse Polish Calculator (逆波兰计算器)方案的分析——如何解决问题,从需要到实现
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<math.h>
#include<ctype.h>
#define MAXOP 100
#define NUMBER '0'
int getop(char *s);
void push(double);
double pop(void);
int getch(void);
void ungetch(int c);
int main(void)
{
int type;
double op2;
char s[MAXOP];
while ((type = getop(s)) != EOF)
{
switch (type)
{
case NUMBER:
push(atof(s));
break;
case '+':
push(pop() + pop());
break;
case '*':
push(pop() * pop());
break;
case '-':
op2 = pop();
push(pop() - op2);
break;
case '/':
op2 = pop();
if (op2 != 0.0)
push(pop() / op2);
else
printf("error: zero divisor\n");
break;
case '%':
op2 = pop();
if (op2 != 0.0)
push(fmod(pop(), op2));
else
printf("error: zero divisor\n");
break;
case '\n':
printf("\t%.8g\n",pop());
break;
default:
printf("error: unknow command %s\n",s);
break;
}
}
return 0;
}
//push() pop()
#define MAXVAL 100
int sp = 0;
double val[MAXVAL];
void push(double f)
{
if (sp < MAXVAL)
val[sp++] = f;
else
printf("error: stack full, can't push %g\n",f);
}
double pop(void)
{
if (sp > 0)
return val[--sp];
else
printf("error: stack empty\n");
return 0.0;
}
// getch() ungetch()
#define BUFSIZE 100
char buf[BUFSIZE];
int bufp = 0;
int getch(void)
{
return (bufp > 0) ? buf[--bufp] : getchar();
}
void ungetch(int c)
{
if (bufp >= BUFSIZE)
printf("ungetch: too many characters\n");
else
buf[bufp++] = c;
}
//getop()
int getop(char *s)
{
int i, c;
while ((s[0] = c = getch()) == ' ' || c == '\t')
;
s[1] = '\0';
if (!isdigit(c) && c != '.' && c != '-')
return c;
i = 0;
if (c == '-')
if (isdigit(c = getch()) || c == '.')
s[++i] = c;
else
{
if (c != EOF)
ungetch(c);
return '-';
}
if (isdigit(c))
while (isdigit(s[++i] = c = getch()))
;
if (c == '.')
while (isdigit(s[++i] = c = getch()))
;
s[i] = '\0';
if (c != EOF)
ungetch(c);
return NUMBER;
}
结果:
3 3 +
6
s
error: unknow command s
error: stack empty //输入非法字符 s 后还有换行符,因此出现该行提示
0
4. Add the commands to print the top elements of the stack without popping, to duplicate it, and to swap the top two elements. Add a command to clear the stack.
5. Add access to library functions like sin, exp, and pow.
6. Add commands for handling variables. (It’s easy to provide twenty-six variables with single-letter names.) Add a variable for the most recently printed value.
三题功能放一个程序中:
加入几个功能仿真计算器:
- 输出栈顶的值但不pop,用 ? 表示
- 复制栈顶元素, 用 d 表示
- 交换栈顶的两个元素, 用 s 表示
- 清空栈内元素,用 c
- 添加数学函数 sin(), cos(), tan(), exp(), pow()
#include<stdio.h>
#include<string.h>
#include<ctype.h>
#include<math.h>
#include<stdlib.h>
#define MAXOP 100
#define NUMBER '0'
#define NAME 'N'
int getop(char *s);
void push(double f);
double pop(void);
int getch(void);
void ungetch(int c);
void mathfunc(char *s);
double top(void);
void duplicate(void);
void swap(void);
void clear(void);
int main(void)
{
int type;
double op2;
char s[MAXOP];
while ((type = getop(s)) != EOF) {
switch (type) {
case NUMBER:
push(atof(s));
break;
case '+':
push(pop() + pop());
break;
case '*':
push(pop() * pop());
break;
case '-':
op2 = pop();
push(pop() - op2);
break;
case '/':
op2 = pop();
if (op2 != 0.0)
push(pop() / op2);
else
printf("error: zero divisor\n");
break;
case '%':
op2 = pop();
if (op2 != 0.0)
push(fmod(pop(), op2));
else
printf("error: zero divisor\n");
break;
case '?':
printf("\t%.8g\n",top());
break;
case 'd':
duplicate();
break;
case 'c':
clear();
break;
case 's':
swap();
break;
case NAME:
mathfunc(s);
break;
case '\n':
printf("\t%.8g\n", pop());
break;
default:
printf("error: unknown command %s\n", s);
break;
}
}
return 0;
}
//pop() push()
#define MAXVAL 100
int sp = 0;
double val[MAXVAL];
void push(double f)
{
if (sp < MAXVAL)
val[sp++] = f;
else
printf("error: stack full, can't push %g\n",f);
}
double pop(void)
{
if (sp > 0)
return val[--sp];
else
printf("error: stack empty\n");
return 0.0;
}
//top() duplicate() swap() clear()
double top(void)
{
if (sp > 0)
return val[sp-1];
else
printf("error: stack empty\n");
return 0.0;
}
void duplicate(void)
{
push(top());
}
void swap(void)
{
double elem1 = pop();
double elem2 = pop();
push(elem1);
push(elem2);
}
void clear(void)
{
sp = 0;
}
//getch() ungetch()
#define BUFSIZE 100
char buf[BUFSIZE];
int bufp = 0;
int getch(void)
{
return (bufp > 0) ? buf[--bufp] : getchar();
}
void ungetch(int c)
{
if (bufp >= BUFSIZE)
printf("ungetch: too many characters\n");
else
buf[bufp++] = c;
}
//void mathfunc()
void mathfunc(char *s)
{
double op2;
if (strcmp(s, "sin") == 0)
push(sin(pop()));
else if (strcmp(s, "cos") == 0)
push(cos(pop()));
else if (strcmp(s, "tan") == 0)
push(tan(pop()));
else if (strcmp(s, "exp") == 0)
push(exp(pop()));
else if (strcmp(s, "pow") == 0) {
op2 = pop();
push(pow(pop(), op2));
}
else
printf("error: unknow command %s\n", s);
}
//getop()
int getop(char *s)
{
int i, c, c1;
while ((s[0] = c = getch()) == ' ' || c == '\t')
;
s[1] = '\0';
i = 0;
if (islower(c)) {
while (islower(s[++i] = c1 = getch()))
;
s[i] = '\0';
if (c1 != EOF)
ungetch(c1);
if (strlen(s) > 1)
return NAME;
return c;
}
if (!isdigit(c) && c != '.' && c != '-')
return c;
if (c == '-')
if (isdigit(c = getch()) || c == '.')
s[++i] = c;
else {
if (c != EOF)
ungetch(c);
return '-';
}
if (isdigit(c))
while (isdigit(s[++i] = c = getch()))
;
if (c == '.')
while (isdigit(s[++i] = c = getch()))
;
s[i] = '\0';
if (c != EOF)
ungetch(c);
return NUMBER;
}
结果:
1 4 5 + tan t
error: unknown command t
-0.45231566
2 2 1 + s pow
9
7. Write a routine ungets(s) that will push back an entire string onto the input. Should ungets know about buf and bufp, or should it just use ungetch?
分析:
ungets() 只需调用 ungetch() 函数就行了,而是否超出缓冲区大小由 ungetch() 函数判断。
#include<stdio.h>
#include<string.h>
#define LEN 100
#define BUFSIZE 100
char buf[BUFSIZE];
int bufp = 0;
char *s_gets(char *s, int n);
int getch(void);
void ungetch(int c);
void ungets(char *s);
int main(void)
{
char s[LEN];
int c;
printf("Please enter a string:\n");
s_gets(s, LEN);
printf("Push back the string:\n");
ungets(s);
while (( c = getch()) != EOF)
putchar(c);
return 0;
}
char *s_gets(char *s, int n)
{
char *ret, *find;
ret = fgets(s, n, stdin);
if (ret) {
find = strchr (s, '\n');
if (find)
*find = '\0';
else
while (getchar() != '\0')
continue;
}
return ret;
}
//getch() ungetch()
int getch(void)
{
return (bufp > 0) ? buf[--bufp] : getchar();
}
void ungetch(int c)
{
if (bufp >= BUFSIZE)
printf("ungetch: too many characters\n");
else
buf[bufp++] = c;
}
void ungets(char *s)
{
int len = strlen(s);
int i = len - 1;
while (i >= 0)
ungetch(s[i--]);
}
8. Suppose that there will never be more than one character of pushback. Modify getch and ungetch accordingly.
参考:
c 程序设计语言笔记
重温
题目意思: BUFSIZE 1,这样只能存一个字符。因此不用声明数组,只有一个元素。
int buf = EOF; //表示 buf 为空
int getch(void)
{
int temp;
if (buf != EOF) {
temp = buf;
buf = EOF;
}
else
temp = getchar();
return temp;
}
void ungetch(int c)
{
if (buf != EOF)
printf("too many characters\n");
else
buf = c;
}
?9. Our getch and ungetch do not handle a pushed-back EOF correctly. Decide what their properties ought to be if an EOF is pushed back, then implement your design.
这个问题暂时不懂,为什么 getop() 中用
if (c != EOF)
ungetch();
将 if (c != EOF) 去掉后:
用gdb调试程序,在 getop 函数设置断点单步运行,输入数字3再按[ctrl] 和d键,程序并没有等按换行键再跳到下一步,直接下一步,未存储 EOF,因此后面本应该扫描到EOF时又没有值,即EOF不在输入缓冲区内,提示输入新的值。
这时输入3和EOF时,**按键输入EOF后没反应,重新按一次EOF后程序进行下一步,**此时成功识别EOF,且将其ungetch 放回buf内,查看buf[0] 的值为 -1,此时getop 返回 NUMBER,将数字字符转换为数字,接着继续循环到getop,此时 c 为 EOF,值为 -1,程序将其判断为不属于合法字符的条件中,因此返回 -1,回到主函数,type 为 -1,跳出循环,程序结束。
目前没明白,先记录,以后补充。
10. An alternate organization uses getline to read an entire input line; this makes getch and ungetch unnecessary. Revise the calculator to use this approach.
未用头文件 stdio.h 中的getline() 函数,自己写的版本
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<ctype.h>
#include<math.h>
#define MAXOP 100
#define NUMBER '0'
#define NAME 'N'
#define BUFSIZE 100
//mygetline() 参数
int bufp = 0;//一次扫描存入字符串个数
int getp = 0;//取字符的位置
double buf[BUFSIZE];
//getline() 参数结束
int getop(char *s);
void push(double f);
double pop(void);
int mygetline(void);
void mathfunc(char *s);
double top(void);
void duplicate(void);
void swap(void);
void clear(void);
int main(void)
{
int type;
double op2;
char s[MAXOP];
while ((type = getop(s)) != EOF) {
switch (type) {
case NUMBER:
push(atof(s));
break;
case '+':
push(pop() + pop());
break;
case '*':
push(pop() * pop());
break;
case '-':
op2 = pop();
push(pop() - op2);
break;
case '/':
op2 = pop();
if (op2 != 0.0)
push(pop() / op2);
else
printf("Error: zero divisor\n");
break;
case '%':
op2 = pop();
if (op2 != 0.0)
push(fmod(pop(), op2));
else
printf("Error: zero divisor\n");
break;
case '?':
printf("\t%.8g\n",top());
break;
case 'd':
duplicate();
break;
case 'c':
clear();
break;
case 's':
swap();
break;
case NAME:
mathfunc(s);
break;
case '\n':
printf("\t%.8g\n",pop());
break;
default:
printf("Error: unknown command %s\n",s);
break;
}
}
return 0;
}
// pop() push()
#define MAXVAL 100
int sp = 0;
double val[MAXVAL];
void push(double f)
{
if (sp < MAXVAL)
val[sp++] = f;
else
printf("Error: stack full, can't push %g\n",f);
}
double pop(void)
{
if (sp > 0)
return val[--sp];
else
printf("Error: stack empty\n");
return 0.0;
}
//top() duplicate() clear() swap()
double top(void)
{
if (sp > 0)
return val[sp-1];
else
printf("Error: stack empty\n");
return 0.0;
}
void duplicate(void)
{
push(pop());
}
void swap(void)
{
double elem1 = pop();
double elem2 = pop();
push(elem1);
push(elem2);
}
void clear(void)
{
sp = 0;
}
//mygetline() 头文件 stdio.h 中自带 getline() 函数,
//但该函数返回读取字符串个数,这里区分,自己写
int mygetline(void)
{
int c;
//buffer 内还有剩余字符且没有取完
if (bufp > 0 && getp < bufp)
return buf[getp++];
//buffer 内为空 或者 buffer 内的字符全部取完
else if ((bufp == 0) || (getp == bufp)) {
bufp = 0;
getp = 0;
while (bufp < BUFSIZE && (c = getchar()) != EOF && c != '\n')
buf[bufp++] = c;
if (bufp != BUFSIZE)
buf[bufp++] = c;
//清除之前已经取过的多余字符
for (int i = bufp; i < BUFSIZE && buf[i] != '\0'; i++)
buf[i] = '\0';
return buf[getp++];
}
}
//mathfunc()
void mathfunc(char *s)
{
double op2;
if (strcmp(s, "sin") == 0)
push(sin(pop()));
else if (strcmp(s, "cos") == 0)
push(cos(pop()));
else if (strcmp(s, "tan") == 0)
push(tan(pop()));
else if (strcmp(s, "exp") == 0)
push(exp(pop()));
else if (strcmp(s, "pow") == 0) {
op2 = pop();
push(pow(pop(), op2));
}
else
printf("Error: unknow command %s\n",s);
}
//getop()
int getop(char *s)
{
int i, c, c1;
while ((s[0] = c = mygetline()) == ' ' || c == '\t')
;
s[1] = '\0';
i = 0;
if (islower(c)) {
while (islower(s[++i] = c1 = mygetline()))
;
s[i] = '\0';
getp--;
if (strlen(s) > 1)
return NAME;
return c;
}
if (!isdigit(c) && c != '.' && c != '-')
return c;
if (c == '-')
if (isdigit(c = mygetline()) || c == '.')
s[++i] = c;
else {
getp--;
return '-';
}
if (isdigit(c))
while (isdigit(s[++i] = c = mygetline()))
;
if (c == '.')
while (isdigit(s[++i] = c = mygetline()))
;
s[i] = '\0';
getp--;
//排除情况 .t 或 -. ,这种返回的只有点号或者负号加点号
//如果按照正常返回 NUMBER 则 atof 转化为0
//现将这两种情况判为异常,返回 #
if (strlen(s) < 3 && !isdigit(s[0]) && !isdigit(s[1]))
return '#';
return NUMBER;
}
输出:
2 3 +
5
3 2 + -.t3 2 ++
Error: unknown command -.
Error: unknown command t
10
11. Modify getop so that it doesn’t need to use ungetch. Hint: use an internal static variable.
将 bufp, char buf[BUFSIZR] 定义为静态变量,声明在 getop() 函数前,则getop() 函数中可直接使用这些变量。
将 ungetch© 直接换成:
if (bufp < BUFSIZE)
buf[bufp++] = c;
else
printf("too many characters\n");
12. Adapt the ideas of printd to write a recursive version of itoa; that is, convert an integer into a string by calling a recursive routine.
#include<string.h>
#include<stdlib.h>
#include<limits.h>
#define LEN 34
void itoa(int n, char *s, int b);
int input(int min, int max);
char digits[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
int main(void)
{
int n;
int base;
char s[LEN];
printf("Please enter an integer (%d ~ %d): ",INT_MIN, INT_MAX);
n = input(INT_MIN, INT_MAX);
printf("Please enter the base (2 ~ 36): ");
base = input(2, 36);
itoa(n, s, base);
printf("%d base %d is %s\n",n, base, s);
return 0;
}
int input(int min, int max)
{
int status;
long n; //识别出超出int范围的数
int ok = 1;
while(ok) {
status = scanf("%ld",&n);
if (status != 1 || n < min || n > max || getchar() != '\n') {
while (getchar() != '\n')
continue;
printf("Enter again (%d ~ %d): ",min, max);
continue;
}
else
return (int)n;
}
}
void itoa(int n, char *s, int b)
{
static int i = 0;
if (n / b)
itoa(n/b, s, b);
else {
if (n < 0)
s[i++] = '-';
}
s[i++] = digits[(unsigned)(abs(n)) % b];
s[i] = '\0';
}
为了识别出超出输入超出int范围的数,在input() 函数中:
long n;
return (int)n;
通过对负数进行转换:
s[i++] = digits[(unsigned)(abs(n)) % b];
保证输入是最小负数时转化正确。
输出:
Please enter an integer (-2147483648 ~ 2147483647): -2147483649
Enter again (-2147483648 ~ 2147483647): -2147483648
Please enter the base (2 ~ 36): 1
Enter again (2 ~ 36): 10
-2147483648 base 10 is -2147483648
13. Write a recursive version of the function reverse(s), which reverses the string s in place.
#include<stdio.h>
#include<string.h>
#define LEN 100
char *s_gets(char *s, int n);
void reverse(char *s, int n);
int main(void)
{
char s[LEN];
int n;
printf("Please enter a string:\n");
s_gets(s, LEN);
n = strlen(s);
reverse(s, n);
printf("Reversed string:\n%s\n",s);
return 0;
}
char *s_gets(char *s, int n)
{
char *ret, *find;
ret = fgets(s, n, stdin);
if (ret) {
find = strchr(s, '\n');
if (find)
*find = '\0';
else
while (getchar() != '\n')
continue;
}
return ret;
}
void swap(char *s1, char *s2)
{
char t;
t = *s1;
*s1 = *s2;
*s2 = t;
}
void reverse(char *s, int n)
{
static int i = 0;
if (i < n-1) {
swap(&s[i++], &s[n-1]);
reverse(s, n-1);
}
}
14. Define a macro swap(t,x,y) that interchanges two arguments of type t. (Block structure will help.)
#include<stdio.h>
#define swap(t, s, y) { t x; x = s; s = y; y = x;}
int main(void)
{
int i, j;
i = 1;
j = 3;
swap(int, i, j);
printf("i = %d, j = %d\n",i, j);
return 0;
}