C Primer Plus 第9章 函数 9.1 函数概述

9.1  函数概述程序员

首先,什么是函数?函数(funcation)是用于完成特定任务的程序代码的自包含单元数组

为何使用函数?第一,函数的使用能够省去重复代码的编写。第二,即便某种功能在程序中只使用一次,将其以函数的形式实现也是有必要的,由于函数使得程序更加模块化,从而有利于程序的阅读、修改和完善。less

许多程序员喜欢把函数看做“黑盒子”,即对应必定的输入会产生特定的结果或返回某个数值,而黑盒子的内部行为并不须要考虑,除非是该函数的编写者。以这种方式看待函数有助于把精力投入到程序总体设计而不是其实现细节。所以,编写函数代码以前首先要考虑的是函数的功能 以及函数和程序总体上的关系模块化

9.11  编写和使用一个简单的函数函数

编写一个在一行中输出40个星号的函数,而后咱们使用该函数打印一个简单的信头。程序清单9.1给出了完整的程序,它由main()函数和starbar()函数组成。测试

程序清单 9.1 lethead1.c程序ui

/*lethead1.c*/
#include <stdio.h>
#define NAME "GIGATHINK,INC."
#define ADDRESS "101 Megabuck plaza"
#define PLACE "Megapolis,CA 94904"
#define WIDTH 40

void starbar(void); /*声明函数原型*/

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.2  程序分析spa

关于函数有如下几点须要注意:prototype

一、starbar标识符在不一样位置使用了3次:函数原型(funcation prototype)告知编译器starbar()的函数类型函数调用(funcation call)致使该函数的执行而函数定义(funcation definition)则确切指定了该函数的具体功能设计

二、函数同变量同样有多种类型。任何程序在使用函数以前都须要声明该函数的类型

void starbar(void)

圆括号代表starbar是一个函数名第一个void指的是函数类型,它的意思是该函数没有返回值。第二个void位于圆括号内代表该函数不接受任何参数分号的做用是表示该语句是进行函数声明而不是函数定义。也就是说,这一行声明了程序将使用一个名为starbar()且函数类型为void的函数,同时通知编译器须要在其余位置找到该函数的定义。对于不识别ANSI C原型的编译器,只须要声明函数的类型,就像下面这样:void starbar();

注意:一些老版本的编译器不能识别void类型。这时,须要把没有返回值的函数声明为INT类型。

三、程序把starbar()原型置于main()以前,也能够置于main()以内,能够放置变量声明的任何位置,这两种方法都正确。

四、程序在main()中经过使用函数名后跟圆括号和分号的格式调用函数starbar(),语句以下:starbar();这是void类型函数的通常调用形式。当计算机执行到starbar();语句时,它找到starbar()函数并执行其中的指令。执行完starbar()中的代码后,计算机返回到调用函数(calling funcation)的下一行继续执行。在本例中,调用函数是main()。

五、程序中starbar()和main()具备相同的定义格式,即首先以类型、名称和圆括号开始,接着是开始花括号、变量声明、函数语句定义以及结束花括号。注意此处的starbar()后没有分号,这告诉编译器您是在定义函数starbar()而不是在调用它或声明它的原型。

六、能够将starbar()和main()放在同一个文件中,也能够将它们放在不一样的文件中。单文件形式比较容易编译,而使用两个文件则有利于在不一样程序中使用相同的函数。若是您把函数写在了另一个单独的文件中,则在那个文件中必须加入#define和#include指令。

七、starbar()中的变量count是一个局部变量。这意味着该变量只在starbar()中可用。即便您在其余函数包括main()函数中使用名称count,也不会出现任何冲突,您将获得具备同一名称的多个单独的、互不相关的变量。

由于不须要来自调用函数的任何信息,因此它没有输入参数。同时它不向main()提供任何信息,所以starbar()也没有返回值。简言之,starbar()不须要同调用函数进行任何通讯。

9.1.3  函数参数

