第七章:函数

简介: 第七章:函数

 一个C语言程序由一个或多个程序模块组成,每一个程序模块由一个或多个函数组成,所以函数是实现模块化程序设计不可缺少的重要机制。

7.1 函数示例

有5个整数,输出其中的最大值。

#include<stdio.h>
int main() {
  int a, b, c, d, e, max;
  a = 10; b = 12; c = 9; d = 13; e = 5;
  max = a;
  if (b > max)max = b;
  if (c > max)max = c;
  if (d > max)max = d;
  if (e > max)max = e;
  printf("Maximum:%d", max);
  return 0;
}

运行结果

00b74057e5df40a88f72cde839e2d5fe.png

使用函数的方法完成以上代码的完成例。

#include<stdio.h>
int main()
{
  int a, b, c, d, e, m;
  int max(int x, int y);
  a = 10; b = 12; c = 9; d = 13; e = 5;
  m = max(a, max(b, max(c, max(d, e))));
  printf("Maximum:%d", m);
  return 0;
}
int max(int x, int y)
{
  int z;
  if (x > y)
  z = x;
  else
  z = y;
  return z;
}

运行结果

ff93d76c71b9446db64c246829266a14.png

从以上两个代码可以看出,前者几乎相同的分支语句重复了4次,而后者重复的分支语句到max中,先后调用4次该函数求最大值。很明显使用函数使程序结构清晰,并可以简化程序。一个函数被调用的次数越多,越能体现该函数的作用。

7.2 函数概述

       函数使C语言的基本组成单元。所有C语言程序都是由一个或多个函数构成的。当一个C语言程序的规模很小时,可以用一个源文件来实现。主函数可以调用其他函数,反之则不行。其他函数之间可以互相调用,同一个函数可以被一个或多个函数调用任意多次。一个函数调用另一个函数,前者称为调用函数;后者称为被调用函数。


   从用户使用的角度来看,函数分为标准函数和用户自定义函数。


(1)标准函数。标准函数即库函数。C语言系统提供了大量已设计好的常用函数,用户可直接调用。主要包括:数学函数,如求实数的绝对值函数fabs()、平方根函数sqrt()等;输入输出函数,如前面已经使用法国的输入函数scanf()、输出函数printf()等。应该说明的是,不同的C语言系统提供库函数的数量和功能可能有些差距,但一些基本的库函数是相同的。


(2)用户自定义函数。用户自定义函数是指由用户根据具体问题而自己定义的函数,功能上相当于其他语言中的过程,用于实现用户的专门功能。


   从函数的形式上看,函数分为无参函数和有参函数。


(1)无参函数。在调用这类函数时,调用函数没有数据需要传送给被调用函数。无参函数一般用来执行一组指定的操作,可以返回或不返回函数值,大多数情况下不返回函数值。


(2)有参函数。有参函数是指调用函数和被调用函数之间有数据传递。也就是说,调用函数可以将数据传递给被调用函数使用;调用结束后被调用函数中的数据也可以返回,供调用函数使用。


   从函数作用范围来看,函数可以分为外部函数和内部函数。


(1)外部函数。函数在本质上都具有外部性质,除了内部函数之外,其余的函数都可以被同一程序中其他源文件的函数所调用。


(2)内部函数。内部函数只限于本文件中的函数调用,而不允许其他文件中的函数调用。


注意:每个函数必须单独定义,不允许嵌套定义,即不能在一个函数的内部再定义另一个函数。用户自定义函数不能单独运行,可以被main函数或其他函数所调用,也可以调用其他函数。所以函数均不能调用main函数。

7.3 函数定义

   C语言的库函数是由编译系统事先定义好的,用户在使用时无需自己定义,只需用#include命令将有关的头文件包含到文件中即可。所有的用户自定义函数均要“先定义,后使用”。定义的目的是通知编译系统函数返回值的类型、函数名字、函数的参数个数与类型及函数实现什么功能等。

7.3.1 无参函数的定义

    无参函数的定义形式如下:

类型名  函数名()                     类型名  函数名()
{                                  或             {
函数体                                          函数体
}                                                   }

   说明:类型名指定函数返回值的类型,省略时,默认函数返回值的类型为int。void表示函数没有参数。函数体包含声明部分和语句部分。声明部分主要是变量的声明或所调用函数的声明;执行部分由执行语句组成,函数的功能正是由这些语句实现的。函数可以既有声明部分又有语句部分,也可以只有语句部分,还可以两个皆无(空函数)。调用空函数不会产生任何有效操作。


