第4章 函数和程序结构

函数要有高效性和易用性

4.1 函数的基本内容

查看strindex()的实现,该功能和strstr()类似, 都是查找子串的位置

int strindex(char s[], char t[]) {
        int idx, i, j;
        for (idx = 0; s[idx] != '\0'; idx++) {
                for (i = idx, j = 0; t[j] != '\0' && s[i] == t[j]; i++, j++);
                if (t[j] == '\0' && j > 0)
                        return idx;
        }
        return -1;
}

理解上述代码:
1. 如果s短,s[i]是否会越界?不会,因为第二层循环中,s[i]=='\0'时,循环肯定会结束
2. if判断中,j>0 什么作用?我认为当 t[] = '\0'时,起到检查作用

4_1 练习编写strrindex(s, t), 它将返回字符串t在s中最右边出现的位置,如果s中不包含t,就返回-1

int strindex(char s[], char t[]) {
    int i, k, pos = -1;
    for (i = 0; s[i] != '\0'; i++) {
        for (k = 0; t[k] != '\0' && s[i + k] == t[k]; k++); // 查找t 是否在 s 中
        if (t[k] == '\0' && k > 0)
            pos = i;
            // i += k-1; // 加上这一行,可以跳过 刚才遍历的 substr    
        }
    return pos;
}

4.2 返回非整型值的函数
练习 4_2 对atof函数进行扩充,使它可以处理形如123.45e-6的科学表示法

#include <stdio.h>
#include <ctype.h>

double atof(char *s) {
    double val, power;
    int i, sign;

    for (i = 0; isspace(s[i]); i++); //跳过空白符
    sign = ('-' == s[i]) ? -1 : 1;
    if ('-' == s[i] || '+' == s[i])
        i++;

    for (val = 0.0; isdigit(s[i]); i++) {
        val = val * 10 + s[i] - '0';
    }
    if (s[i] == '.')
        i++;

    for (power = 1.0;isdigit(s[i]); i++){
        val = val * 10 + s[i] - '0';
        power *= 10.0;
    }
    val = val * sign / power;

    if (s[i] == 'e' || s[i] == 'E') { // 处理指数部分
        sign = ('-' == s[++i]) ? 0 : 1;

        if ('-' == s[i] || '+' == s[i])
            i++;
        int exp = 0;
        for (; isdigit(s[i]); i++)
            exp = exp * 10 + s[i] - '0';

        while (exp-- > 0) {
            if (sign)
                val *= 10;
            else
                val /= 10; // 不乘0.1 是为了减少误差
        }
    }
    return val;
}

 

4.3 外部变量

应避免外部变量和局部变量的名子一样
这一节 介绍了 处理逆波兰表达式的代码,代码中使用getop()函数处理 字符串

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

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

#define BUFSIZE 100
char buf[BUFSIZE];
int bufp = 0; // next free position in buf

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

思考:1.这个getch() 中使用的buf 只用 2个字节是不是就足够了? 2. 这个getop() 使用的s[] 最大可以装下 INT_MIN的位数, 就可以?

 

4.9 初始化
对于外部变量和静态变量,初始化表达式必须是常量表达式,且只初始化一次

4.10 递归

我看书中的printd() 不能处理INT_MIN, 自己的修改如下;然后对比 练习 4-12发现了 劣处

#include <stdio.h>
#include <stdlib.h>
#include <limits.h>

void print_d(int num) {
    if (num == 0)
        return;
    if (num < 0)
        putchar('-');
    print_d(abs(num / 10));
    putchar(abs(num % 10) + '0');
}

void main() {
    int num = INT_MIN;
    print_d(num);

    while(scanf("%d", &num)>= 0) {
        print_d(num);
        putchar('\n');
    }
}

 

练习 4-12 使用printd 的思想重写 递归半版本的 itoa()函数

#include <stdio.h>
#include <stdlib.h>
#include <limits.h>

//#define abs(x) ((x) < 0 ? (-x) : (x))

void itoa(int n, char *s) {
    static int i; // 默认初值就是0

    if (n / 10) {
        itoa(n / 10, s);
    } else {    // 说明 n 已经个位数
        i = 0;
        if (n < 0)
            s[i++] = '-';
    }

    s[i++] = abs(n % 10) + '0'; // 使用宏 ABS(x) 会出错;并且 abs(n)%10 也是 -8
    s[i] = 0;
}
void main() {
    char s[20];
    int n = INT_MIN;
itoa(n, s); printf(
"n=%d, s=%s\n", n, s); while (scanf("%d", &n) >= 0) { itoa(n, s); printf("s=%s\n", s); } }

 

posted @ 2025-02-11 23:31  靖意风  Views(10)  Comments(0)    收藏  举报