4-1:
编写函数strindex(s, t),它返回字符串t在s中最右边出现的位置。如果s中不包含t,则返回-1
#include <stdio.h>
#include <string.h>
#define MAXSIZE 1000
int getline(char s[], int lim);
int strindex(char s[], char t[]);
int main(void)
{
char line[MAXSIZE];
int found;
char Pattern[MAXSIZE] = "ould";
printf("请输入一条字符串\n");
while(getline(line, MAXSIZE) > 0)
{
if((found = strindex(line, Pattern)) >= 0)
printf("%s出现在字符串的%d位\n", Pattern, found);
else
printf("字符串中不包含%s\n", Pattern);
printf("请继续输入\n");
}
return 0;
}
int getline(char s[], int lim)
{
int c, i;
i = 0;
while(--lim > 0 && (c = getchar()) != EOF && c != '\n')
{
s[i++] = c;
}
if(c == '\n')
s[i++] = c;
s[i] = '\0';
return i;
}
int strindex(char s[], char t[])
{
int length_s = strlen(s) - 1;
int length_t = strlen(t) - 1;
int k;
for(int i = length_s; i >= 0; i--)
{
for(int j = i, k = length_t; k >= 0 && s[j] == t[k]; j--, k--)
;
if(k < 0)
return (i - length_t + 1);//-length_t返回的是待查找字符串首字符出现的位置,+1表示返回的是实际位置,而不是数组下标
}
return -1;
}
4-2:
对atof函数进行扩充,使它可以处理形如123.45e-6的科学表示法,其中,浮点数后面可能会紧跟一个e或E以及一个指数(可能有正负号)
#include <stdio.h>
#include <ctype.h>//包含isspace()库函数和isdigit()库函数,前者判断空格,后者判断数字
#define MAXSIZE 40
int getline(char s[], int lim);
double atof(char s[]);
int main(void)
{
char line[MAXSIZE];
double result;
printf("请输入待转换的字符串\n");
while(getline(line, MAXSIZE) > 0)
{
result = atof(line);
printf("字符串中的数字为:%lf", result);
printf("继续输入\n");
}
return 0;
}
int getline(char s[], int lim)
{
int c, i;
i = 0;
while(--lim > 0 && (c = getchar()) != EOF && c != '\n')
{
s[i++] = c;
}
if(c == '\n')
s[i++] = c;
s[i] = '\0';
return i;
}
double atof(char s[])
{
double val;
double power = 1.0;
double sign;
int mask; //标记e后面的符号
int power_e; //记录指数大小
double result; //结果值
for(int i = 0; isspace(s[i]); i++)
;
sign = (s[i] == '-')? -1 : 1;
if(s[i] == '+' || s[i] == '-')
i++;
for(val = 0; isdigit(s[i]); i++)
val = 10.0 * val + (s[i] - '0');
if(s[i] == '.')
{
i++;
for(; isdigit(s[i]); i++)
{
val = 10.0 * val + (s[i] - '0');
power *= 10.0;
}
}
result = sign * val / power;
if(s[i] == 'e' || s[i] == 'E')
{
i++;
if(s[i] == '-')
mask = 1;
else if(s[i] == '+' || isdigit(s[i]))
mask = 0;
else
return result;
if(s[i] == '-' || s[i] == '+')
i++;
for(power_e = 0; isdigit(s[i]); i++)
power_e = 10 * power_e + (s[i] - '0');
if(mask)//e后面符号为-时,表示除以power_e个10.0
{
while(power_e > 0)
{
result /= 10.0;
power_e--;
}
return result;
}
else//e后面符号为+或没有符号时,表示乘以power_e个10.0
{
while(power_e > 0)
{
result *= 10.0;
power_e--;
}
return result;
}
}
else//数字后面没有e或E
return result;
}
4-3、4-4、4-5、4-6:
请对现在的计算器程序进行扩充:
1.在该程序中加入取模(%)运算符,并注意考虑负数的情况
2.添加以下命令:在不弹出元素的情况下打印栈顶元素,复制栈顶元素,交换栈顶两个元素的值,清空栈
3.增加访问sin、exp与pow库函数的操作,这些库函数包含在<math.h>头文件中
4.增加处理变量的命令(提供26个具有单个英文字母变量名的变量)。增加一个变量存放最近打印的值
/*关于练习4-6,我不太明白变量需要实现什么功能,是像C语言本身的变量一样可以
赋值,可以使用?还是其他?我假定变量的功能和C语言本身的变量类似,例如在输
入行中输入 2 4 + A A A +,2 + 4 的结果赋值给第一个A,赋值结束,变量可以正
常使用,而A A +是表示需要使用变量完成操作,打印结果为A + A的值,即12*/
#include <stdio.h>
#include <math.h>
#include <ctype.h>
#include <stdlib.h>
#define RAD_TO_DEG (180 / (4 * atan(1)))//将弧度转换为角度的公式,即1弧度 = 180 / π 度,其中π = 4 * atan(1)
#define MAXOP 100
#define NUMBER '0'
#define MAXVAL 100
#define BUFSIZE 100
#define MAXVAR 26
int sp = 0;
double val[MAXVAL];
char buf[BUFSIZE];
int bufp = 0;
int getop(char []);
void push(double);
double pop(void);
int getch(void);
void ungetch(int);
void printf_stack_top(void);
void copy_stack_top(void);
void change_stack_top(void);
void empty_stack(void);
int main(void)
{
int type, i;
double op2;
double prev; //记录最近打印的值
char s[MAXOP];
double var[MAXVAR];//包含26个单个英文字母的变量数组
int print = 0; //记录最近是否有打印的值
int mask = 0; //标记变量是否已赋值
for(i = 0; i < MAXVAR; i++)//对每个变量的值进行初始化
var[i] = 0.0;
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((int)pop() % (int)op2);//由于取模运算符只能执行整型数据之间的取模,因此需要将两个double类型数据强制转换为int类型
//如果需要处理浮点型数据,可以使用<math.h>头文件中的库函数fmod(),将上面的语句更改为push( fmod(pop(), op2) );
//如果不想使用库函数,但是需要处理浮点型数据,那么可以先将浮点数乘以n个10转换为整数,取模运算后将结果除以n个10还原,但是很麻烦
else
printf("error: zero divisor\n");
break;
case 'd'://打印栈顶元素
printf_stack_top();
break;
case 'f'://复制栈顶元素
copy_stack_top();
break;
case 'j'://交换栈顶两个元素的值
change_stack_top();
break;
case 'q'://清空栈
empty_stack();
break;
case 's'://sin函数,注意sin、cos、tan接受的参数为弧度,不是角度
push( sin( (pop() / RAD_TO_DEG) ) );//将输入的角度转化为弧度
break;
case 'c'://cos函数
push( cos( (pop() / RAD_TO_DEG) ) );
break;
case 't'://tan函数
op2 = pop();
if(op2 != 90)
push( tan( (pop() / RAD_TO_DEG) ) );
else
printf("tan90 is invalid value\n");
break;
case 'e'://exp函数,返回e的指数幂
push( exp( pop() ) );
break;
case 'p'://pow函数,返回参数1的参数2次幂,即假设pow(2, 3),返回2^3的值
op2 = pop();
push( pow( pop(), op2 ) );
break;
case 'v':
if(print)
printf("The most recently printed value is %.8g\n", prev);
else
printf("No recently printed values!\n");
break;
case '\n':
prev = pop();
print = 1;
printf("\t%.8g\n", prev);
break;
default:
if(type >= 'A' && type <= 'Z')
{
if(mask == 0)
{
var[type - 'A'] = pop();
mask = 1;
}
else
push(var[type - 'A']);
}
//上面的语句有一个很大的缺陷,mask只能标记有没有为变量赋值,但是无法标记是为哪一个变量赋的值
//如果需要改进,就要使用结构,但是这还没有开始学,我尽量按照书本上的进度来
//如果想改进,就创建一个26个元素的结构数组,结构包含一个mask成员和一个value成员
else
printf("error: unkown command %s\n", s);
break;
}
}
return 0;
}
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;
}
}
int getop(char s[])
{
int i, c;
while((s[0] = c = getch()) == ' ' || c == '\t')
;
s[1] = '\0';
if(!isdigit(c) && c != '.')
return c;
i = 0;
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;
}
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 printf_stack_top(void)
{
if(sp > 0)
printf("top of stack: %8g\n", val[sp - 1]);
else
printf("stack is empty\n");
}
void copy_stack_top(void)
{
double temp = pop();//先弹出栈顶元素,将其拷贝给临时变量,将该变量压入两次,完成复制工作
push(temp);
push(temp);
printf("Done!\n");
}
void change_stack_top(void)
{
double temp1 = pop();//temp1是栈顶第一个元素
double temp2 = pop();//temp2是栈顶第二个元素
push(temp1);//temp1先压入,temp2后压入,temp1变成栈顶第二个元素,temp2变成栈顶第一个元素
push(temp2);
}
void empty_stack(void)
{
sp = 0;
}
4-7:
编写一个函数ungets(s),将整个字符串s压回到输入中。ungets函数需要使用buf和bufp吗?它能否仅适用ungetch函数?
#include <stdio.h>
#include <string.h>
#define MAXSIZE 100
#define BUFSIZE 100
char buf[BUFSIZE];
int bufp = 0;
void ungets(char s[]);
int getch(void);
void ungetch(int);
int main(void)
{
char string[MAXSIZE] = "It's a test statement";
int c;
ungets(string);
while((c = getch()) != EOF)
putchar(c);
return 0;
}
void ungets(char s[])
{
int length = strlen(s);
while(length > 0)
{
ungetch(s[--length]);
}
}
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;
}
4-8:
假定最多只压回一个字符,请相应的修改getch和ungetch这两个函数
#include <stdio.h>
int bufp = EOF;
void ungets(char s[]);
int getch(void);
void ungetch(int);
int main(void)
{
int c;
while((c = getch()) != EOF)
{
if(c == '\n')
ungetch('*');
putchar(c);
}
return 0;
}
int getch(void)
{
int c;
c = (bufp == EOF) ? getchar() : bufp;
bufp = EOF;
return c;
}
void ungetch(int c)
{
if(bufp != EOF)
printf("ungetch: too many characters\n");
else
bufp = c;
}
4-9:
以上介绍的getch与ungetch函数不能正确的处理压回的EOF。考虑压回EOF时应该如何处理?请实现你的设计方案
将声明中的
char buf[BUFSIZE];
修改为int buf[BUFSIZE];
再将getop函数中的
if(c != EOF)
ungetch©;
去除if条件判断
4-10:
另一种方法是通过getline函数读入整个输入行,这种情况下可以不使用getch与ungetch函数。请运用这一方法修改计算器程序
/*此程序的主要部分不需要改动,仍然保留,主要修改getop函数和main函数,定义
一个外部字符数组,当遇到输入的数字时,将数字以字符形式存储在字符数组中返回
给main函数,交由atof转换为数字,同时我们定义一个int类型外部变量,作为输入
字符串的下标,这样从getop函数返回到main函数再次进入getop函数时,我们仍可以
从上次读取到的地方继续读取*/
#include <stdio.h>
#include <math.h>
#include <ctype.h>
#include <stdlib.h>
#define RAD_TO_DEG (180 / (4 * atan(1)))//将弧度转换为角度的公式,即1弧度 = 180 / π 度,其中π = 4 * atan(1)
#define MAXOP 100
#define NUMBER '0'
#define MAXVAL 100
#define MAXVAR 26
#define NUMBERSIZE 100
int sp = 0;
char number[NUMBERSIZE];//储存输入的数字字符
double val[MAXVAL];
int index = 0; //index为s字符数组的下标,是一个外部变量,可以跨函数使用
int getline(char s[], int lim);
int getop(char []);
void push(double);
double pop(void);
void printf_stack_top(void);
void copy_stack_top(void);
void change_stack_top(void);
void empty_stack(void);
int main(void)
{
int type, i;
double op2;
double prev; //记录最近打印的值
char s[MAXOP];
double var[MAXVAR];//包含26个单个英文字母的变量数组
int print = 0; //记录最近是否有打印的值
int mask = 0; //标记变量是否已赋值
for(i = 0; i < MAXVAR; i++)//对每个变量的值进行初始化
var[i] = 0.0;
while(getline(s, MAXOP) > 0)
{
while((type = getop(s)) != '\0')
{
switch(type)
{
case NUMBER:
push(atof(number));
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((int)pop() % (int)op2);
else
printf("error: zero divisor\n");
break;
case 'd'://打印栈顶元素
printf_stack_top();
break;
case 'f'://复制栈顶元素
copy_stack_top();
break;
case 'j'://交换栈顶两个元素的值
change_stack_top();
break;
case 'q'://清空栈
empty_stack();
break;
case 's'://sin函数,注意sin、cos、tan接受的参数为弧度,不是角度
push( sin( (pop() / RAD_TO_DEG) ) );//将输入的角度转化为弧度
break;
case 'c'://cos函数
push( cos( (pop() / RAD_TO_DEG) ) );
break;
case 't'://tan函数
op2 = pop();
if(op2 != 90)
push( tan( (pop() / RAD_TO_DEG) ) );
else
printf("tan90 is invalid value\n");
break;
case 'e'://exp函数,返回e的指数幂
push( exp( pop() ) );
break;
case 'p'://pow函数,返回参数1的参数2次幂,即假设pow(2, 3),返回2^3的值
op2 = pop();
push( pow( pop(), op2 ) );
break;
case 'v':
if(print)
printf("The most recently printed value is %.8g\n", prev);
else
printf("No recently printed values!\n");
break;
case '\n':
prev = pop();
print = 1;
printf("\t%.8g\n", prev);
break;
default:
if(type >= 'A' && type <= 'Z')
{
if(mask == 0)
{
var[type - 'A'] = pop();
mask = 1;
}
else
push(var[type - 'A']);
}
else
printf("error: unkown command %s\n", s);
break;
}
}
printf("继续输入\n");
index = 0;//将index重置为0
}
return 0;
}
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;
}
}
int getop(char s[])
{
int c;
while(s[index] != '\0')
{
if (s[index] == ' ' || s[index] == '\t')//跳过空格和制表符
index++;
else if (!isdigit(s[index]) && s[index] != '.')//当遇到非数字字符和非.字符返回该字符
{
c = s[index];
index++;//index递增,跳过该字符
return c;
}
else if (isdigit(s[index]))
{
int i = 0;
while(isdigit(s[index]))
{
number[i++] = s[index];
index++;
}
if(s[index] == '.')
{
number[i++] = s[index];
index++;
while(isdigit(s[index]))
{
number[i++] = s[index];
index++;
}
}
number[i] = '\0';//number数组此时存储一个数字的字符形式
return NUMBER;
}
}
return s[index];//返回空字符
}
void printf_stack_top(void)
{
if(sp > 0)
printf("top of stack: %8g\n", val[sp - 1]);
else
printf("stack is empty\n");
}
void copy_stack_top(void)
{
double temp = pop();//先弹出栈顶元素,将其拷贝给临时变量,将该变量压入两次,完成复制工作
push(temp);
push(temp);
printf("Done!\n");
}
void change_stack_top(void)
{
double temp1 = pop();//temp1是栈顶第一个元素
double temp2 = pop();//temp2是栈顶第二个元素
push(temp1);//temp1先压入,temp2后压入,temp1变成栈顶第二个元素,temp2变成栈顶第一个元素
push(temp2);
}
void empty_stack(void)
{
sp = 0;
}
int getline(char s[], int lim)
{
int c, i;
i = 0;
while(--lim > 0 && (c = getchar()) != EOF && c != '\n')
{
s[i++] = c;
}
if(c == '\n')
s[i++] = c;
s[i] = '\0';
return i;
}
4-11:
修改getop函数,使其不必使用ungetch函数。提示,可以使用一个static类型的内部变量解决问题
/*ungetch函数的本来目的是将输入的非数字字符压入缓冲区buf[]中,例如,如果输
入123a,getop函数首先读取所有数字字符,存储到s字符数组中,然后利用ungetch
函数将字符'a'压入缓冲区buf[]中,bufp + 1,程序控制权返回给main函数,将s数
组中的数字字符转化为数字,循环迭代,重新进入getop函数,语句c = getch()调用
getch函数,判断bufp > 0,返回上一次储存在缓冲区buf中的字符'a',于是c ==
'a',继续执行getop函数中的语句
我们可以在getop函数中定义一个静态局部变量,该变量具有静态存储期、块作用域
和无链接,即该变量只能在getop函数中被使用,但是离开该函数该变量不会消失*/
int getop(char s[])
{
int i;
static int c = ' ';//初始化变量,静态局部变量只能初始化一次,即退出再
//进入该函数时,变量不会再次初始化,其值为上一次退出该函数时赋的值
while((s[0] = c = getch()) == ' ' || c == '\t')
;
s[1] = '\0';
if(!isdigit(c) && c != '.')
return c;
i = 0;
if(isdigit(c))
while(isdigit(s[++i] = c = getch()))
;
if(c == '.')
while(isdigit(s[++i] = c = getch()))
;
s[i] = '\0';
return NUMBER;
}
4-12:
运用printd函数的设计思想编写一个递归版本的itoa函数,即通过递归调用把整数转换为字符串
#include <stdio.h>
#define MAXSIZE 1000
void itoa(int n, char s[]);
int main(void)
{
int number = 0;
char string[MAXSIZE];
printf("请输入一个数字\n");
scanf("%d", &number);
itoa(number, string);
printf("%s\n", string);
return 0;
}
void itoa(int n, char s[])
{
static int i = 0;
unsigned int u;
if(n < 0)
{
u = -n;
s[i++] = '-';
}
else
u = n;
if((u / 10) > 0)
itoa(u / 10, s);
s[i++] = u % 10 + '0';
s[i] = '\0';
}
4-13:
编写一个递归版本的reverse(s)函数,以将字符串s倒置
#include <stdio.h>
#include <string.h>
#define MAXSIZE 1000
void itoa(int n, char s[]);
void reverse(char s[]);
int main(void)
{
int number = 123456;
char string[MAXSIZE];
itoa(number, string);
printf("%s\n", string);
return 0;
}
void itoa(int n, char s[])
{
int i;
unsigned int u;
u = (n < 0) ? -n : n;
i = 0;
do{
s[i++] = (u % 10) + '0';
}while((u /= 10) > 0);
if(n < 0)
s[i++] = '-';
s[i] = '\0';
reverse(s);
}
void reverse(char s[])
{
static int i = 0;
static int j = strlen(s) - 1;
int temp;
if(i < j)
{
temp = s[i];
s[i] = s[j];
s[j] = temp;
i++;
j--;
reverse(s);
}
}
//感觉这个递归不怎么简洁.....
4-14:
定义宏swap(t, x, y)以交换t类型的两个参数。(使用程序块结构会对你有所帮助)
#include <stdio.h>
#include <string.h>
#define SWAP(t, x, y) { t temp = x;\
x = y;\
y = temp;\
}
#define MAXSIZE 1000
void itoa(int n, char s[]);
void reverse(char s[]);
int main(void)
{
int number = 123456;
char string[MAXSIZE];
itoa(number, string);
printf("%s\n", string);
return 0;
}
void itoa(int n, char s[])
{
int i;
unsigned int u;
u = (n < 0) ? -n : n;
i = 0;
do{
s[i++] = (u % 10) + '0';
}while((u /= 10) > 0);
if(n < 0)
s[i++] = '-';
s[i] = '\0';
reverse(s);
}
void reverse(char s[])
{
int temp;
for(int i = 0; s[i] != '\0'; i++)
;
i--;
for(int j = 0; i > j; j++, i--)
{
SWAP(int, s[i], s[j]);
}
}