读《C程序设计语言》2E-3 [4~6]章

本文详细记录了阅读《C程序设计语言》第二版第三部分(第4~6章)的练习过程,涵盖了函数与程序结构、指针与数组、结构等主题。作者通过编写函数、分析问题并实现解决方案,逐步解决了一系列关于C语言编程的练习,包括处理科学表示法、计算器程序的扩展、栈操作、字符串处理、库函数的使用等。文章还讨论了如何使用指针改进现有程序,并引入了结构的概念。
摘要由CSDN通过智能技术生成

2016.02.22 - 04.05

读《C程序设计语言》2E-3 [1~3]章

1 练习

02.22

函数与程序结构

练习4-1

编写函数strrindex(s, t),它返回字符串t在s中最右边出现的位置、如果s中不包含t,则返回-1。

[1] 打算
简单打算如下:
这里写图片描述

[2] 实现
简单实现[1]中打算如下:

/* p4_1.c
 * 编写函数strrindex(s, t) -
 * 它返回字符串t在s中最右边出现的位置。如果s中不包含t,则返回-1。
 */
#include <string.h> //直接用strlen获取字符串长度

int strrindex(char s[], char t[])
{
    int     i, j, k;
    size_t  slen, tlen;

    slen    = strlen(s);
    tlen    = strlen(t);
    for (i = slen - 1; i >= tlen - 1; --i) {    //O(n^2)
        k   = i;
        for (j = tlen - 1; j >= 0; --j) {
            if (s[k--] != t[j]) break;
        }
        if (j < 0) return i - (tlen - 1);
    }
    return -1;
}

#include <stdio.h>
/* 简单验证strrindex(s, t) */
int main()
{
    char s[]    = "hellohellohellohello";
    char t1[]   = "lo";
    char t2[]   = "ho";

    printf("%d %d\n", strrindex(s, t1), strrindex(s, t2));
    return 0;
}

在shell交互界面下简单测试p4_1.c程序:
这里写图片描述

练习4-2

对atof函数(P.60)进行扩充,使它可以处理形如123.45e-6的科学表示法,其中,浮点数后面可能会紧跟一个e或E以及一个指数(可能有正负号)。

根据stof函数原有的思路简单扩充atof如下:

/* p4_2.c
 * 扩展Page.60中的atof函数,使其可以处理形如123.45e-6的科学表示法,
 * 其中,浮点数后面可能会紧跟一个e或E以及一个指数(可能有正负号)
 */
#include <ctype.h>
#include <stdio.h>

double atof(char s[])
{
    double  val, power;
    int     i, ep, es, sign;

    for (i = 0; isspace(s[i]); ++i)
        NULL;

    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;
    }

    if (s[i] == 'e' || s[i] == 'E')
        ++i;
    if (s[i] == '+' || s[i] == '-') {
        ++i;
        es  = (s[i] == '+') ? 1 : -1;
    }

    for (ep = 0; isdigit(s[i]); ++i)
        ep  = 10 * ep + (s[i] - '0');
    for (i = 0; i < ep; ++i) {
        if (es > 0) power   /= 10.0;
        else        power   *= 10.0;
    }

    return sign * val / power;
}

/* 简单测试stof(s) */
int main()
{
    char s[] = "123.45e-6";
    printf("%f\n", atof(s));
    return 0;
}

在shell程序下简单的测试p4_2.c程序如下:
这里写图片描述

02.23
4-3 ~ 4-10与书中P.63 ~ P.67的程序相关。
整理P.63 ~ P.67上代码如下:
main.c

/* main.c
 * 计算器程序入口程序
 */
#include <stdio.h>
#include <stdlib.h>
#include "share.h"

#define MAXOP   100 /* 操作数的最大长度 */

int     getop(char s[]);
void    push(double op);
double  pop(void);

/* 逆波兰计算器 */
int main()
{
    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 '\n':
                printf("\t%.8g\n", pop());
                break;
            default:
                printf("error: unknown command %s\n", s);
                break;
        }
    }
    return 0;
}

getop.c

/* getop.c
 * 获取操作数或运算符
 */
#include <ctype.h>
#include <stdio.h>
#include "share.h"

int     getch(void);
void    ungetch(int);

/* getop函数:获取下一个运算符或操作数 */
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;
}

statck.c

/* statck.c
 * 保存操作数的栈和栈操作函数
 * 本文件中的全局变量对于其它文件来说是隐藏的
 */
#include <stdio.h>

#define MAXVAL  100 /* 栈val的最大深度 */

