论坛交流
首页办公自动化| 网页制作| 平面设计| 动画制作| 数据库开发| 程序设计| 全部视频教程
应用视频: Windows | Word2007 | Excel2007 | PowerPoint2007 | Dreamweaver 8 | Fireworks 8 | Flash 8 | Photoshop cs | CorelDraw 12
编程视频: C语言视频教程 | HTML | Div+Css布局 | Javascript | Access数据库 | Asp | Sql Server数据库Asp.net  | Flash AS
当前位置 > 文字教程 > C语言程序设计教程
Tag:新手,函数,指针,数据类型,对象,Turbo,入门,运算符,数组,结构,二级,,tc,游戏,试题,问答,编译,视频教程

C语言程序设计(第4章 函数)

文章类别:C语言程序设计 | 发表日期:2008-9-24 14:19:48

4.1 函数说明与返回值
    4.1.1 函数的类型说明
    4.1.2 返回语句
4.2 函数的作用域规则
    4.2.1 局部变量
    4.2.2 全局变量
    4.2.3 动态存储变量
    4.2.4 静态存储变量
4.3 函数的调用与参数
    4.3.1 形式参数与实际参数
    4.3.2 赋值调用与引用调用
4.4 递归
4.5 实现问题
    4.5.1 参数和通用函数
    4.5.2 效率
4.6 函数库和文件
    4.6.1 程序文件的大小
    4.6.2 分类组织文件
    4.6.3 函数库
4.7 C语言的预处理程序与注释
    4.7.1 C语言的预处理程序
    4.7.2 #define


    在学习C语言函数以前,我们需要了解什么是模块化程序设计方法。人们在求解一个复杂问题时,通常采用的是逐步分解、分而治之的方法,也就是把一个大问题分解成若干个比较轻易求解的小问题,然后分别求解。程序员在设计一个复杂的应用程序时,往往也是把整个程序划分为若干功能较为单一的程序模块,然后分别予以实现,最后再把所有的程序模块像搭积木一样装配起来,这种在程序设计中分而治之的策略,被称为模块化程序设计方法。
    在C语言中,函数是程序的基本组成单位,因此可以很方便地用函数作为程序模块来实现C语言程序。利用函数,不仅可以实现程序的模块化,程序设计得简单和直观,提高了程序的易读性和可维护性,而且还可以把程序中普通用到的一些计算或操作编成通用的函数,以供随时调用,这样可以大大地减轻程序员的代码工作量。
    函数是C语言的基本构件,是所有程序活动的舞台。函数的一般形式是:
type-specifier function_name(parameter list)
parameter declarations
{
body of the function
}
    类型说明符定义了函数中return语句返回值的类型,该返回值可以是任何有效类型。假如没有类型说明符出现,函数返回一个整型值。参数表是一个用逗号分隔的变量表,当函数被调用时这些变量接收调用参数的值。一个函数可以没有参数,这时函数表是空的。但即使没有参数,括号仍然是必须要有的。参数说明段定义了其中参数的类型。

4.1 函数说明与返回值
    当一个函数没有明确说明类型时, C语言的编译程序自动将整型( i n t)作为这个函数的缺省类型,缺省类型适用于很大一部分函数。当有必要返回其它类型数据时,需要分两步处理:
    首先,必须给函数以明确的类型说明符;
    其次,函数类型的说明必须处于对它的首次调用之前。只有这样,C编译程序才能为返回非整型的值的函数生成正确代码。

4.1.1 函数的类型说明
    可将函数说明为返回任何一种合法的C语言数据类型。
    类型说明符告诉编译程序它返回什么类型的数据。这个信息对于程序能否正确运行关系极大,因为不同的数据有不同的长度和内部表示。
    返回非整型数据的函数被使用之前,必须把它的类型向程序的其余部分说明。若不这样做,C语言的编译程序就认为函数是返回整型数据的函数,调用点又在函数类型说明之前,编译程序就会对调用生成错误代码。为了防止上述问题的出现,必须使用一个非凡的说明语句。
[例4 - 1 ]
float sum( ); / *函数说明* /
main( )
{
    float first,s e c o n d ;
    first =123.23;
    second = 99.09;
    printf ("%f",sum (first,s e c o n d ) );
}

float sum (a,b ) / * 函数定义* /
float a,b;
{
    return a+b;
}