一般情况下无参函数不会有返回值,此时函数类型名为void。


注意,函数首部的后面不能加分号,它和函数一起构成完整的定义。


定义一个无参且无返回值的 函数prtstar()。

#include<stdio.h>
void prtstar(void)
{
  printf("* * * * * * * * * * * * * * * * * *\n");
}
int main()
{
  prtstar();
  printf("   Welcome to C language world!\n");
  prtstar();
  return 0;
}

运行结果

08b42ddbc03e448dac8376fd74977f8c.png

    以上代码中的函数prtstar是一个无参、无返回值的函数,两次调用该函数分别在屏幕上输出行星号。函数类型为void,表示不返回值。void类型的函数不直接返回值,其作用通常是完成某一特定功能。

7.3.2 有参函数的定义

   有参函数的定义形式如下:

类型名  函数名(形式参数表列)
{
函数体
}

说明:


(1)有参函数比无参函数多一项内容,即形式参数表列。在其中给出的参数称为形式参数(简称形参),它们可以是各种类型的变量,各参数之间用逗号分隔。在进行函数调用时,调用函数将赋予这些形参实际的值。形式参数列表的格式如下:

类型1形参1,类型2形参2,...,类型n形参n

(2)函数体包括一对大括号内的若干条语句,这些语句实现了函数的功能,并用return语句返回运算的结果。


(3)编译系统不检查函数名与形参变量名是否相同,但在实际编程时,应避免这样使用,不要人为造成混淆。


    输入一个实数x,计算并输出下式的值,直到最后一项的绝对值小于10^-5(保留两位小数)。要求定义和调用函数fact(n),计算n的阶乘,可以调用pow()函数求幂。

#include<stdio.h>
#include<math.h>
double fact(int n);
int main()
{
  int i = 1;
  double x, item, s = 0;
  printf("Enter a value for X:");
  scanf("%lf", &x);
  item = x;
  while (fabs(item) > 0.00001)
  {
  s = s + item;
  i++;
  item = pow(x, i) / fact(i);
  }
  printf("And yes:%lf\n", s);
  return 0;
}
  double fact(int n)
  {
  int i;
  double jc = 1;
  for (i = 1; i <= n; i++)
    jc = jc * i;
  return jc;
  }

运行结果

ffced8856e7c42c4919590e1aace487b.png

7.3.3 空函数的定义

空函数的定义形式如下:

类型名  函数名( )
{  }

   函数体是空的,调用此函数时,什么动作都不做,在调用程序中写出fun(),表明这里要调用一个fun函数,而这个函数还没有完成,等待以后完善。在程序设计中可根据需要确定若干个模块,分别由不同的函数来实现。而在最初阶段可只实现最基本的模块,其他的模块等待以后完成。这些未编写的函数先占一个位置,表明以后在此要调用此函数完成相应的功能。这样写的目的程序的结构清晰,可读性好,以后扩充功能方便。

7.4 函数调用

   定义函数的目的就是为了重复使用,因此只有在程序中调用函数才能实现函数的功能,C语言程序从main函数开始执行,而自定义函数的执行是通过对自定义函数的调用来实现的。

7.4.1 函数调用的形式和过程

1、函数调用的一般形式


函数名(实际参数表列)


   实际参数表列中的参数称为实际参数(简称实参),它们可以是常数、变量和表达式。各实参之间用逗号分隔,并且实参的个数、类型应该与函数形参的个数、类型一致。如果为无参函数,则无实际参数表列,但括号不能省略。


2、函数调用的方式


(1)函数语句。把函数调用作为一条语句。一般形式为


函数名(实参表列)


   这种方式用于调用一个没有返回值的函数,只要求函数一定的操作。


(2)函数表达式。函数调用作为表达式中的一部分出现在表达式中,以函数返回值参与表达式的运算。这种方式要求函数是有返回值的。


(3)函数嵌套调用。C语言的函数定义时互相平行、独立的,也就是说,在定义函数时,一个函数内不能再定义另一个函数,也就是不能嵌套定义,但可以嵌套调用函数。即一个函数调用另一个函数的参数,或一个函数的函数体中又调用另一个函数。


3、函数调用的过程


1、为被调用的所有形参分配内存,再计算实参的值,并一一对应地赋予相应的形参(对于无参函数,不做该项工作)。