static int sp  = 0;        /* 栈中下一个空闲位置 */
static double val[MAXVAL]; /* 栈空间 */

/* push函数:把f压入到栈中 */
void push(double f)
{
    if (sp < MAXVAL)
        val[sp++]   = f;
    else
        printf("error: stack full, can't push %g\n", f);
}

/* pop函数:弹出并返回栈顶的值 */
double pop(void)
{
    if (sp > 0)
        return val[--sp];
    else {
        printf("error: stack empty\n");
        return 0.0;
    }
}

un_getch.c

/* un_getch.c
 * 用缓冲区保存运算符
 * 本文件中的全局变量对于其它文件来说是隐藏的
 */
#include <stdio.h>

#define BUFSIZE 100

static char    buf[BUFSIZE];   /* 用于ungetch函数的缓冲区 */
static int     bufp = 0;       /* buf中下一个空闲位置 */

/* getch函数:取一个字符(可能是压回到缓冲区的运算符) */
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;
}

share.h

/* share.h
 * 两个文件共享的声明文件
 */
#define NUMBER  '0' /* 用于标识是一个数字 */

在shell下简单测试以上程序:
这里写图片描述

  • 栈以及保存运算符及其相关的内存通过全局数组变量(多个函数共用。注 - 少/慎用全局变量)来实现,且这些变量的作用域都只在定义它的文件中(static),其它文件不得擅自访问(在其它文件中被隐藏了起来)。
  • ungetch()函数的作用其实是用来保存已读入的运算符(+、-、*、/)。

后面的几个程序写得很差,无稳定性之言。

练习4-3

在有了基本框架后,对计算器程序进行扩充就比较简单了。在该程序中加入取模(%)运算符,并注意考虑负数的情况。

[1] 打算
简单打算如下:
这里写图片描述
在冷静时思考;用笔画画有助于分析。

[2] 实现打算
简单实现打算。

扩充取模运算。
main.c

...
int main()
{
    ...
    while ((type = getop(s)) != EOF) {
        switch (type) {
            ...
            case '%':
                op2 = pop();
                if (op2 != 0.0)
                    push((double)((int)pop() % (int)op2));
                else
                    printf("error: zero mod\n");
                break;
            case '\n':
                printf("\t%.8g\n", pop());
                break;
            default:
                printf("error: unknown command %s\n", s);
                break;
        }
    }
    return 0;
}

在shell程序下简单的测试取模运算扩充:
这里写图片描述
由于取模运算只能用于整型,程序中直接将浮点数强制转换为了整型。

02.24
考虑负数。
getop.c

/* getop.c
 * 获取操作数或运算符
 */
#include <ctype.h>
#include <stdio.h>
#include "share.h"

int     getch(void);
void    ungetch(int);

/* getop函数:获取下一个运算符或操作数 */
int getop(char s[])
{
    int i, c, cn;

    while ((s[0] = c = getch()) == ' ' || c == '\t')
        ;
    s[1]    = '\0';
    if (!isdigit(c) && c != '.' && c != '+' && c != '-')    //非法表达式
        return c;

    if ((c == '+' || c == '-') && !isdigit(cn = getch())) { //+或-运算符
        ungetch(cn);
        return c;
    }
    i   = 0;
    if (isdigit(c)) {   //不带+、-符号的数
        while (isdigit(s[++i] = c = getch()))
            ;
    } else {    //带正负符号的数
        s[++i]  = cn;
        while (isdigit(s[++i] = c = getch()))
            ;
    }

    if (c == '.')
        while (isdigit(s[++i] = c = getch()))
            ;
    s[i]    = '\0';
    if (c != EOF)
        ungetch(c);
    return NUMBER;
}

在shell程序中测试getop.c程序:
这里写图片描述

练习4-4

在栈操作中添加几个命令,分别用于在不弹出元素的情况下打印栈顶元素;复制栈顶元素;交换栈顶两个元素的值。另外增加一个命令用于清空栈。

[1] 分析
简单分析如下:
这里写图片描述

[2] 实现
简单实现分析如下:
stack.c

/* statck.c
 * 保存操作数的栈和栈操作函数
 * 本文件中的全局变量对于其它文件来说是隐藏的
 */
#include <stdio.h>

#define MAXVAL  100 /* 栈val的最大深度 */

static int sp   = 0;        /* 栈中下一个空闲位置 */
static double val[MAXVAL];  /* 栈空间 */

...