第一个函数的类型说明sum( )函数返回浮点类型的数据。这个说明使编译程序能够对sum( )的调用产生正确代码。
    函数类型说明语句的一般形式是:
    type_specifier function_name ( ) ;
    即使函数使用形参,也不要将其写入说明句。若未使用类型说明语句,函数返回的数据类型可能与调用者所要求的不一致,其结果是难以预料的。假如两者同处于一个文件中,编译程序可以发现该错误并停止编译。假如不在同一个文件中,编译程序无法发现这种错误。类型检查仅在编译中进行,链接和运行时均不检查。因此,必须十分细心以确保绝不发生上述错误。当被说明为整型的函数返回字符时,这个字符值被转换为整数。因为C语言以不加说明的方式进行字符型与整型之间的数据转换,因而多数情况下,返回字符值的函数并不是说明为返回字符值,而是由函数的这种字符型向整型的缺省类型转换隐含实现的。

4.1.2 返回语句
    返回语句return有两个重要用途。第一,它使得内含它的那个函数立即退出,也就是使程序返回到调用语句处继续进行。第二,它可以用往返送一个数值。本章将说明这两个用途。
1. 从函数返回
函数可以用两种方法停止运行并返回到调用程序。第一种是在执行完函数的最后一个语句之后,从概念上讲,是碰到了函数的结束符“ }”(当然这个花括号实际上并不会出现在目标码中,但我们可以这样理解)。例如,下面的函数在屏幕上显示一个字符串。

[例4 - 2 ]
pr_reverse ()
{
    char s[80]; / *定义一个字符数组* /
    scanf("%s",s); / *输入一个字符串,其长度不超过7 9个字符* /
    printf("%s\n",s);
}
一旦字串显示完毕,函数就没事可做了,这时它返回到被调用处。
    在实际情况中,没有多少函数是以这种缺省方式终止运行的。因为有时必须送回一个值,大多数函数用return语句终止运行,有时在函数中设立了多个终止点以简化函数、提高效率。切记,一个函数可以有多个返回语句。如下所示,函数在s1、s2相等时返回1,不相等时返回- 1。

[例4 - 3 ]
find_char(s1,s2)
char s1,s2;
{
    if(s1 == s2)
        return 1;
    else
        return -1;
}
2. 返回值
    所有的函数,除了空值类型外,都返回一个数值(切记,空值是A N S I建议标准所做的扩展,也许并不适合读者手头的C编译程序)。该数值由返回语句确定。无返回语句时,返回值是0。这就意味着,只要函数没有被说明为空值,它就可以用在任何有效的C语言表达式中作为操作数。这样下面的表达式都是合法的C语言表达式。
    x = power(y);
    if(max (x,y) >100) printf("greater");
    for (ch=getchar( ); isdigit (ch);) . . . ;
    可是,函数不能作为赋值对象,下列语句是错误的:
    swap(x,y) =100;
    C编译程序将认为这个语句是错误的,而且对含有这种错误语句的程序不予编译。
    所有非空值的函数都会返回一个值。我们编写的程序中大部分函数属于三种类型。第一种类型是简单计算型—函数设计成对变量进行运算,并且返回计算值。计算型函数实际上是一个“纯”函数,例如sqr( )和sin( )。第二类函数处理信息,并且返回一个值,仅以此表示处理的成功或失败。例如write( ),用于向磁盘文 件写信息。假如写操作成功了, write( )返回写入的字节数,当函数返回- 1时,标志写操作失败。最后一类函数没有明确的返回值。实际上这类函数是严格的过程型函数,不产生值。假如读者用的是符合A N S I建议标准的C编译程序,那么所有这一类函数应当被说明为空值类型。希奇的是,那些并不产生令人感爱好的结果的函数却无论如何也要返回某些东西。例如printf( )返回被写字符的个数。然而,很难找出一个真正检查这个返回值的程序。因此,虽然除了空值函数以外的所有函数都返回一个值,我们却不必非得去使用这个返回值。有关函数返回值的一个常见问题是:既然这个值是被返回的,我是不是必须把它赋给某个变量?回答是:不必。假如没有用它赋值,那它就被丢弃了。
    请看下面的程序,它使用了mul( )函数。mul( )函数定义为:int mul(int x, int y){......}
