C++篇 函数

本文详细解释了C语言中函数的定义、参数传递、返回值类型、声明与调用,涉及静态局部变量、数组作为参数、递归以及return语句和exit函数的使用,还以Fibonacci数列为例讲解了递归的概念。
摘要由CSDN通过智能技术生成

函数的定义

函数定义的一般格式如下:         

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;
}
如果形式参数是多维数组,声明参数时我们只能省略第一个维度的长度 ( why ?) 。例如,如果 sum_array 函数的第一个参数是二维数组, 我们可以不指定行的数量,但是必须指定列的数量
#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;
}

 局部变量和外部变量

我们把在函数体内声明的变量称为该函数的 局部变量,声明在函数体外的变量称之为外部变量 。比如下面函数中的 sum 是局部 变量:
int sum_digits(int n) {
    int sum = 0;       /*local variable*/
    while (n > 0) {
        sum += n % 10;
        n /= 10;
   }
    return sum;
}
默认情况下,局部变量具有下列性质:
  • 变量的生命周期。简单来讲,存储期限就是变量在程序运行过程中存在的时间长度。局部变量的存储单元在函数调用时"自动"分配,在函数返回时自动回收,所以称这种变量具有自动存储期限。
  • 块作用域。变量的作用域就是:在程序文本中可以引用该变量的部分。局部变量拥有块作用域 :从变量声明的点开始一直到所在块的末尾。
  • 块:简单来说,就是用 {} 括起来的文本区域。

静态局部变量

在局部变量声明中使用 static 关键字,可以使局部变量具有 静态存储期限 。具有静态存储期限的变量拥有永久的存储单元, 所以在整个程序执行期间都会保留变量的值 。比如:
#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 语句
void 函数必须使用 return 语句来指定函数要返回的值。 return 语句的格式如下:
return expr;
如果 return 语句中表达式的类型和函数的返回值类型不匹配,那么系统将会把表达式的类型隐式转换为返回值类型。例如,如果函数的返回值类型为 int ,但是 return 语句中表达式的类型为 double ,那么系统会把表达式的值转换成 int 类型。
void 函数也可以使用 return 语句,使函数立刻返回,只是 return 后面不能接表达
式。如:
void print_positive(int n) {
 if (n <= 0)
     return ;
 cout<< i;
}
程序终止
main 函数的返回值类型为 int ,它表示程序返回时的状态码。如果程序正常终止,main 函数应该返回 0 ;如果程序异常终止,那么 main 函数应该返回非 0 的值。
如何在其他函数中终止进程?
        exit 函数

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

}
因为 0 是一个魔法数字,所以 C 语言允许使用 EXIT_SUCCESS 来替代:exit(EXIT_SUCCESS);
如果异常退出,可以这样写: exit(EXIT_FAILURE);
EXIT_SUCCESS EXIT_FAILURE 都是定义在 <iostream > 中的宏,它们的值是由实现决定的,通常分别为 0 1
return 语句和 exit 函数之间的差异是:不管哪个函数调用 exit 函数都会导致程序终止, return 语句仅当在 main 函数中执行时才会导致程序的终止。
递归
如果一个函数调用它本身,那么这个函数就是递归的。
从递归的定义去理解递归不是很好,我们可以从名字入手去理解。
  1. 递:把大问题分解成若干个子问题,子问题的求解方式和大问题一致,只是问题规模不一致。
  2. 归:把子问题的解合并成大问题的解。
例子 :Fibonacci 数列。

我们可以利用上面的公式,求解 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);
}

  • 8
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值