2、为函数说明部分中定义的变量分配储存空间,再依次执行函数的可执行语句。当执行到“return(表达式);”语句时,计算返回值(如果是无返回值的函数,不做该项工作)。


3、释放在本函数中定义的变量所占的存储空间(对于static类型的变量,其空间不会释放),返回主调函数继续执行。


求两个正整数m和n的最大公约数

#include<stdio.h>
int main()
{
  int a, b, c;
  int gcd(int m, int n);
  printf("Please enter 2 integers:");
  scanf("%d%d", &a, &b);
  c = gcd(a, b);
  printf("Integer %d and %d the greatest common divisor is:%d\n", a, b, c);
}
int gcd(int m, int n)
{
  int r;
  r = m % n;
  while (r != 0)
  {
  m = n;
  n = r;
  r = m % n;
  }
  return n;
}

运行结果

876abdf4f98d4bd394f954bf7294e8b1.png

7.4.2 参数传递

       在调用有参函数时,调用函数与被调用函数之间存在数据传递的关系。调用函数向被调用函数传递数据主要是通过函数参数进行的,而被调用函数向调用函数传递数据一般是通过return语句实现的。


       形参是函数定义时函数后括号中的变量;实参是指调用函数时函数名后括号中的常量、变量或表达式。在调用函数时,将实参的值传递给形参,使形参在数值上和实参相同。


       C语言提供了两种参数传递数据方式:按值传递和按地址传递。


(1)按值传递。函数调用时,调用函数把实参的值传递给被调用函数的形参形参值得变化不会影响实参的值。这是一种单向的数据传递方式.


当实参是变量、常量、表达式或数组元素。形参是变量名时,函数传递数据采用的是按值传递。


阅读程序

#include<stdio.h>
void swap(int x, int y)
{
  int t;
  t = x; x = y; y = t;
  printf("x=%d,y=%d\n", x, y);
}
int main()
{
  int a, b;
  scanf("%d,%d", &a, &b);
  swap(a, b);
  printf("a=%d,b=%d\n", a, b);
  return 0;
}

运行结果

3b5743673edb43c18fca03f910e87220.png

注意:1、实参的个数、数据类型应与形参一致,否则将会出现编译错误。


2、定义函数时,系统并不给形参分配存储单元,只有函数被调用时系统才给形参分配存储单元。在调用结果后,形参所占用的存储单元被释放。


3、实参与形参即使同名,也会分配不同的存储单元。


4、参数传递是“值传递”时,属于单项传递,即实参把值传给形参,但形参的值不能传递给实参,也就是说对形参的修改不会影响对于的实参。这是由于在内存中,实参与形参是不同的存储单元,函数执行结束后,形参的存储单元被释放。


(2)按地址传递。当函数的形参为数字或指针类型时,函数调用的参数传递称为按地址传递。由于传递的是地址,使形参与实参共享相同的存储单元,这样通过形参可以直接引用或处理该地址中的数据,达到改变实参的目的。

7.4.3 函数的返回值

   函数的返回值是指函数被调用之和,执行函数体中的语句所取得并返回给调用函数的值。


(1)函数的值只能通过return语句返回给调用函数。return语句的一般形式为:

return  表达式;
return(表达式);

该语句的功能是计算表达式的值,并返回给调用函数。在函数中允许有多个return语句,但每次调用逻辑上只能有一个return语句被执行。


(2)如果不需要从被调用函数返回函数值,可以省略return语句。


(3)return语句中表达式值得类型应与函数定义中函数的类型保持一致。


(4)对不需要返回函数值得函数,可以将其函数类型定义为“void”(即“空类型”)。对类型说明为void的函数,在其函数体中不能使用return语句。否则,会产生编译错误。

7.4.4 函数声明

       在C语言程序中,一个函数的定义可以放在任意位置,当然是相对其他函数而言的。同变量一样,函数的调用也遵循“先声明,后使用”的原则。


(1)调用库函数时,一般需要在程序的开头用"#include"命令。


(2)调用用户自己定义的函数,而该函数与调用函数在同一个程序中,被调用函数的位置在调用函数的后面时,应该在调用函数中对被调用函数进行声明,即向编译系统声明将要调用哪些函数,并将有关信息通知编译系统。


   函数声明的格式为:

类型名  函数名(参数类型1,参数类型2,......);
类型名  函数名(参数类型1形参1,参数类型2形参2,......);

   当满足下面两个情况之一时,在主调函数中可以不对被调用函数进行声明。