[例4 - 4 ]
main( )
{
    int x,y,z;
    x = 10, y = 20;
    z = mul(x,y); /* 1 */
    printf("%d",mul(x,y)); /* 2 */
    mul(x,y); /* 3 */
}
    在第一行, mul( )的返回值被赋予z,在第二行中,返回值实际上没有赋给任何变量,但被printf( )函数所使用。最后,在第三行,返回值被丢弃不用,因为既没有把它赋给第一个变量,也没有把它用作表达式中的一部分。

4.2 函数的作用域规则
    “语言的作用域规则”是一组确定一部分代码是否“可见”或可访问另一部分代码和数据的规则。
    C语言中的每一个函数都是一个独立的代码块。一个函数的代码块是隐藏于函数内部的,不能被任何其它函数中的任何语句(除调用它的语句之外)所访问(例如,用go to语句跳转到另一个函数内部是不可能的)。构成一个函数体的代码对程序的其它部分来说是隐蔽的,它既不能影响程序其它部分,也不受其它部分的影响。换言之,由于两个函数有不同的作用域,定义在一个函数内部的代码数据无法与定义在另一个函数内部的代码和数据相互作用。
    C语言中所有的函数都处于同一作用域级别上。这就是说,把一个函数定义于另一个函数内部是不可能的。

4.2.1 局部变量
    在函数内部定义的变量成为局部变量。在某些C语言教材中,局部变量称为自动变量,这就与使用可选要害字auto定义局部变量这一作法保持一致。局部变量仅由其被定义的模块内部的语句所访问。换言之,局部变量在自己的代码模块之外是不可知的。切记:模块以左花括号开始,以右花括号结束。
    对于局部变量,要了解的最重要的东西是:它们仅存在于被定义的当前执行代码块中,即局部变量在进入模块时生成,在退出模块时消亡。
    定义局部变量的最常见的代码块是函数。例如,考虑下面两个函数。
[例4 - 5 ]
func1( )
{
    int x; /* 可定义为auto int x; */
    x = 10 ;
}
func2( )
{
    int x; /* 可定义为auto int x; */
    x = -1999;
}
    整数变量x被定义了两次,一次在func1( )中,一次在func2( )中。func1( )和func2( )中的x互不相关。其原因是每个x作为局部变量仅在被定义的块内可知。
    语言中包括了要害字auto,它可用于定义局部变量。但自从所有的非全局变量的缺省值假定为auto以来,auto就几乎很少使用了,因此在本书所有的例子中,均见不到这一要害字。
    在每一函数模块内的开始处定义所有需要的变量,是最常见的作法。这样做使得任何人读此函数时都很轻易,了解用到的变量。但并非必须这样做不可,因为局部变量可以在任何模块中定义。为了解其工作原理,请看下面函数。

