函数的定义
函数定义的一般格式如下:
return-type function-name (parameters) {
declarations
statements
}
返回值类型遵循以下规则:
- 函数不能返回数组,除此之外,函数可以返回任意类型的值。
- 如果函数的返回值类型为 void,则函数没有返回值。
- 如果省略返回值类型,C89 会假定返回值类型为 int;但在 C99 中这是不合法的。
一些程序员喜欢把返回值类型放在函数名的上边:
double
average(double a, double b) {
return (a + b) / 2;
}
如果返回值类型很长,比如 unsigned long long int ,那么这种方法是很有用的。 函数名的后面是形式参数列表。我们需要在每个形式参数前面指定它的类型,并且参数之间用逗号分隔。如果函数没有形式参数,那么应该标明为 void。 函数体内包含声明和语句。在函数体内声明的变量只属于此函数,其他函数不能访问 和修改这些变量。
函数调用
函数调用由函数名和实际参数 (argument) 列表组成。如: average(x, y) 。 非 void 函数 (返回值类型不为 void 的函数) 调用会产生一个值,该值可以存储在变量中,用于测试、显示,或者是用于其他用途:
avg = average(x, y);
if (average(x, y) > 0)
printf("Average is positive\n");
printf("The average is %lf\n", average(x, y));
返回值类型为 void 的函数,如:
void print_pun(void) {
printf("To C, or not to C: that is the question.\n");
}
void 函数调用必须紧跟分号,如:
print_pun();
【练】判断一个整数是否为素数。用户输入整数,程序显示该数是否为素数。会话如下:
#include<iostream>
//#include<stdio.h>
using namespace std;
void isPrime(int m){
for(int i = 2; i < m; i++)
if(m%i == 0){
cout<<"Not Prime";
}
cout<<"Is Prime";
}
int main()
{
int n;
cout<<"Enter a number :";
cin>>n;
isPrime(n);
return 0;
}
#include<iostream>
//#include<stdio.h>
using namespace std;
bool isPrime(int m){
for(int i = 2; i < m; i++)
if(m%i == 0){
return false;
}
return true;
}
int main()
{
int n;
cout<<"Enter a number :";
cin>>n;
if(isPrime(n))
cout<<"Is Prime";
else cout<<"Not Prime";
return 0;
}
函数声明
如果把函数定义放在函数调用之后,会发生什么情况呢?
#include<iostream>
using namespace std;
int main(void) {
double x, y, z;
printf("Enter three numbers: ");
scanf("%lf%lf%lf", &x, &y, &z);
printf("Average of %lf and %lf: %lf\n", x, y, average(x, y));
printf("Average of %lf and %lf: %lf\n", y, z, average(y, z));
printf("Average of %lf and %lf: %lf\n", x, z, average(x, z));
return 0;
}
double average(double a, double b) {
return (a + b) / 2;
}
为了避免这样的问题,一种方法是使每个函数的定义都出现在其调用之前。但是这种 方法不总是可行的 (why?),而且会使程序难以阅读。但C 语言提供了一种更好的解决方法:在调用之前声明函数。函数声明 (function declaration) 使得编译器能够知道函数的概要信息。函数声明类似函数定义 的第一行,其格式如下:
return-type function-name ( parameters );
函数声明中可以省略形参的名字,只要给定形参的类型即可。如:
double average(double, double);
实际参数
我们首先复习一下形式参数和实际参数的区别。形式参数出现在函数定义中,它们表 示在函数调用时应该提供哪些值;而实际参数出现在函数调用中。
在 C 语言中实参是值传递 (passed by value) 的。调用函数时,计算每个实参的值并 把它赋值给相应的形参。在函数调用过程中,对形参的改变不会影响到实参的值,这 是因为形参中保留的其实是实参的副本。
#include<iostream>
using namespace std;
void swap(int a, int b){
int t = a;
a = b;
b = t;
}
int main(void) {
int a = 10;
int b = 20;
swap(a,b);
cout<<"a:"<<a<<" b:"<<b;
return 0;
}
数组作为参数
我们经常把数组当作参数。当形式参数为一维数组时,我们可以不指定它的长度,数组作为参数传递时,会退化成指向数组第一个元素的指针,导致长度信息丧失:
虽然我们可以使用 sizeof 运算符计算数组的长度,但在这种情况下不适用。(why?)
#include<iostream>
using namespace std;
int sum_array(int a[]) {
int i, sum = 0;
for (i = 0; i < sizeof(a) / sizeof(a[0]); i++)
sum += a[i];
return sum;
}
int main(void) {
int a[3] = {1, 2, 3};
cout<<sum_array(a);
return 0;
}
函数调用时,第一个参数是数组名,第二个参数是数组的长度。如:
#include<iostream>
using namespace std;
int sum_array(int a[], int n) {
int i, sum = 0;
for (i = 0; i < n; i++)
sum += a[i];
return sum;
}
int main(void) {
int a[3] = {1, 2, 3};
cout<<sum_array(a, sizeof(a)/sizeof(a[0]));
return 0;
}
#define LEN 10
int sum_array(int a[][LEN], int n) {
int i, j, sum = 0;
for (i = 0; i < n; i++)
for (j = 0; j < LEN; j++)
sum += a[i][j];
return sum;
}
局部变量和外部变量
int sum_digits(int n) {
int sum = 0; /*local variable*/
while (n > 0) {
sum += n % 10;
n /= 10;
}
return sum;
}
- 变量的生命周期。简单来讲,存储期限就是变量在程序运行过程中存在的时间长度。局部变量的存储单元在函数调用时"自动"分配,在函数返回时自动回收,所以称这种变量具有自动存储期限。
- 块作用域。变量的作用域就是:在程序文本中可以引用该变量的部分。局部变量拥有块作用域 :从变量声明的点开始一直到所在块的末尾。
- 块:简单来说,就是用 {} 括起来的文本区域。
静态局部变量
#include<iostream>
using namespace std;
void foo(void);
int main(void) {
foo();
return 0;
}
void foo(){
int i = 0;
cout<<&i<<" "<<i<<endl;
foo();
}
#include<iostream>
using namespace std;
void foo(void);
int main(void) {
foo();
return 0;
}
void foo(){
static int i = 0;
cout<<&i<<" "<<i<<endl;
foo();
}
静态局部变量,初始化只执行了一次。保留上一次函数的结果。
【练】fibonacci:0, 1, 1, 2, 3, 5, 8, 13, ……
方法一:
#include<iostream>
using namespace std;
long long next_fib(void);
void foo(void);
int main(void) {
cout<<next_fib()<<endl;
cout<<next_fib()<<endl;
cout<<next_fib()<<endl;
return 0;
}
long long next_fib(void){
static long long a = 0;
static long long b = 1;
static long long temp = 0;
temp = a + b;
a = b;
b = temp;
return temp;
}
方法二:
#include<iostream>
using namespace std;
long long next_fib(void);
void foo(void);
int main(void) {
cout<<next_fib()<<endl;
cout<<next_fib()<<endl;
cout<<next_fib()<<endl;
return 0;
}
long long next_fib(void){
static long long a = 0;
static long long b = 1;
static long long temp = 0;
temp = a + b;
a = b;
b = temp;
return temp;
}
外部变量(全局变量)就是声明在任何函数体外的变量,外部变量的性质不同于局部变量:
- 静态存储期限。具有静态存储期限的变量拥有永久的存储单元,所以在整个程序执行期间都会保留变量的值。
- 文件作用域。从变量声明开始,一直到文件的末尾。因此,在外部变量声明之后的函数都可以访问(并修改)它。
return expr;
void print_positive(int n) {
if (n <= 0)
return ;
cout<< i;
}
在 main 函数中执行 return 语句是终止程序的一种方法,另一种方法是调用 exit 函数。 exit 函数包含在 <stdlib.h> 头文件中。传递给 exit 函数的参数和 main 函数的返回值具有相同的含义:两种都表示程序终止时的状态。
正常结束时,我们可以这样写:exit();
#include<iostream>
using namespace std;
void bar();
void foo(void);
int main(void) {
cout<< "main begin"<<endl;
bar();
cout<< "main end"<<endl;
return 0;
}
void bar(){
cout<<"bar begin"<<endl;
exit(0);
cout<<"bar end"<<endl;
}
- 递:把大问题分解成若干个子问题,子问题的求解方式和大问题一致,只是问题规模不一致。
- 归:把子问题的解合并成大问题的解。
我们可以利用上面的公式,求解 Fibonacci 数列的第 n 项。
#include<iostream>
using namespace std;
long long fib(int n);
void foo(void);
int main(void) {
cout<< fib(10)<<endl;
return 0;
}
// 0, 1, 1, 2
long long fib(int n) {
if (n == 0 || n == 1) return n;
return fib(n-2) + fib(n-1);
}