1、被调函数定义在主调函数之前时,对被调用函数的声明可以省去。


2、被调函数的返回值类型是整型或字符型时,对被调用函数的声明可以省去。

7.4.5 函数的嵌套调用

      C语言的函数定义都是互相平行,独立的。也就是说,在定义函数时,一个函数内不能包含另一个函数的定义。如果在一个函数中再调用其他的函数,则称为函数的嵌套调用。也就是说,在被调用函数的过程中,又可以调用另一个函数。


输入n个正整数,将它们从小到大排序后输出。

#include<stdio.h>
void input(int a[], int n);
void choose(int a[], int n);
void print(int a[], int n);
int main(void)
{
  int n, a[10];
  printf("Enter the value of variable n:(n<=10):");
  scanf("%d", &n);
  printf("Enter %d array elements:", n);
  input(a, n);
  choose(a, n);
  printf("After sorting:",n);
  print(a, n);
  printf("\n");
  return 0;
}
void input(int a[], int n)
{
  int i;
  for (i = 0; i < n; i++)
  scanf("%d", &a[i]);
}
void choose(int a[], int n)
{
  int i, j, k, t;
  for (i = 0; i < n - 1; i++)
  {
  k = i;
  for (j = i + 1; j < n; j++)
    if (a[j] < a[k])k = j;
  t = a[i], a[i] = a[k], a[k] = t;
  }
}
void print(int a[], int n)
{
  int i;
  for (i = 0; i < n; i++)
  printf("%3d", a[i]);
}

运行结果

ded03d2041974cf6a0e7ab07d9842f95.png

以下程序中包括1个main()函数和3个自定义函数prtadd(),prtstarline()和prtstars(),在自定义函数add()中调用prtstarline()函数,在prtstarline函数中调用prtstars()函数。

#include<stdio.h>
void prtstars(int);
void prtstarline();
void prtadd(int x, int y)
{
  prtstarline();
  printf("  sum=%d\n", x + y);
  prtstarline();
}
void prtstars(int n)
{
  int i;
  for (i = 0; i < n; i++)printf("*");
}
void prtstarline()
{
  prtstars(10);
  printf("\n");
}
int main()
{
  int a = 42, b = 16;
  prtadd(a, b);
  return 0;
}

运行结果

abf98a8288a4411da027e7b6c70cad4a.png

分析下面程序函数的调用关系。

#include<stdio.h>
int fun2()
{
  int k = 10;
  printf("%d\n", k);
  return (10 * k);
}
int fun1()
{
  int b;
  b = fun2();
  printf("%d\n", b);
  return(10 * b);
}
int main()
{
  int a;
  a = fun1();
  printf("%d\n", a);
  return 0;
}

运行结果

34bc39a7433b422d867db4fb80dd5940.png

7.5 递归函数

   在C语言中直接或者间接调用自己,这种自己调用自己的形式称为函数的递归调用,带有递归调用的函数称为递归函数。


用递归调用的方法求n!。

#include<stdio.h>
long fac(int n)
{
  int k;
  if (n == 0 || n == 1)
  k = 1;
  else
  k = n * fac(n - 1);
  return k;
}
int main()
{
  int n;
  scanf("%d", &n);
  if (n < 0)
  printf("Input error!\n");
  else
  printf("%d!=%d\n", n, fac(n));
  return 0;
}

运行结果

772df44dfbc14c279a5ccac397d4819a.png

7.5.1 递归函数的概念

   C语言中的递归函数有两种形式。如果函数直接调用它自身,称为直接递归调用;如果函数通过其他函数间接来调用它自身,则称为间接递归调用。无论哪种递归条件,不能无限递归调用下去。


一个问题采用递归调用时要满足两个条件:


(1)要解决的问题能简化为一个新问题,而这个新问题的解决方法仍与原来的解决方法相同,知识所处理的问题进一步简化。归结为:寻找递归表达式。


(2)必定要有一个明确的结果递归的条件,一定要能够在适当的时候结果递归调用。归结为:寻找递归的出口。


递归调用过程分为两个阶段:


(1)递推阶段。将原来不断分解为新的子问题,逐渐从未知向已知的方向推进,最终到达已知的条件。即递归结束条件,这时递推阶段结束。


