本章介绍
- 关键字:return
-
- 运算符: *(一元), &(一元)
-
- 函数及其定义方式
-
- 如何使用参数和返回值
-
- 如何把指针变量用作函数参数
-
- 函数类型
-
- ANSI C原型
-
- 递归
章末小结
9.1.2 分析程序
函数原型指明函数的返回值类型和函数可以接受的参数类型。这些信息统称为函数的签名(signature)
#include <stdio.h>
#define NAME "GIGATHINK, INC."
#define ADDRESS "101 Megabuck Plaza"
#define PLACE "Megapolis, CA 94904"
#define WIDTH 40
void starbar(void); // ANSI C 函数原型
int main(void)
{
starbar(); // 该函数的作用就是打印四十个星号,调用函数
printf("%s\n", NAME);
printf("%s\n", ADDRESS);
printf("%s\n", PLACE);
starbar(); // 该函数的作用就是打印四十个星号,调用函数
return 0;
}
void starbar(void) // 函数准确的定义
{
int count;
for (count = 1; count <= WIDTH; count++)
putchar('*');
putchar('\n');
}
9.1.13 函数参数
函数参数的书写格式
void dibs(int x, y, z) /* 无效的函数头 */
void dubs(int x, int y, int z) /* 有效的函数头 */
void dibs(x, y, z)
int x, y, z; /* 有效 */
/* lethead2.c*/
/*20210315 20:11*/
#include <stdio.h>
#include <string.h>
#define NAME "GIGATHINK, INC."
#define ADDRESS "101 Megabuck Plaza"
#define PLACE "Megapolis, CA 94904"
#define WIDTH 40
#define SPACE ' '
void show_n_char(char ch, int num);
int main(void)
{
int spaces;
show_n_char('*', WIDTH); // 第一行
putchar('\n'); // 手动换行
show_n_char(SPACE, 12); // 12个空格
printf("%s\n", NAME); //打印NAME并且换行
spaces = (WIDTH - strlen(ADDRESS)) / 2;
show_n_char(SPACE, spaces);
printf("%s\n", ADDRESS); // 打印第二行的文字: ADDRESS
show_n_char(SPACE, (WIDTH - strlen(PLACE)) / 2);
printf("%s\n", PLACE);
show_n_char('*', WIDTH);
putchar('\n'); //换行
return 0;
}
void show_n_char(char ch, int num)
{
int count;
for (count = 1; count <= num; count++)
putchar(ch);
}
9.1.8 return返回
返回值不仅可以赋值给变量,还可以用作表达式的一部分;
返回值不一定是变量的值,也可以是任意表达式;
return语句可以融入花括号里面;
#include <stdio.h>
int imin(int, int);
int main(void)
{
int evill, evil2;
printf("Enter a pair of integers(q to quit): \n");
while (scanf_s("%d %d", &evill, &evil2) == 2)
{
printf("The lesser of %d and %d is %d.\n", evill, evil2, imin(evill, evil2));
printf("Enter a pair of integers (q to quit):\n");
}
printf("Bye.\n");
return 0;
}
int imin(int n, int m)
{
int min;
if (n < m)
min = n;
else
min = m;
return min;
}
9.2ANSI 函数原型
/* misuse.c -- 错误使用函数*/
// 20210315 23:15
#include <stdio.h>
int imax();
int main(void)
{
printf("The maximum of %d and %d is %d\n", 3, 5, imax(3));
printf("The maximum of %d and %d is %d\n", 3, 5, imax(3.0, 5.0));
return 0;
}
int imax(n, m)
int n, m;
{
return (n > m ? n : m);
}
9.2.2 ANSI 的解决方案
由代码可见,函数原型中,个数对应的形式参数数据类型是必须的,而变量是选配;
函数原型可以去掉,直接把自定义函数放在主调函数之前,完整声明,然后在主调函数中调用即可;
printf()的参数是不确定的,可以是多个。
/* proto.c -- 使用函数原型*/
// 20210315 23:43
#include <stdio.h>
int imax(int, int);
int main(void)
{
printf("The maximum of %d and %d is %d\n", 3, 5, imax(3, 5));
printf("The maximum of %d and %d is %d\n", 3, 5, imax(3.0, 5.0));
return 0;
}
int imax(int n, int m)
{
return (n > m ? n : m);
}
9.3 递归
代码解析:但n的值小于四的时候进入循环,资质进入第四层,因为每一层都只执行到第一句打印语句;
所以但第四层循环退出之后,首先进行的是重复当前循环的打印语句,也就是第二个打印语句,然后逐次退出第三层,第二层和第一层;
递归和循环,条件下可以相互转换;
可在调试模式下观察代码执行过程。
/*recur.c -- 演示递归*/
// 20210315 23:59
#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: location %p\n", n, &n); // #2
}
9.3.3尾递归
把递归调用置于函数末尾,return语句之前,相当于循环;
每次递归都会创造一组变量,所以占用的内存更多;
递归调用的数量受限于内存空间;
// factor.c -- 使用循环和递归计算阶乘
// 20210317 14:13
#include <stdio.h>
long fact(int n);
long rfact(int n);
int main(void)
{
int num;
printf("This program calculates factorials.\n");
printf("Enter a value in the range 0-12(q to quit):\n");
while (scanf_s("%d", &num) == 1)
{
if (num < 0)
printf("No negative numbers, please.\n");
else if (num > 12)
printf("Keep input under 13.\n");
else
{
printf("loop: %d factorial = %ld\n", num, fact(num));
printf("recursion: %d factorial = %ld\n", num, rfact(num));
}
printf("Enter a value in the range 0-12 (q to quit):\n");
}
printf("Bye!\n");
return 0;
}
long fact(int n)
{
long ans;
for (ans = 1; n > 1; n--)
ans *= n;
return ans;
}
long rfact(int n)
{
long ans;
if (n > 0)
ans = n * rfact(n - 1);
else
ans = 1;
return ans;
}
9.3.5 递归的优缺点
为编程问题提供了简单的解决方案,但会快速消耗计算机内存;
递归通过创造指数级变量快速占有大量内存空间,类似于fork()函数创建多线程;
9.4.5 使用头文件(构建文件项目)
1,文件结构
2,hotel.h
/*hotel.h -- 符号常量和hotel.c中的所有函数原型*/
//20210319 08:42
#include <stdio.h>
#define QUIT 5
#define HOTEL1 180.00
#define HOTEL2 225.00
#define HOTEL3 255.00
#define HOTEL4 355.00
#define DISCOUNT 0.95
#define STARS "****************************"
//显示选择列表
int menu(void);
//返回预定天数
int getnights(void);
//根据费率,入住天数计算费用
//并显示结果
void showprice(double rate, int nights);
3 hotel.c
/*hotel.c -- 酒店管理函数*/
//20210319 0851
#include <stdio.h>
#include "hotel.h"
int menu(void)
{
int code, status;
printf("\n%s%s\n", STARS, STARS);
printf("Enter the number of the desired hotel:\n");
printf("1) Fairfield Arms 2) Hotel Olympic\n");
printf("3) Cherworthy Plaza 4) The Stockon\n");
printf("5) quit\n");
printf("%s%s\n", STARS, STARS);
while ((status = scanf_s("%d", &code) != 1) || (code < 1 || code > 5))
{
if (status != 1)
scanf_s("%*s");
printf("Enter an integer form 1 to 5, please.\n");
}
return code;
}
int getnights(void)
{
int nights;
printf("How many nights are needed?");
while (scanf_s("%d", &nights) != 1)
{
scanf_s("%*s");
printf("Please enter an integer, such as 2.\n");
}
return nights;
}
void showprice(double rate, int nights)
{
int n;
double total = 0.0;
double factor = 1.0;
for (n = 1; n <= nights; n++, factor *= DISCOUNT)
total += rate * factor;
printf("The total cost will be $%0.2f.\n", total);
}
3 main.c
/*usehotel.c -- 房间汇率程序*/
/*与程序清单9.10一起编译*/
//20210319 09:23
#include <stdio.h>
#include "hotel.h"
int main(void)
{
int nights;
double hotel_rate;
int code;
while ((code = menu()) != QUIT)
{
switch (code)
{
case 1:
hotel_rate = HOTEL1;
break;
case 2:
hotel_rate = HOTEL2;
break;
case 3:
hotel_rate = HOTEL3;
break;
case 4:
hotel_rate = HOTEL4;
break;
default:
hotel_rate = 0.0;
printf("Oops!\n");
break;
}
nights = getnights();
showprice(hotel_rate, nights);
}
printf("Goodbye and welcome next time.\n");
return 0;
}
4,测试输出
9.5 查找地址:&运算符
取地址运算符也是一元运算符;
子函数共享主调函数的变量;
/*locheck.c -- 查看变量被存储在何处*/
// 20210320 19:31
#include <stdio.h>
void mikado(int);
int main(void)
{
int pooh = 2, bah = 5;
printf("In main(), pooh = %d and &pooh = %p\n", pooh, &pooh);
printf("In main(), bah = %d and &bah = %p\n", bah, &bah);
mikado(pooh);
return 0;
}
void mikado(int bah)
{
int pooh = 10;
printf("In mikado(), pooh = %d and &pooh = %p\n", pooh, &pooh);
printf("In mikado(), bah = %d and &bah = %p\n", bah, &bah);
}
9.6更改主调函数的变量
失败截图
/*swap1.c -- 第一个版本的交换函数*/
// 20210320 19:53
#include <stdio.h>
void interchange(int u, int y);
int main(void)
{
int x = 5, y = 10;
printf("Originally x = %d and y = %d.\n", x, y);
interchange(x, y);
printf("Now x = %d and y = %d.\n", x, y);
return 0;
}
void interchange(int u, int v)
{
int temp;
temp = u;
u = v;
v = temp;
}
对代码的改进
使用printf()语句,检查程序运行过程
/*swap1.c -- 第一个版本的交换函数*/
// 20210320 20:19
#include <stdio.h>
void interchange(int u, int y);
int main(void)
{
int x = 5, y = 10;
printf("Originally x = %d and y = %d.\n", x, y);
interchange(x, y);
printf("Now x = %d and y = %d.\n", x, y);
return 0;
}
void interchange(int u, int v)
{
int temp;
printf("Originally u = %d and v = %d.\n", u, v);
temp = u;
u = v;
v = temp;
printf("Now u = %d and v = %d.\n", u, v);
}
修改函数调用方式,传递参数返回主调函数,交换子函数的值会传递给主调函数,但缺点也是明显的。
首先需要修改函数原型声明,其次只能返回一个值
/*swap1.c -- 第一个版本的交换函数*/
// 20210320 20:19
#include <stdio.h>
int interchange(int u, int y);
int main(void)
{
int x = 5, y = 10;
printf("Originally x = %d and y = %d.\n", x, y);
x = interchange(x, y);
printf("Now x = %d and y = %d.\n", x, y);
return 0;
}
int interchange(int u, int v)
{
int temp;
printf("Originally u = %d and v = %d.\n", u, v);
temp = u;
u = v;
v = temp;
printf("Now u = %d and v = %d.\n", u, v);
return (u);
}
9.7 指针简介
使用指针也需要声明指针的数据类型;
&将变量指向该变量的内存地址, *解析该内存地址存储的值;
val = *ptr;
ptr = &bah;
val = *ptr;
// 等价于 val = bah
9.7.2 声明指针
必须指明指针指向变量的数据类型;
程序员在声明指针数据类型时在* 和数据类型之间保留一个空格,但是解引用变量时忽略空格;
指针指向的是一个新的数据类型,不要对它做整数的一些操作;
%p用于printf()语句中指针的转义声明;
9.7.3 使用指针在函数间通信
传入的是指向该变量的内存地址,但是函数内部完成交换是通过解析引用运算符
// 第一种声明方式,形式参数必须是一个与调入值数据类型相同
void interchange(int u, int v);
// 第二种声明方式, 形式参数必须是一个指向正确类型的指针
void interchange(int * u, int * v);
/* swap3.c 使用指针解决交换函数失败(实际是一种调用关系)*/
// 20210320 20:43
#include <stdio.h>
void interchange(int * u, int * v);
int main(void)
{
int x = 5, y = 10;
printf("Orinignally x = %d and y = %d.\n", x, y);
interchange(&x, &y);
printf("Now x = %d and y = %d.\n", x, y);
return 0;
}
void interchange(int * u, int * v)
{
int temp;
temp = *u;
*u = *v;
*v = temp;
}