在上例中,若是文字居中显示那么信头就会更漂亮。能够经过在打印文字以前打印必定数目的空格 来达到此目的 。这和starbar()函数相似。在starbar()中打印的是必定数量的星号,而如今要打印的是必定数目的空格。遵循c的设计思想,咱们不该为每一个任务编写一个单独的函数,而应该编写一个能够同时胜任这两个任务的更为通用的函数。新函数将命名为show_n_char()。惟一改变是要显示的字符和显示次数将被做为参数传递给函数show_n_char(),而不是将它们置于函数内部。

具体一点说,假如一行是40个字符宽。40个星号填满一行,调用函数show_n_char('*',40)能够同starbar()同样实现该功能。而将GIGATHINK,INC.居中须要多少个空格?由于 GIGATHINK,INC. 是15个字符宽,所以在前面的例子中该短语后跟有25个空格。为使其居中,必须先输出12个空格,这样该短语两边就会分别有13个和12个空格。因此,能够调用 show_n_char(' ',12)输出12个空格。

除了使用参数外,在其余方面show_n_char()函数和starbar()很是类似。二者的一个不一样之处是show_n_char()不像starbar()那样输出换行符,由于在同一行中可能还须要输出其余文字。程序清单9.2给出了改进后的程序。为了强调参数的使用,程序中使用了多种参数形式。

程序清单 9.2 lethead2.c 程序