(2)回归阶段。从已知条件出发,按照递推的逆过程,逐一求值回归,最终到达递推的开始出,结束回归阶段,完成递归调用。


   递归函数的结构清晰,可读性强,并且容易用数学归纳法证明算法的正确性。递归函数为算法的设计带来了很大方便。但是,递归函数的运行效率比较低,时间效率和空间效率都不高。

7.5.2 递归函数程序设计

用递归函数输出斐波拉契数列的第n项。

#include<stdio.h>
long fibo(int);
int main()
{
  int n, f;
  scanf("%d", &n);
  f = fibo(n);
  printf("%d\n", f);
  return 0;
}
long fibo(int n)
{
  int f;
  if (n == 1 || n == 2)
  f = 1;
  else
  f = fibo(n - 1) + fibo(n - 2);
  return f;
}

运行结果

27193abf8408416fa39e05c1009120ba.png

7.6 数组做函数的参数

   数组可以作为函数的参数使用,完成函数间的数据传送。数组作函数参数有两种形式:一是把数组元素作为实参使用;二是把数组名作为函数的形参和实参使用。

7.6.1 数组元素作函数实参

判别一个由3位整数组成的数组中的升序数。输出这些升序数。

#include<stdio.h>
int sx(int x)
{
  int a, b, c;
  a = x / 100;
  b = x / 10 % 10;
  c = x % 10;
  if (a < b && b < c)
  return 1;
  else
  return 0;
}
int main()
{
  int a[10] = { 213,345,224,258,367,412,736,379,423,456 }, i;
  for (i = 0; i < 10; i++)
  if (sx(a[i]))
    printf("%d ", a[i]);
  return 0;
}

运行结果

a7b6b5ed31c44193b8c1994f83227080.png

7.6.2 数组作函数参数

1、一维数组作函数参数


数组a中存放了10个正整数,要求调用函数将数组a中的数重新排列。排列的方法是将偶数放在数组的左部,奇数放在数组的右部。

#include<stdio.h>
void judge(int b[], int n)
{
  int i, j, k, temp;
  for (i = 0, j = n - 1; i < j;)
  {
  if (b[i] % 2 != 0)
  {
    temp = b[i];
    for (k = i; k < j; k++)
    b[k] = b[k + 1];
    b[j] = temp;
    j--;
  }
  else
    i++;
  }
}
int main()
{
  int a[10] = { 21,34,224,25,367,41,736,37,42,456 }, i;
  for (i = 0; i < 10; i++)
  printf("%d ", a[i]);
  judge(a, 10);
  printf("\n");
  for (i = 0; i < 10; i++)
  printf("%d ", a[i]);
  return 0;
}

运行结果

eb49e58dc66642e094ff3816df156ad6.png

2、二维数组作函数参数


求一个二维数组所有元素的最大值。

#include<stdio.h>
int max_val(int a[][4]);
int main()
{
  int a[3][4] = { {3,5,2,6},{9,4,1,5},{12,7,9,2} };
  printf("Maximum element is:%d", max_val(a));
  return 0;
}
int max_val(int b[][4])
{
  int i, j, max;
  max = b[0][0];
  for (i = 0; i < 3; i++)
  for (j = 0; j < 4; j++)
    if (b[i][j] > max)
    max = b[i][j];
  return max;
}

运行结果

12506eda60af4062acad683c33c48b25.png

7.7 局部变量与全局变量

   变量的作用域就是变量的有效范围。C语言只允许在3个地方定义变量。


1、函数内部的申明部分。


2、所有函数的外部。


3、复合语句中的声明部分。


   变量定义的位置不同,其作用域也不同。从变量的作用域来分,可以将其分为局部变量和全局变量。

7.7.1 局部变量

   在一个函数内部定义的变量是局部变量,其作用域仅限于该函数内,它只在本函数范围内有效。C语言规定在复合语句内也可以定义变量,其作用域仅限于该复合语句中。


   对局部变量注意如下几点:


1、不同的函数或复合语句中可使用同名的变量,但它们不是同一变量,它们在内存中占不同的单元。


2、主函数main()中定义的变量只在主函数中有效。


3、形参也是局部变量。


4、在一个函数内部可在复合语句中定义变量,但变量只在本复合语句中有效。


5、编译系统不检查函数名与局部变量是否同名。

#include<stdio.h>
int f(int x)
{
  if (x == 4)
  {
  int x = 6;
  return x;
  }
  else
  return x;
}
int main()
{
  int y;
  y = f(4);
  printf("%d,%d", y, f(3));
  return 0;
}