/* get_top_val函数:不弹出栈顶元素值,获取val栈顶的值 */
double get_top_val(void)
{
    if (sp > 0)
        return val[sp - 1];
    else {
        printf("error: stack empty\n");
        return -1;
    }
}

/* cp_top_val函数:复制栈顶元素到d中
 * 返回0复制成功,-1则失败 */
int cp_top_val(double *d)
{
    if (!d || sp < 1)
        return -1;
    *d  = get_top_val();
    return 0;
}

/* swap_top_val函数:交换两个栈顶元素 */
int swap_top_val(void)
{
    double tv1, tv2;
    if (sp >= 2) {
        tv1 = pop();
        tv2 = pop();
        push(tv1);
        push(tv2);
        return 0;
    }
    return -1;
}

/* clr_stack函数:清空栈 */
void cr_stack(void)
{
    sp  = 0;
}

练习4-5

给计算器程序增加访问sin、exp与pow等库函数的操作。有关这些库函数的详细信息,参见附录B.4节中的头文件< math.h>。
[1] 分析
简单分析如下:
这里写图片描述

[2] 实现
简单实现分析如下:
main.c

/* main.c
 * 计算器程序入口程序
 */
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include "share.h"

#define MAXOP   100 /* 操作数的最大长度 */
#define MAXCMMD 10  /* 命令的最大长度 */

....
void    get_rp_cal_cmmd(void);
void    match_cmmd(char cmmd[]);

/* 逆波兰计算器 */
int main()
{
    get_rp_cal_cmmd();
    return 0;
}

/* get_cp_cal_cmmd函数:解析输入的命令 */
void get_rp_cal_cmmd(void)
{
    int     i;
    int     type;
    char    op[MAXOP];
    char    cmmd[MAXCMMD];
    double  op2;

    i   = 0;
    while ((type = getop(op)) != EOF) {
        switch (type) {
            ....
            case '\n':
                if (i == 0)
                    printf("\t%.8g\n", pop());
                else {
                    cmmd[i] = '\0';
                    i   = 0;
                    match_cmmd(cmmd);
                }
                break;
            default:
                if (i < MAXCMMD)
                    cmmd[i++]   = type;
                else {
                    cmmd[i] = '\0';
                    i   = 0;
                    printf("error: command %s...too long\n", cmmd);
                }
                break;
        }
    }
}

/* match_cmmd函数:匹配输入命令是否为库函数名 */
void match_cmmd(char cmmd[])
{
    double op2;
    if (!strcmp(cmmd, "sin")) {
        printf("\t%.8g\n", sin(pop()));
    } else if (!strcmp(cmmd, "exp")) {
        printf("\t%.8g\n", exp(pop()));
    } else if (!strcmp(cmmd, "pow")) {
        op2 = pop();
        printf("\t%.8g\n", pow(pop(), op2));
    }/*else if...*/else {
        printf("error: command not known\n");
    }
}

在shell程序中简单的测试该程序:
这里写图片描述

02.25

练习4-6

给计算器程序增加处理变量的命令(提供26个具有单个英文字母变量名的变量很容易)。增加一个变量存放最近打印的值。

[1] 分析
简单分析/打算如下:
这里写图片描述

[2] 打算
简单实现分析/打算如下:
main.c

/* main.c
 * 计算器程序入口程序
 */
...

int     is_stack_empty(void);

/* 逆波兰计算器 */
int main()
{
    get_rp_cal_cmmd();
    return 0;
}

/* get_cp_cal_cmmd函数:解析输入的命令 */
void get_rp_cal_cmmd(void)
{
    int     i, sv;      /* sv用来保存'='前的字符 */
    int     type;
    ...
    double  v, op2;     /* lv用来保存最近所打印的值 */
    double  var[26];    /* 用'A' ~ 'Z'单个英文字母作为计算器的内部变量 */

    v   = 0.0;
    for (i = 0; i < 26; ++i)
        var[i]  = 0.0;
    i   = 0;
    while ((type = getop(op)) != EOF) {
        switch (type) {
        ...
        case '=':
            if (isupper(sv)) {
                pop();  /* 弹出变量对应的值 */
                var[sv - 'A']   = pop();    /* 给变量对应的内存赋值 */
            } else
                printf("error: no varible\n");
            break;
        case '\n':
            if (i == 0) {
                if (!is_stack_empty()) {
                    v   = pop();
                    printf("\t%.8g\n", v);
                }
            } else {
                cmmd[i] = '\0';
                i   = 0;
                match_cmmd(cmmd);
            }
            break
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值