[例4 - 6 ]
f( )
{
    int t;
    scanf("%d", &t);
    if(t == 1) {
        char s[80]; /* 此变量仅在这个块中起作用* /
        printf("enter name:");
        gets(s); /* 输入字符串* /
        process(s); /* 函数调用* /
    }
}
    这里的局部变量s就是在i f块入口处建立,并在其出口处消亡的。因此s仅在i f块中可知,而在其它地方均不可访问,甚至在包含它的函数内部的其它部分也不行。
    在一个条件块内定义局部变量的主要优点是仅在需要时才为之分配内存。这是因为局部变量仅在控制转到它们被定义的块内时才进入生存期。虽然大多数情况下这并不十分重要,但当代码用于专用控制器(如识别数字安全码的车库门控制器)时,这就变得十分重要了,因为这时随机存储器( R A M)极其短缺。
    由于局部变量随着它们被定义的模块的进出口而建立或释放,它们存储的信息在块工作结束后也就丢失了。切记,这点对有关函数的访问非凡重要。当访问一函数时,它的局部变量被建立,当函数返回时,局部变量被销毁。这就是说,局部变量的值不能在两次调用之间保持。

4.2.2 全局变量
    与局部变量不同,全局变量贯穿整个程序,并且可被任何一个模块使用。它们在整个程序执行期间保持有效。全局变量定义在所有函数之外,可由函数内的任何表达式访问。在下面的程序中可以看到,变量count定义在所有函数之,函数main( )之前。但其实它可以放置在任何第一次被使用之前的地方,只要不在函数内就可以。实践表明,定义全局
变量的最佳位置是在程序的顶部。

[例4 - 7 ]
int count; /*count 是全局变量* /
main( )
{
    count = 100;
    func1( );
}
func1( )
{
    int temp;
    temp = count;
    func2( );
    printf("count is %d",count); /* 打印100 */
}
func2( )
{
    int count;
    for(count = 1; count < 10; count++)
    putchar('.'); /* 打印出"。" */
}
    仔细研究此程序后,可见变量count既不是main( )也不是func1( )定义的,但两者都可以使用它。函数func2( )也定义了一个局部变量c o u n t。当f u n c 2访问c o u n t时,它仅访问自己定义的局部变量count,而不是那个全局变量count。切记,全局变量和某一函数的局部变量同名时,该函数对该名的所有访问仅针对局部变量,对全局变量无影响,这是很方便的。然而,假如忘记了这点,即使程序看起来是正确的,也可能导致运行时的奇异行为。
    全局变量由C编译程序在动态区之外的固定存储区域中存储。当程序中多个函数都使用同一数据时,全局变量将是很有效的。然而,由于三种原因,应避免使用不必要的全局变量:
    ①不论是否需要,它们在整个程序执行期间均占有存储空间。
    ②由于全局变量必须依靠外部定义,所以在使用局部变量就可以达到其功能时使用了全局变量,将降低函数的通用性,这是因为它要依靠其本身之外的东西。
    ③大量使用全局变量时,不可知的和不需要的副作用将可能导致程序错误。如在编制大型程序时有一个重要的问题:变量值都有可能在程 序其它地点偶然改变。
    结构化语言的原则之一是代码和数据的分离。C语言是通过局部变量和函数的使用来实现这一分离的。下面用两种方法编制计算两个整数乘积的简单函数mul( )。
通用的专用的
mul(x,y) int x,y;
int x,y; mul( )
{{
    return( x * y ); return( x * y );
}}
    两个函数都是返回变量x和y的积,可通用的或称为参数化版本可用于任意两整数之积,而专用的版本仅能计算全局变量x和y的乘积。

4.2.3 动态存储变量
    从变量的作用域原则出发,我们可以将变量分为全局变量和局部变量;换一个方式,从变量的生存期来分,可将变量分为动态存储变量及静态存储变量。
    动态存储变量可以是函数的形式参数、局部变量、函数调用时的现场保护和返回地址。这些动态存储变量在函数调用时分配存储空间,函数结束时释放存储空间。动态存储变量的定义形式为在变量定义的前面加上要害字“ auto”,例如:
auto int a, b, c;
“auto”也可以省略不写。事实上,我们已经使用的变量均为省略了要害字“ auto”的动态存储变量。有时我们甚至为了提高速度,将局部的动态存储变量定义为寄存器型的变量,定义的形式为在变量的前面加要害字“ register”,例如:
    register int x, y, z;
    这样一来的好处是:将变量的值无需存入内存,而只需保存在C P U内的寄存器中,以使速度大大提高。由于C P U内的寄存器数量是有限的,不可能为某个变量长期占用。因此,一些操作系统对寄存器的使用做了数量的限制。或多或少,或根本不提供,用自动变量来替代。

4.2.4 静态存储变量
在编译时分配存储空间的变量称为静态存储变量,其定义形式为在变量定义的前面加上要害字“static”,例如:
    static int a=8;
    定义的静态存储变量无论是做全程量或是局部变量,其定义和初始化在程序编译时进行。
    作为局部变量,调用函数结束时,静态存储变量不消失并且保留原值。

[例4 - 8 ]
main( )
{
    inf f( ); /*函数声明* /
    int j;
    for(j=0; j<3; j++)
        printf ("%d\n",f( ));
}
int f( ) /*无参函数* /
{
    static int x=1;
    x ++;
    return x;
}
运行程序:
2 3 4
从上述程序看,函数f( )被三次调用,由于局部变量x是静态存储变量,它是在编译时分配存储空间,故每次调用函数f( )时,变量x不再重新初始化,保留加1后的值,得到上面的输出。

4.3 函数的调用与参数
    假如一个函数要使用参数,它就必须定义接受参数值的变量。

4.3.1 形式参数与实际参数

    函数定义时填入的参数我们称之为形式参数,简称形参,它们同函数内部的局部变量作用相同。形参的定义是在函数名之后和函数开始的花括号之前。
    调用时填入的参数,我们称之为实际参数,简称实参。
    必须确认所定义的形参与调用函数的实际参数类型一致,同时还要保证在调用时形参与实参的个数出现的次序也要一一对应。假如不一致,将产生意料不到的结果。与许多其它高级语言不同,(是健壮的,它总要做一些甚至你不希望的事情,几乎没有运行时错误检查,完全没有范围检测。作为程序员,必须小心行事以保证不发生错误,安全运行。

4.3.2 赋值调用与引用调用
    一般说来,有两种方法可以把参数传递给函数。第一种叫做“赋值调用”(call by value),这种方法是把参数的值复制到函数的形式参数中。这样,函数中的形式参数的任何变化不会影响到调用时所使用的变量。
    把参数传递给函数的第二种方法是“引用调用”(call by reference)。这种方法是把参数的地址复制给形式参数,在函数中,这个地址用来访问调用中所使用的实际参数。这意味着,形式参数的变化会影响调用时所使用的那个变量(具体内容请参见后续章节)。
    除少数情况外,C语言使用赋值调用来传递参数。这意味着,一般不能改变调用时所用变量的值。请看例4 - 9。

[例4 - 9]
main ( )
{
    int t =10;
    printf("%d %d ",sqr(t),t); /* sqr(t)是函数调用,t是实参* /
}
int sqr(x) /* 函数定义,x是形式参数* /
int x;
{
    x = x * x;
    return (x);
}
    在这个例子里,传递给函数sqr( )的参数值是复制给形式参数x的,当赋值语句x = x * x执行时,仅修改局部变量x。用于调用sqr( )的变量t,仍然保持着值10。
执行程序:
100 10
    切记,传给函数的只是参数值的复制品。所有发生在函数内部的变化均无法影响调用时使用的变量。

4.4 递归
    C语言函数可以自我调用。假如函数内部一个语句调用了函数自己,则称这个函数是“递归”。递归是以自身定义的过程。也可称为“循环定义”。
    递归的例子很多。例如定义整数的递归方法是用数字1,2,3,4,5,6,7,8,9加上或减去一个整数。例如,数字1 5是7 + 8;数字2 1是9 + 1 2; 数字1 2是9 + 3。
    一种可递归的计算机语言,它的函数能够自己调用自己。一个简单的例子就是计算整数阶乘的函数factor( )数N的阶乘是1到N之间所有数字的乘积。例如3的阶乘是1×2×3,即是6。
    factor( )和其等效函数fact( )如例4 - 1 0所示。

[例4 - 1 0 ]
factor(n) /* 递归调用方法* /
int n;
{
    int answer;
    if (n==1)
    return (1);
    answer=factor(n-1) * n; /* 函数自身调用* /
    return(answer);
}

[例4 - 11 ]
fact(n) /* 非递归方法* /
int n;
{
    int t,a n s w e r;
    answer = 1;
    for (t=1; t < = n; t ++)
        answer = answer * t;
    return(answer);
}
    非递归函数fact( )的执行应该是易于理解的。它应用一个从1开始到指定数值结束的循环。
    在循环中,用“变化”的乘积依次去乘每个数。
    factor( )的递归执行比fact( )稍复杂。当用参数1调用factor( )时,函数返回1;除此之外的其它值调用将返回factor(n-1) * n这个乘积。为了求出这个表达式的值,用( n - 1)调用factor( )一直到n等于1,调用开始返回。
    计算2的阶乘时对factor( )的首次调用引起了以参数1对factor( )的第二次调用。这次调用返回1,然后被2乘(n的初始值),答案是2(把printf( )语句插入到factor ( )中,察看各级调用及其中间答案,是很有趣的)。
    当函数调用自己时,在栈中为新的局部变量和参数分配内存,函数的代码用这些变量和参数重新运行。递归调用并不是把函数代码重新复制一遍,仅仅参数是新的。当每次递归调用返回时,老的局部变量和参数就从栈中消除,从函数内此次函数调用点重新启动运行。可递归的函数被说成是对自身的“推入和拉出”。
    大部分递归例程没有明显地减少代码规模和节省内存空间。另外,大部分例 程的递归形式比非递归形式运行速度要慢一些。这是因为附加的函数调用增加了时间开销(在许多情况下,速度的差别不太明显)。对函数的多次递归调用可能造成堆栈的溢出。不过溢出的可能性不大,因为函数的参数和局部变量是存放在堆栈中的。每次新的调用就会产生一些变量的复制品。这个堆栈冲掉其它数据和程序的存储区域的可能性是存在的。但是除非递归程序运行失控,否则不必为上述情况担心。
    递归函数的主要优点是可以把算法写的比使用非递归函数时更清楚更简洁,而且某些问题,非凡是与人工智能有关的问题,更适宜用递归方法。递归的另一个优点是,递归函数不会受到怀疑,较非递归函数而言,某些人更相信递归函数。编写递归函数时,必须在函数的某些地方使用i f语句,强迫函数在未执行递归调用前返回。假如不这样做,在调用函数后,它永远不会返回。在递归函数中不使用i f语句,是一个很常见的错误。在开发过程中广泛使用printf( )和getchar( )可以看到执行过程,并且可以在发现错误后停止运行。

4.5 实现问题
    在编写C语言的函数时,有几个要点需要我们牢记,因为它们影响到函数的效率和可用性。

4.5.1 参数和通用函数
    通用函数是指能够被用在各种情况下,或者是可被许多不同程序员使用的函数。我们不应该把通用函数建立在全局变量上(不应该在通用函数中使用全局变量)。函数所需要的所有数据都应该用参数传递(在个别难以这样做的情况下,可以使用静态变量)。使用参数传递,除了有助于函数能用在多种情况下之外,还能提高函数代码的可读性。不用全局变量,可以使得函数减少因副作用而导致错误的可能性。

4.5.2 效率
    函数是C语言的基本构件。对于编写简单程序之外的所有程序来说,函数是必不可少的。但在一些特定的应用中,应当消除函数,而采用内嵌代码。内嵌代码是指一个函数的语句中不含函数调用语句。仅当执行速度是很要害的场合下,才用内嵌代码而不用函数。
    有两个原因使得内嵌代码的执行速度比函数快。首先,调用需要花费时间;其次,假如有参数需要传递,就要把它们放在堆栈中,这也要用时间。在几乎所有的应用中,执行时间上的这些微小开销是微不足道的。不过当时间开销至关重要时,使用内嵌代码消除函数调用,可以把每次函数调用的开销节省下来。下面的两个程序都是打印从1到1 0的数的平方。由于函数调用需要花费时间,所以内嵌代码版本运行的比另一个要快。

内嵌函数调用
main( )                            main( )
{                                  {
    int x;                             int x;
    for(x=1,x < 11;++x)                for(xx=1;x < 11;++x)
    printf ("%d",x * x );             printf ("%d",sqr( x ) );
}                                  }
                                   s q r ( a ) ;
                                  int a;
                                   {
                                       return a*a;
                                   }


4.6 函数库和文件
    一个函数设计完后,我们可以用三种方法处理它:
1) 把它放在main( )函数的同一个文件中;
2) 把它和写好的其它函数一起放在另一个文件中;
3) 把它放在函数库中。下面分别讨论这三种方法。

4.6.1 程序文件的大小
    因为C语言答应分别编译,很自然就会提出这样的问题:一个文件的最适宜的规模是多大?这规模很重要,因为编译时间与被编译文件的大小直接相关。一般说来,链接处理的时间比编译处理的时间短得多,且不需要经常去重新编译已经运行过的代码;另一方面,不得不同时处理多个文件也确实是件厌烦的事。
    问题的答案是,每个用户、每个编译程序、每个操作系统环境都是不同的。可是对大部分微型机和一般的C编译程序来说。源程序文件不应长于10000个字节,建立短于5000个字节的文件,可以避免不少麻烦。

4.6.2 分类组织文件
    在开发一个大型程序时,最令人烦恼的而又是最常碰到的工作之一就是需要检查每个文件,以确定某个函数的存放。在程序开发的早期做一点文件组织工作就可以避免这一问题。
    首先可以把概念上有关的函数组织到一个文件中。假如在编写正文编辑程序时,把删除正文所用的所有函数放进另一个文件,等等。
    第二,把所有的通用函数放在一起。例如,在数据库程序中,输入/输出格式编排函数是被其它函数调用的通用函数,应把它们放进一个单独的文件里。
    第三,把最高层函数放进一个单独的文件中,假如空间答应,就和main ( )放在一起。最高层函数被用来启动程序的总体活动。这些例程从本质上定义了程序的操作。

4.6.3 函数库
    从技术上讲,函数库与分别编译的函数文件不同。当库中例程被链接到程序中,或当使用一个分别编译的文件时,文件中的所有函数都被装入和链接到程序中去。对自己创建的函数文件中的大多数文件来说,文件中所有的函数都是要用到的。而对C的标准函数库,永远也无法把所有的函数都连接到自己的程序中去,因为目的码会大得吓人!
有时候我们需要建立一个函数库,例如,假定已经完成了一套专门的统计函数,假如当
    前开发的某个程序仅仅需要求出一批数值的均值,我们就不必把这些函数全部装入。在这种情况下,函数库是很有用的。
    大部分C语言的编译程序都有建立函数库的指令。操作过程因编译程序不同而异,可从用户手册中寻找建库的具体步骤。

4.7 C语言的预处理程序与注释
    C程序的源代码中可包括各种编译指令,这些指令称为预处理命令。虽然它们实际上不是C语言的一部分,但却扩展了C程序设计的环境。本节将介绍如何应用预处理程序和注释简化程序开发过程,并提高程序的可读性。

4.7.1 C语言的预处理程序
    ANSI 标准定义的C语言预处理程序包括下列命令:
    #define
    #error
    #include
    #if
    #else
    #elif
    #endif
    #ifdef
    #ifndef
    #undef
    #line
    #pragma
    非常明显,所有预处理命令均以符号#开头,下面分别加以介绍。

4.7.2 #define
    命令#define定义了一个标识符及一个串。在源程序中每次碰到该标识符时,均以定义的串代换它。ANSI标准将标识符定义为宏名,将替换过程称为宏替换。命令的一般形式为:
    #define identifier string
    注重,该语句没有分号。在标识符和串之间可以有任意个空格,串一旦开始,仅由一新行结束。
例如,如希望TURE取值1,FALSE 取值0,可说明两个宏#define
    #define TURE 1
    #define FALSE 0
    这使得在源程序中每次碰到T U R E或FA L S E就用0或1代替。
    例如,在屏幕上打印“ 0 1 2 ”:
    printf("%d %d %d",FALSE,TRUE,TRUE + 1 );
    宏名定义后,即可成为其它宏名定义中的一部分。例如,下面代码定义了ONE、TWO及THREE的值。
    #define ONE   1
    #define TWO   ONE + ONE
    #define THRE E ONE + TWO
    懂得宏替换仅仅是以串代替标识符这点很重要。因此,假如希望定义一个标准错误信息,可编写如下代码:
    #define E_MS "standard error on input\n"
    printf(E_MS);
    编译程序碰到标识符E_MS时,就用“standard error on input\n”替换。对于编译程序,printf( )语句实际是如下形式:
    printf("standard error on input\n") ;
    假如在串中含有标识符,则不进行替换。例如:
    #define XYZ this is a test
    . . .
    printf("XYZ");
    该段不打印"this is a test"而打印"XYZ"。
    假如串长于一行,可以在该行末尾用一反斜杠续行,例如:
    #define LONG_STRING  "this is a very long string that is used as an example"
    C语言程序普遍使用大写字母定义标识符。这种约定可使人读程序时很快发现哪里有宏替换。最好是将所有的
#define放到文件的开始处或独立的文件中(用#include访问),而不是将它们分散到整个程序中。
    宏代换的最一般用途是定义常量的名字和程序中的“游戏数”。例如,某一程序定义了一个数组,而它的几个子程序要访问该数组,不应直接以常量定数组大小,最好是用名字定义之(需改变数组大小时)。
    #define MAX_SIZE 100
    float balance[MAX_SIZE] ;
    #define命令的另一个有用特性是,宏名可以取参量。每次碰到宏名时,与之相连的形参均由程序中的实参代替。   
例如:
[例4 - 1 2 ]
#define MIN(a,b) (a<b) ? a : b
main( )
{   
    int x, y;
    x = 10;
    y = 20;
    printf("the minimum is: %d" ,MIN( x ,y ) );
}
当编译该程序时,由MIN(a,b )定义的表达式被替换, x和y用作操作数,即printf( )语句被代换后取如下形式:
    printf("the minimum is: %d" ,(x<y) ? x : y);
    用宏代换代替实在的函数的一大好处是宏替换增加了代码的速度,因为不存在函数调用的开销。但增加速度也有代价:由于重复编码而增加了程序长度。

视频教程列表
文章教程搜索
 
C语言程序设计推荐教程
C语言程序设计热门教程
看全部视频教程
购买方式/价格
购买视频教程: 咨询客服
tel:15972130058