运行结果

3f96fa818dbd458fa84bf5b4b2b1f10d.png

7.7.2 全局变量

   C语言规定在函数之外也可以定义变量,这样的变量称为全局变量(也称外部变量)。在同一源程序文件中的函数可共用全局变量。


   全局变量的有效范围为从定义全局变量的位置到该源文件结束。若在全局变量定义处之前的函数想引用该全局变量,则需要在该函数中用关键字extern作外部变量声明。


   对全局变量注意如下几点:


1、全局变量的定义:同一作用域范围内只能定义一次,定义的位置在所有函数之外,系统根据全局变量的定义分配存储单元。若全局变量需要初始化则则应在定义时进行。


2、外部变量的声明:用于说明该变量是一个已在外部定义国的变量,则要在本函数中使用这个变量。可在不同函数中声明多次。


3、如果全局变量与局部变量同名,则在局部变量的作用范围内,全局变量不起作用(程序对变量的引用遵守最小作用域原则)。


4、由于全局变量可以被多个函数直接引用,因此全局变量为函数间进行数据传递的渠道之一。


全局变量定义示例。

#include<stdio.h>
void num()
{
  extern int x, y;
  int a = 8, b = 7;
  x += a - b;
  y -= a + b;
}
int x, y;
int main()
{
  int a = 7, b = 8;
  x = a + b;
  y = a - b;
  num();
  printf("%d,%d\n", x, y);
  return 0;
}

运行结果

27e05edc37384c7aaaa3d7bfb3b89f43.png

      使用全局变量尽管有一定的作用,比如全局变量可以供本程序其他函数所共享,使用全局变量可以从函数得到一个以上的返回值等等。但是,全局变量的副作用也很明显,比如会破坏程序模块的独立性,降低函数的通用性,在不同的函数中都可能改变全局变量的值,程序容易出错,所以在程序设计中应尽量少用全局变量。

7.8 变量生命期与存储类型

   变量从定义开始分配存储单元到运行结束存储单元被回收,整个过程称为变量生命期。影响变量生命期的是变量的存储类型,也就是说变量的存储类型不同其生命期也是不同的。

7.8.1 静态存储与动态存储

C语言的数据区分为动态存储区和静态存区。


从变量的生命周期来分,可以将变量的存储类型分为静态存储方式和动态存储方式。


1、静态存储方式是指在程序运行期间分配固定的存储空间方式。


2、动态存储方式是指在程序运行期间根据需要进行动态地分配存储空间的方式。


静态存储区用于存放全局变量和静态变量;动态存储区则用于存放函数的形参、自动变量,变量存放在何处决定了变量的生命期。

7.8.2 变量的存储类型

用变量的存储类型说明来确定变量的存放位置。


带有存储类型的变量定义的一般形式为:


存储类型  数据类型  变量名;


在C语言中,变量的存储类型有以下4种。


1、auto(自动类型)。


定义自动变量时,auto可以省略。省略auto后的格式与先前使用的变量的定义格式相同,所以以前用到的变量都是自动变量。


2、register(寄存器类型)。


此变量为自动变量,它与auto的型变量的区别在于:register变量的值存放在寄存器中而不是在内存中。常把一些对运行速度有较高要求、需要频繁引用的变量定义为register型。register说明符是过时的说明符。目前大多数编译器都可以做到程序优化,程序根据优化结果自动决定是此变量。


3、static(静态类型)。


此变量的存储单元被分配在数据区的静态存储区中。


对静态局部变量和自动变量注意以下几点:


(1)静态局部变量在编译时赋初值,即只赋一次初值;而对自动变量赋初值是在函数调用时进行,每调用一次函数重新赋一次初值。


(2)自动变量如果没有赋初值,其初值是不确定;若在定义静态局部变量时不赋初值,编译时系统自动赋初值0。并且赋初值只在函数第一次调用时进行。以后调用时其值为前一次调用保留的值。


(3)静态变量和全局变量一样,属于变量的特殊用法,若没有静态保存的要求,不建议使用静态变量。


4、external(外部类型)。


extern只能声明已经存在的变量,而不能定义变量。外部变量的作用域为从变量的定义处开始到本程序的结束,在此作用域内,外部变量可以为程序中各个函数所使用。


