/* lethead1.c */#include <stdio.h>#define NAME "GIGATHINK, INC."#define ADDRESS "101 Megabuck Plaza"#define PLACE "Megapolis, CA 94904"#define WIDTH 40void starbar(void); /* prototype the function */int main(void){ starbar(); printf("%s\n", NAME); printf("%s\n", ADDRESS); printf("%s\n", PLACE); starbar(); /* use the function */ return 0;}void starbar(void) /* define the function */{ int count; for (count = 1; count <= WIDTH; count++) putchar('*'); putchar('\n');}
函数类型:有返回值就声明成返回值的类型,无返回值声明 void;老式 C 默认 int 已被 C99 废除。注意类型指的是返回值,与参数无关:double klink(int a, int b) 收两个 int、返回 double。标准库的函数原型按家族放在头文件里:stdio.h 放 I/O 函数、math.h 放数学函数(如 double sqrt(double);)——声明告诉编译器函数长什么样,定义才提供代码。
9.5 ANSI C 函数原型(Prototyping)
老式声明 int imax(); 不说明参数,错误便溜进来。原书清单 9.4(misuse.c)用老式声明后分别调用 imax(3)(少一个参数)和 imax(3.0, 5.0)(类型不对),程序照样编译运行,输出却是 1245120、1074266112 这类垃圾值。
C 允许函数调用自己,即递归(recursion)。递归方案往往比循环更优雅,但效率更低、更耗内存。
递归揭秘
清单 9.6(recur.c)展示递归的进出路径:
/* recur.c -- recursion illustration */#include <stdio.h>void up_and_down(int);int main(void){ up_and_down(1); return 0;}void up_and_down(int n){ printf("Level %d: n location %p\n", n, &n); /* 1 */ if (n < 4) up_and_down(n+1); printf("LEVEL %d: n location %p\n", n, &n); /* 2 */}
输出呈对称的”先上后下”:
Level 1: n location 0x0012ff48Level 2: n location 0x0012ff3cLevel 3: n location 0x0012ff30Level 4: n location 0x0012ff24LEVEL 4: n location 0x0012ff24LEVEL 3: n location 0x0012ff30LEVEL 2: n location 0x0012ff3cLEVEL 1: n location 0x0012ff48
递归的基本事实:
每层调用有自己的变量——四层调用创建四个各叫 n 的独立变量(地址各不相同),返回第 1 层时最初的 n 仍是 1。
long fact(int n) // loop-based function{ long ans; for (ans = 1; n > 1; n--) ans *= n; return ans;}long rfact(int n) // recursive version{ long ans; if (n > 0) ans = n * rfact(n-1); else ans = 1; return ans;}
驱动程序限制输入 0–12,因为 13! 已超出本系统 long 的范围。递归版就是数学定义的照搬,到 n 为 0 时返回 1 收尾。两者结果一致,通常选循环:每次递归都要在栈上压入一整套新变量,更耗内存也更慢;但有些问题没有简单的循环解法。
/* binary.c -- prints integer in binary form */#include <stdio.h>void to_binary(unsigned long n);int main(void){ unsigned long number; printf("Enter an integer (q to quit):\n"); while (scanf("%ul", &number) == 1) { printf("Binary equivalent: "); to_binary(number); putchar('\n'); printf("Enter an integer (q to quit):\n"); } printf("Done.\n"); return 0;}void to_binary(unsigned long n) /* recursive function */{ int r; r = n % 2; if (n >= 2) to_binary(n / 2); putchar('0' + r); return;}
/* hotel.h -- constants and declarations for hotel.c */#define QUIT 5#define HOTEL1 80.00#define HOTEL2 125.00#define HOTEL3 155.00#define HOTEL4 200.00#define DISCOUNT 0.95#define STARS "**************************"// shows list of choicesint menu(void); // returns code for selected hotel// returns number of nights desiredint getnights(void);// computes price from rate and nightsvoid showprice(double rate, int nights);
间接(解引用)运算符 *:已知 ptr = &bah;,则 val = *ptr; 等价于 val = bah;——经由指针间接取值。别把它和乘法的二元 * 混淆。
声明指针必须指明指向的类型——不同类型占用字节数和存储格式不同,指针运算与解引用都依赖它:
int * pi; /* pi 是指向 int 的指针 */char * pc; /* pc 是指向 char 的指针 */float * pf, * pg; /* pf、pg 都是指向 float 的指针 */
int * pi; 读作:pi 是指针,*pi 是 int。指针的值是地址,多数系统内部按无符号整数表示,但指针不是整数类型(不能相乘),是独立的新类型,所以 printf() 专门有 %p。
用指针实现成功的 swap
清单 9.15(swap3.c)传地址版:
/* swap3.c -- using pointers to make swapping work */#include <stdio.h>void interchange(int * u, int * v);int main(void){ int x = 5, y = 10; printf("Originally x = %d and y = %d.\n", x, y); interchange(&x, &y); /* send addresses to function */ printf("Now x = %d and y = %d.\n", x, y); return 0;}void interchange(int * u, int * v){ int temp; temp = *u; /* temp gets value that u points to */ *u = *v; *v = temp;}
输出 Now x = 10 and y = 5.——成功。原理:interchange(&x, &y) 传地址,形参 u、v 是指向 int 的指针;u 的值是 &x,故 *u 就是 x 本身。temp = *u; 取到 x 的值(写成 temp = u; 就错了,那是地址),*u = *v; 相当于 x = y;。函数借此”伸手”到 main() 改数据。原型可省名:void interchange(int *, int *);。
Warning
想让函数修改变量,必须传地址(function2(&x))且形参声明为对应类型的指针;只想用值计算才传值(function1(x))。形式用错——把地址赋给普通 int 形参、或忘了 * 解引用——都是典型错误。scanf(“%d”, &num) 用的正是传地址形式。