#include <stdio.h>
#include <string.h>    /*为strlen()提供原型*/
#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);    /*使用常量做为参数*/
    printf("%s\n",NAME);
    spaces=(WIDTH-strlen(ADDRESS))/2;    /*让程序计算须要多少个空格*/

    show_n_char(SPACE,spaces);    /*使用变量做为参数*/
    printf("%s\n",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.4  定义带有参数的函数:形式参量

函数定义如下面的ANSI C 函数头开始

void show_n_char(char ch,int num)

这行代码通知编译器show_n_char()使用名为ch和num的两个参数,而且这两个参数的类型分别是char和int。变量ch和num被称为形式参数(formal argument)或形式参量(formal parameter,如今这个名称更为正式)。如同函数内部定义的变量同样,形式参量是局部变量它们是函数私有的。这意味着能够其余函数中使用相同的变量名。每当调用函数时,这此变量就会被赋值。

注意,ANSI C 形式要求在每一个变量前声明其类型。也就是说,不能像一般的变量声明那样使用变量列表来声明同一类型的变量,以下所示:

void dibs (int x,y,z)               /*不正确的函数头*/

void dubs (int x, int y,int z)   /*正确的函数头*/

尽管show_n_char()接收来自main()的数值,可是它没有返回值。所以,show_n_char()的类型是void。

9.1.5  带参数函数的原型声明

使用函数以前须要用ANSI原型声明该函数:

void show_n_char(char ch,int num);

当函数接受参数时,函数原型经过使用一个逗号分隔的类型列表指明参数的个数和类型。在函数原型中,能够根据我的的喜爱省略变量名:

void show_n_char(char,int);

在原型中使用变量名并无实际的建立变量。这只是说明char表明了一个char类型变量,依此类推。

9.1.6  调用带有参数的函数:实际参数

函数调用中,经过使用实际参数(actual argument)对ch 和 num赋值

show_n_char(SPACE,12);

实际参数是空格字符和12。这两个数值被赋给show_n_char()中相应的形式参量:变量ch和num。换句话说,形式参量是被调函数中的变量,而实际参数是调用函数分配给被调用函数变量的特定数值。实际参数能够是常量、变量或一个复杂的表达式。可是不管何种形式的实际参数,执行时首先要计算其值,而后将该值复制给被调函数中相应的形式参量。

再次,实际参数是赋给被称为形式参量的函数变量的具体值。由于被调函数使用的值是从调用函数中复制而来的,因此无论在被调函数中对复制数值进行什么操做,调用函数中的原数值不会受到任何影响。

*当一个函数被调用时,将建立被声明为形式参量的变量,而后用计算后获得的实际参数的值初始化该变量。

9.1.7  黑盒子观点

黑盒子方法的核心部分在于ch num 和count都是show_n_char()私有的局部变量。也就说,若是在main()中使用相同名字的变量,它们相互独立,互不影响。例如,若是在main()中存在一个count变量,那么该 变量值的改变不会影响show_n_char()中的count变量,其他变量也是如此。黑盒子的一切操做对调用函数来讲是不可见的。

9.1.8  使用return从函数中返回一个值

前面讨论了从调用函数到被调用函数的通讯方法。须要沿相反方向传递信息时,可使用函数返回值。为了进一步说明,咱们将构建一个比较两个参数大小并将小数值返回的函数。由于比较的是int类型的数值,因此函数被命名为imin()。同时,为了检查imin()的执行结果,须要编写一个简单的main()函数。这种用来测试函数的程序有时被称为驱动程序。驱动程序实际调用了被测试的函数。若是该函数成功经过了测试,那么它就能够在一个更为重要的程序中使用。

程序清单9.3

/*lesser.c --找出两个整数中的较小者*/
#include <stdio.h>
int imin(int,int);
int main(void)
{
    int evil1,evil2;
    printf("Enter a pair of integers (q to quit): \n");
    while(scanf("%d %d",&evil1,&evil2)==2)
    {
        printf("The lesser of %d and %d is %d.\n",
               evil1,evil2,imin(evil1,evil2));
        printf("Enter a pair of integer (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;
}

关键字return指明了其后的表达式的数值便是该函数的返回值。在本例子中,函数返回变量min的数值。由于min的类型是int,因此函数imin()的类型也是int。

变量min是imin()私有的,可是return语句把min的数值返回给了调用函数。下面这个语句的做用至关于把min的值赋给lesser:

lesser=imin(n,m);

可否用下厕所这个语句代替上句:

imin(n,m); lesser=min;

答案是否认的,由于调用函数并不知道min变量的存在。imin()中的变量是该函数的局部变量。函数调用imin(evil1,evil2)只是复制了两个变量的数值。

返回值不只能够被赋给 一个变量,也能够被用做表达式的一部分。例如:

answer=2*imin(z,zstar)+25;

printf("%d\n",imin(-32+answer,LIMIT));

返回值能够由任何表达式计算得出,而不是仅仅来自于变量。例如:

/*最小函数值的第2个版本*/

imin(int n,int m)

{

        return ((n<m) ? n : m) ;

}

当函数返回值的类型和声明的类型不相同时会有什么结果呢?

这时,实际返回值是当把指定要返回的值赋给 一个具备所声明的返回类型的变量时获得的数值

return语句的另外一做用是终止执行函数,并把控制返回给调用函数的下一个语句。即便return语句不是函数最后一个语句,其执行结果也是如此。所以,能够用下面的方式编写imin()函数:

/*最小值函数的第3个版本*/

imin(int n,int m)

{

    if (n<m)

        return n;

    else

        return m;

}

许多C程序员更倾向于只在函数结尾使用一次return语句,由于这样更有利于阅读程序的人明白函数的执行流程。

您也可使用如下语句:

return ;

这个语句会终止执行函数并把控制返回给调用函数。由于return后没有任何表达式,因此没有返回值,这种形式只能用于void类型的函数之中。

9.1.9  函数类型

函数应该进行类型声明。同时其类型应该和返回值类型相同。而无返回值的函数应该被声明为void类型

类型声明是函数定义的一部分。但须要注意的是该 类型是返回值类型,而不是函数参数类型

为正确的使用函数,程序在首次调用函数以前须要知道该函数的类型。途径之一是在第一次调用以前进行完整的函数定义。可是,这种方式会使得程序难于阅读。并且,须要的函数可能在C库中或其余文件中。所以,一般的作法是预先对函数进行声明,以便将函数的信息通知给编译器。

在上例的代码中,函数的预先声明被放在调用函数以外。也能够在调用函数内部预先声明被调函数

在以上两种形式中,须要重点注意的是函数声明要在使用函数以前进行。

不要把函数声明和函数定义混淆。函数声明只是将函数类型告诉编译器,而函数定义部分则是函数的实际实现代码。引用math.h头文件只向编译器说明了sqrt()的返回值类型double,可是sqrt()的实现代码则位于另一个库函数文件中。

相关文章
相关标签/搜索