如果在定义点之前的函数想引用全局变量,则应在引用之前用关键字external对该变量声明,表示该变量是一个已经定义的外部变量。有了此声明,即可合法的使用该变量。

7.9 内部函数与外部函数

与变量有局部变量和外部变量一样,函数也分为内部函数和外部函数。


1、内部函数


如果一个函数只能被本文件中其他函数所调用,该函数称为内部函数。其定义格式为:


static  类型名  函数名(形参表);


       此函数也可称为静态函数。这样定义的好处是:函数的作用域只局限于所在文件,在又多人编写不同的程序模块时,不同担心自己所用的函数名与别人使用的是否相同,即使函数名相同,也不会产生干扰。


2、外部函数


如果一个函数允许被其他文件的函数所调用,该函数称为外部函数,其定义格式为:


extern  类型名  函数名(形参表);


外部函数在整个源程序中都有效。事实上C语言规定,如果在定义函数时省略extern,则默认为外部函数,在前面章节中所用的函数都是外部函数。


外部函数定义后,要在需要调节此函数的程序文件中用extern声明所调用函数的原型,表示该函数已在其他文件中被定义。

7.10 函数程序设计示例

以下程序输出100-1000的范围内的回文素数

#include<stdio.h>
#include<math.h>
int prime_pal(int n)
{
  int i, k = sqrt(n), m;
  for (i = 2; i <= k; i++)
  if (n % i == 0)return 0;
  k = n; m = 0;
  while (k > 0)
  {
  m = m * 10 + k % 10;
  k = k / 10;
  }
  if (m == n)return 1;
  return 0;
}
int main()
{
  int j, k = 0;
  for (j = 100; j <= 999; j++)
  {
  if (prime_pal(j))
  {
    printf("%d\t", j);
    if (++k % 5 == 0)printf("\n");
  }
  }
}

运行结果

6c042c3461f847e7a92cf37be3499da4.png

根据式子计算cos(x)的近似值,精度要求:当通项的绝对值小于等于10的-6次方时为止。

#include<stdio.h>
#include<math.h>
double mycos(double x)
{
  int n = 1;
  double sum = 0, term = 1.0;
  while (fabs(term) >= 1e-6)
  {
  sum += term;
  term *= -pow(x, 2) / ((2 * n - 1) * (2 * n));
  n = n + 1;
  }
  return sum;
}
int main()
{
  double x;
  scanf("%lf", &x);
  printf("fx(%f)=%f,%f\n", x, mycos(x), cos(x));
  return 0;
}

运行结果

bae615467ba64e14a0571cc22ed3fc73.png

目录
相关文章
|
6月前
|
安全 算法 编译器
【C++ 泛型编程 进阶篇】深入探究C++模板参数推导:从基础到高级
【C++ 泛型编程 进阶篇】深入探究C++模板参数推导:从基础到高级
731 3
|
2月前
|
存储 C语言
C语言程序设计核心详解 第七章 函数和预编译命令
本章介绍C语言中的函数定义与使用,以及预编译命令。主要内容包括函数的定义格式、调用方式和示例分析。C程序结构分为`main()`单框架或多子函数框架。函数不能嵌套定义但可互相调用。变量具有类型、作用范围和存储类别三种属性,其中作用范围分为局部和全局。预编译命令包括文件包含和宏定义,宏定义分为无参和带参两种形式。此外,还介绍了变量的存储类别及其特点。通过实例详细解析了函数调用过程及宏定义的应用。
|
5月前
|
编译器 vr&ar C++
c++primer plus 6 读书笔记 第七章 函数--C++的编程模块
c++primer plus 6 读书笔记 第七章 函数--C++的编程模块
|
6月前
第七章 用函数实现模块化程序设计
第七章 用函数实现模块化程序设计
22 0
|
6月前
|
存储 分布式计算 运维
第五章 Python函数你知多少
第五章 Python函数你知多少
|
6月前
|
C++
第三章:C++中的函数
第三章:C++中的函数
36 1
|
编译器 C语言 C++
《C和指针》读书笔记(第七章 函数)
《C和指针》读书笔记(第七章 函数)
|
C++
C++ Primer Plus 第七章答案 函数——C++的编程模块
只有聪明人才能看见的摘要~( ̄▽ ̄~)~
83 0
|
搜索推荐 算法 编译器
Python编程基础:实验5——函数定义与调用
有关Python函数定义与调用的一些题目练习。
375 0
Python编程基础:实验5——函数定义与调用