函数可以把大的计算任务分解成若干个较小的任务,从而使整个程序结构更加清晰,并降低编写和修改程序的难度。
4.1 函数的基本知识 【A * 查找模式串】 4.2 返回非整型值的函数 【B * 函数: atof 字符串 ->双精度浮点数】 【简单的计算器程序】 4.3 外部变量 【逆波兰计算器】 4.10 递归 【C 打印十进制数】 【D * 快速排序】 |
4.1 函数的基本知识 【查找模式串】
任务的划分
while (还有未处理的行) if (该行包含指定的模式) 打印该行 |
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 | #include <stdio.h> #define MAXLINE 1000 #define MAXWORD 20 int getline(char line[], int max); int main() { char line[MAXLINE]; char pattern[MAXWORD]; int found = 0; scanf("%s", pattern); while (getline(line, MAXLINE) > 0) if (strstr(line, pattern)) { printf("%s", line); found++; } printf("total found: %d\n", found); } /* getline: get line into s, return length */ int getline(char line[],int max) { if (fgets(line, max, stdin) == NULL) return 0; else return strlen(line); } |
函数 strindex 在库函数中已经有实现,也就是 strstr 。
另一种实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | #include <stdio.h> #define MAXLINE 1000 #define MAXWORD 20 int main() { char line[MAXLINE]; char pattern[MAXWORD]; int found = 0; /* freopen("in.txt","r",stdin); */ scanf("%s", pattern); while (gets(line)) if (strstr(line, pattern)) { puts(line); found++; } printf("total found: %d\n", found); return 0; } |
上面这个程序只能用作学校练习来使用,并不足以胜任工业级应用,因为 gets 函数存在缓冲区溢出漏洞。
这也是教材为何要定义了函数 getline 来替换 gets 。
#include <string.h> char *strstr( const char *str1, const char *str2 ); char *gets( char *str ); |
为了避免输入,可以使用重定向,从文件 in.txt 而不是标准输入 stdin 中读取数据。
文件 in.txt 的第1行是待搜索的字符串,下面是搜索的文本。下面是 in.txt 的示例。
1 2 3 4 5 6 7 8 9 | Richie 100107001 Apple F 20 72 100107002 Darwin M 19 68 100107003 Eric M 19 79 100107004 Peter M 21 46 100107006 Richie M 70 100 100107007 Kern M 69 100 100107008 Jane F 20 74 100107009 Ann F 19 69 |
4.2 返回非整型值的函数
【函数: atof 字符串 ->双精度浮点数】
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | 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 = 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; } return sign * val / power; } |
【简单的计算器程序】
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | #include <stdio.h> #define MAXLINE 100 int main() { double sum; char line[MAXLINE]; sum = 0; while (getline(line, MAXLINE) > 0) printf("\t%g\n", sum += atof(line)); return 0; } |
4.3 外部变量 【逆波兰计算器】
程序结构
1 2 3 4 5 6 7 8 9 10 11 12 13 | while (下一个运算符或操作数不是文件结束指示符) { if (是数) 将该数压入到栈中 ; else if (是运算符) { 弹出所需数目的操作数 ; 执行运算 ; 将结果压入到栈中 ; } else if (是换行符) 弹出并打印栈顶的值; else 出错; } |
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 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 | #include <stdio.h> #include <stdlib.h> /* for atof() */ #include <ctype.h> #define MAXOP 100 /* max size of operand or operator */ #define NUMBER '0' /* signal that a number was found */ #define MAXVAL 100 /* maximum depth of val stack */ int sp = 0; double val[MAXVAL]; /* value stack */ 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; } } #define BUFSIZE 100 char buf[BUFSIZE]; /* buffer for ungetch */ int bufp = 0; int getch(void) /* get a (possibly pushed-back) character */ { return (bufp > 0) ? buf[--bufp] : getchar(); } void ungetch(int c) { if (bufp >= BUFSIZE) printf("ungetch: too many characters\n"); else buf[bufp++] = c; } /* getop: get next character or numeric operand */ 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; /* not a number */ 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; } 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; } |
4.10 递归
【打印十进制数】
1 2 3 4 5 6 7 8 9 10 | void printd(int n) { if (n < 0) { putchar('-'); n = -n; } if (n / 10) printd(n / 10); putchar(n % 10 + '0'); } |
调用 printd(123)时,第一次调用 printd 的参数 n=123。它把 12 传递给printd 的第二次调用,后者又把 1 传递结 printd 的第三次调用。第三次调用 printd 时,首先将打印 1,然后再返回到第二次调用。从第三次调用返回后的第二次调用同样也将先打印2,然后再返回到第一次调用。返回到第一次调用时将打 3,随之结束函数的执行。
【快速排序】
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 | void swap(int v[], int i, int j) { int t; t = v[i]; v[i] = v[j]; v[j] = t; } void qsort(int v[], int left, int right) { int i, last; if (left >= right) /* fewer than two elements */ return; /* move partition elem to v[0]*/ swap(v, left, (left + right)/2); last = left; for (i = left + 1; i <= right; i++) /* partition */ if (v[i] < v[left]) swap(v, ++last, i); swap(v, left, last); /* restore partition elem */ qsort(v, left, last-1); qsort(v, last+1, right); } |