C语言总结(第7章 用函数实现模块化程序设计)

简介: 为什么要用函数为了更好的实现模块化程序设计 函数(function)也有功能的意思

7.1为什么要用函数


为了更好的实现模块化程序设计    函数(function)也有功能的意思


7.2怎样定义函数


为什么要定义函数?


事先编辑好的功能,编译系统按照定义的功能去执行


定义函数应包括以下内容:


指定函数的名字,以便以后按名调用

指定函数的类型,即函数返回值的类型

指定函数的参数的名字和类型,以便在调用函数时向它们传递数据。对无参函数不需要这项

指定函数应该完成什么操作,也就是函数是做什么工作的,即函数的功能


定义函数的方法:

定义无参函数的方法:

函数名()

函数体

或者

函数名(void)

函数体

函数体内包含声明部分和语句部分

定义有参函数

函数程序设计

编写一个函数,利用参数传入一个3位数number,找出101~number之间所有满足下列两个条件的数:

它是完全平方数,又有两位数字相同,如144、676等,函数返回找出这样的数据的个数。请同时编写主函数。

例:(括号内为说明)

输入


3 (repeat=3)

150

500

999

输出

count=2

count=6

count=8

#include <stdio.h>

#include <string.h>

int search(int n);           //对函数的声明

int main(void)

{

int number,ri,repeat;

scanf("%d",&repeat);

for(ri=1;ri<=repeat;ri++){

do{

scanf("%d",&number);

}while(number<101||number>999);

printf("count=%d\n",search(number));

}

return 0;

}

int search(int n)            //对函数的定义

{

int i,j,a1,a2,a3, flag=0;

for (i = 101; i <= n; i++)

{

for (j = 1; j <= i; j++)

{

if (j * j == i)

{

a1 = i % 10;

a2 = i / 10 % 10;

a3 = i /100 % 10;

if ( (a1==a2)||(a1==a3) || (a2==a3) )

{

flag += 1;

break;

}

}

}

}

return flag;

}

定义有参函数的一般形式:


函数名(形式参数表列)



函数体



7.3调用函数


7.3.1函数调用的形式171


计算函数P(n,x) (函数递归)


输入一个正整数repeat (0<repeat<10),做repeat次下列运算:


输入一个整数n (n>=0)和一个双精度浮点数x,输出函数p(n,x)的值(保留2位小数)。


               [1                                    (n=0)

     p(n, x) =   [x                                    (n=1)

               [((2*n-1)*p(n-1,x)-(n-1)*p(n-2,x))/n        (n>1)

例:括号内是说明


输入


3   (repeat=3)

0 0.9 (n=0,x=0.9)

1 -9.8 (n=1,x=-9.8)

10 1.7 (n=10,x=1.7)


输出


p(0, 0.90)=1.00

p(1, -9.80)=-9.80

p(10, 1.70)=3.05

#include <stdio.h>

double p(int n, double x);       //对函数的声明

int main(void)

{

 int repeat, ri;

  int n;

  double x, result;

  scanf("%d", &repeat);

  for(ri = 1; ri <= repeat; ri++)

  {

      scanf("%d%lf", &n, &x);

      result = p(n, x);

      printf("p(%d, %.2lf)=%.2lf\n", n, x, result);

  }

}

double p(int n, double x)        //对函数的定义

{

double t;

if(n==0)  t=1;

else if(n==1)

t=x;

else

t=((2*n-1)*p(n-1,x)-(n-1)*p(n-2,x))/n;

return t;

}

7.3.2函数调用时的数据传递


形式参数和实际参数

在定义函数时函数名后面的括号中的变量名称为“形式参数”或“虚拟参数”。


在主调函数中调用一个函数时,函数名后面括号中的参数称为“实际参数”。


实际参数可以是常量,变量或表达式。


实参和形参间的数据传递

在调用函数过程中,系统会把实参的值传递给被调用函数的形参。也可以这么理解,形参从实参得到一个值。该值在函数调用期间有效,可以参加该函数的运算。


7.3.3函数调用的过程


一个函数只能有一个返回值,(想返回多个可以参考后面的指针中的内容)


如果函数不需要返回值,则不需要return语句,这是函数的类型应定义为void类型。


7.4对被调用函数的声明和函数原型


在一个函数中调用另一个函数(即被调用函数)需要具备以下条件:


首先被调用的函数必须是已经定义的函数(是库函数或用户自己定义的函数)

如果使用库函数,应该在本文件开头用#include指令将调用有关库函数时所需用到的信息“包含”到本文件中去

#include<stdio.h>在stdio.h文件中包含了输入输出库函数的声明


使用数学库中的函数,应使用#include<math.h>


(3)如果使用用户自己定义的函数,而该函数的位置在调用它的函数(即主调函数)的后面(在同一文件中),应该在主调函数中被调用的函数作声明(declaration)。声明的作用是把函数名,函数参数的个数和参数类型等信息通知编译系统,以便在遇到函数调用时,编译系统能正确识别函数并检查调用是否合法。


思考总结:


可以发现,函数的声明和函数定义中的第1行(函数首部)基本上是相同的。只差一个分号(函数声明比函数定义中的首行多一个分号)


为什么要用函数的首部来作为函数声明呢?


这是为了对函数调用的合法性进行检查。因为在函数的首部包含了检查调用函数是否合法的基本信息(包含了函数名,函数值类型,参数个数,参数类型和参数顺序),在检查函数调用时要求函数名,函数类型,参数个数,参数类型和参数顺序必须和函数声明一致,实参类型必须与函数声明中的形参类型相同,这样就能保证函数的正确调用。


函数的“定义”和‘声明’并不是同一回事


函数的定义是指对函数功能的确立,包括指定函数名,函数值类型,形参及其类型以及函数体等,它是一个完整的,独立的函数单位。


而函数的声明的作用则是把函数名,函数值类型,参数个数,参数类型和参数顺序通知编译系统,以便在调用该函数时系统按此进行对照检查(例如:函数名,函数值类型,参数个数,参数类型和参数顺序),他不包含函数体。


7.5函数的嵌套调用


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


7.6函数的递归调用


在调用一个函数的过程中又出现直接或间接地调用该函数本身,称为函数的递归调用。


C语言函数递归调用_外太空程序员的博客-CSDN博客_c语言函数的递归调用


范例:计算5的阶乘,其实就是计算 1*2*3*4*5,


我们并不知道5的阶乘是多少,但是我们知道一点,4的阶乘 * 5他就等于5的阶乘。


我们并不知道4的阶乘是多少,但是我们知道一点,3的阶乘 * 4他就等于4的阶乘。


我们并不知道3的阶乘是多少,但是我们知道一点,2的阶乘 * 3他就等于3的阶乘。


所以递归调用的出口肯定是在1的阶乘,因为1的阶乘是1,可以作为出口,我们能够求出2的阶乘,也就是1*2;以此类推。


#include<stdio.h>


// 函数声明


int dg_jiecheng(int n);

int main()  //主函数

{

   int result = dg_jiecheng(5);

   printf("result=%d\n",result);  // 结果为120

}


// 函数定义


int dg_jiecheng(int n)

{

   int result;  // 局部变量,保存阶乘结果

   if(n==1) // 1的阶乘就是1

   {

       return 1;  // 这里就是该递归调用的出口

   }

   else

   {

     // 第一次是 result = dg_jiecheng(4)*5,然后进入到了 dg_jiecheng(4),这行代码就被暂存了;


        第二次是 result = dg_jiecheng(3)*4,然后进入到了 dg_jiecheng(3),这行代码就被暂存了;


        第三次是 result = dg_jiecheng(2)*3,然后进入到了 dg_jiecheng(2),这行代码就被暂存了;


        第四次是 result = dg_jiecheng(1)*2,然后进入到了 dg_jiecheng(1),这行代码就被暂存了;


      此时,dg_jiecheng(1)的出口条件成立了,终于,能够执行return 1,这可是 return 语句第一次捞着执行。


      第一次:return 1,返回的是1,返回到dg_jiecheng(2)这里:


      return =1*2 并且也执行return result;,返回1*2=2;


返回到dg_jiecheng(3)这里:


      return =2 并且也执行return result;,返回2*3=6;


返回到dg_jiecheng(4)这里:


      return =6 并且也执行return result;,返回6*4=24;


返回到dg_jiecheng(5)这里:


      return =24 并且也执行return result;,返回24*5=120;

       result = dg_jiecheng(n-1)* n;

   }

   return result;

递归的优缺点


优点:


       代码少,代码看起来简洁,精妙。


缺点:


       虽然代码简洁,精妙,但是不好理解。


        如果调用的层次太深,调用栈(内存),可能会溢出,如果真出现这种情况,那么说明不能用递归解决该问题。


       效率和性能都不高,这深层次的调用,要保存的东西很多,所以效率和性能肯定高不起来。有些问题用不用递归都行,有些问题可能必须用递归解决。汉诺塔


递归函数的直接和间接调用:


递归函数的直接调用:调用递归函数f的过程中,f函数有调用自己,这就是直接调用。


递归函数的间接调用:调用函数f1的过程中要调用f2函数,然后f2函数又调用f1函数,这就是间接调用。                                                            


7.7数组作为函数参数


编程题(指针数组)


输入一个正整数repeat (0<repeat<10),做repeat次下列运算:


编写程序,输入一个月份,输出对应的英文名称,要求用指针数组表示12个月的英文名称。


若输入月份错误,输出提示信息。


输入输出示例:括号内为说明


输入:


3 (repeat=3)

5

9

14


输出:


May

September

Wrong input!

#include<stdio.h>

void main()

{

int ri,repeat;

int month;

char *month_name[]={"","January","February","March","April","May","June","July","August","September","October","November","December"};

scanf("%d",&repeat);

for(ri=1;ri<=repeat;ri++){

scanf("%d",&month);

if((month>=1)&&(month<=12))

puts(month_name[month]);

else

printf("Wrong input!");

}

}

     


编程题 (指针数组,查找相同的字符串)


程序填空,不要改变与输入输出有关的语句。


输入一个正整数repeat (0<repeat<10),做repeat次下列运算:


定义一个指针数组将下表的星期信息组织起来,输入一个字符串,在表中查找,若存在,输出该字符串在表中的序号,否则输出-1。


(表格详见实验教材P99)


输入输出示例:括号内为说明


输入:


3 (repeat=3)

Tuesday

Wednesday

year


输出:


3

4

-1

#include<stdio.h>

#include<string.h>

void main()

{

int i,ri,repeat,dateNum;

char *date[]={"Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"};

char str[80];

scanf("%d",&repeat);

getchar();

for(ri=1;ri<=repeat;ri ++){

scanf("%s",str);

/*---------*/

dateNum=sizeof(date)/sizeof(char *);

for (i = 0; i<dateNum; i++){

if (strcmp(date[i],str) == 0) {

printf("%d\n",i+1); break;

}

}

if (i==dateNum) printf("%d\n",-1);

}

}


计算最长的字符串长度


编写一个函数int max_len(char *s[ ],  int n),用于计算有n(n<10)个元素的指针数组s中最长的字符串的长度,并编写主程序验证。


例:(括号内为说明)


输入


4 (n=4)

blue

yellow

red

green


输出


length=6

#include <stdio.h>

#include <string.h>

int max_len(char *s[],int n);

void main()

{

int i,n;

char s[10][80],*p[10];

scanf("%d",&n);

for(i=0;i<n;i++){

 scanf("%s",s[i]);

 p[i]=s[i];

}

printf("length=%d\n",max_len(p,n));

}

int max_len(char *s[],int n)

{

int max,i;

max=strlen(s[0]);

for(i=1;i<n;i++)

if(max<strlen(s[i]))  

max=strlen(s[i]);

 return max;

}

   


静态局部变量的值(static局部变量):


有时希望函数中的局部变量的值在函数调用结束后不消失而继续保留原值,即其占用的存储单元不释放,在下一次调用该函数时,该变量已有值(就是上一次函数调用结束时的值),这时就应该指定该局部变量为“静态局部变量”,用关键字static进行声明。


自动变量(auto变量)


函数中的局部变量,如果不专门声明为static(静态)存储类别,都是动态地分配存储空间的,数据存储在动态存储去中,函数中的形参和在函数中定义的局部变量(包括在复合语句中定义的局部变量),都属于此类。在调用该函数时,系统会给这些变量分配存储空间,在函数调用结束时就自动释放这些存储空间。


实际上,关键字auto可以省略,不写auto则隐含指定为“自动存储类别”


int b,c=3    ==========   auto int b,c=316.png



第七章课后题


C语言程序设计第五版谭浩强 第七章答案_月已满西楼的博客-CSDN博客_c语言第七章课后题答案


第七章目录回顾:


第7章用函数实现模块化程序设计167


7.1为什么要用函数167


7.2怎样定义函数169


7.2.1为什么要定义函数169


7.2.2定义函数的方法170


7.3调用函数171


7.3.1函数调用的形式171


7.3.2函数调用时的数据传递172


7.3.3函数调用的过程174


7.3.4函数的返回值174


7.4对被调用函数的声明和函数原型176


7.5函数的嵌套调用179


7.6函数的递归调用181


7.7数组作为函数参数189


7.7.1数组元素作函数实参189


7.7.2一维数组名作函数参数191


7.7.3多维数组名作函数参数194


7.8局部变量和全局变量196


7.8.1局部变量196


7.8.2全局变量197


*7.9变量的存储方式和生存期201


7.9.1动态存储方式与静态存储方式201


7.9.2局部变量的存储类别202


7.9.3全局变量的存储类别205


7.9.4存储类别小结209


7.10关于变量的声明和定义211


*7.11内部函数和外部函数212


7.11.1内部函数212


7.11.2外部函数213


目录
相关文章
|
24天前
|
存储 Serverless C语言
【C语言基础考研向】11 gets函数与puts函数及str系列字符串操作函数
本文介绍了C语言中的`gets`和`puts`函数,`gets`用于从标准输入读取字符串直至换行符,并自动添加字符串结束标志`\0`。`puts`则用于向标准输出打印字符串并自动换行。此外,文章还详细讲解了`str`系列字符串操作函数,包括统计字符串长度的`strlen`、复制字符串的`strcpy`、比较字符串的`strcmp`以及拼接字符串的`strcat`。通过示例代码展示了这些函数的具体应用及注意事项。
|
27天前
|
C语言
C语言程序设计核心详解 第四章&&第五章 选择结构程序设计&&循环结构程序设计
本章节介绍了C语言中的选择结构,包括关系表达式、逻辑表达式及其运算符的优先级,并通过示例详细解释了 `if` 语句的不同形式和 `switch` 语句的使用方法。此外,还概述了循环结构,包括 `while`、`do-while` 和 `for` 循环,并解释了 `break` 和 `continue` 控制语句的功能。最后,提供了两道例题以加深理解。
|
27天前
|
存储 C语言
C语言程序设计核心详解 第十章:位运算和c语言文件操作详解_文件操作函数
本文详细介绍了C语言中的位运算和文件操作。位运算包括按位与、或、异或、取反、左移和右移等六种运算符及其复合赋值运算符,每种运算符的功能和应用场景都有具体说明。文件操作部分则涵盖了文件的概念、分类、文件类型指针、文件的打开与关闭、读写操作及当前读写位置的调整等内容,提供了丰富的示例帮助理解。通过对本文的学习,读者可以全面掌握C语言中的位运算和文件处理技术。
|
27天前
|
存储 C语言
C语言程序设计核心详解 第七章 函数和预编译命令
本章介绍C语言中的函数定义与使用,以及预编译命令。主要内容包括函数的定义格式、调用方式和示例分析。C程序结构分为`main()`单框架或多子函数框架。函数不能嵌套定义但可互相调用。变量具有类型、作用范围和存储类别三种属性,其中作用范围分为局部和全局。预编译命令包括文件包含和宏定义,宏定义分为无参和带参两种形式。此外,还介绍了变量的存储类别及其特点。通过实例详细解析了函数调用过程及宏定义的应用。
|
27天前
|
C语言
C语言程序设计核心详解 第三章:顺序结构,printf(),scanf()详解
本章介绍顺序结构的基本框架及C语言的标准输入输出。程序从`main()`开始依次执行,框架包括输入、计算和输出三部分。重点讲解了`printf()`与`scanf()`函数:`printf()`用于格式化输出,支持多种占位符;`scanf()`用于格式化输入,需注意普通字符与占位符的区别。此外还介绍了`putchar()`和`getchar()`函数,分别用于输出和接收单个字符。
|
27天前
|
存储 算法 C语言
C语言程序设计核心详解 第一章:数制及转换与ASCII码
本专栏旨在夯实C语言基础,涵盖基础知识与进阶内容,助力解决自命题考试和考研问题,为数据结构与算法设计奠定坚实基础。内容包括数制及其转换、ASCII码、内存管理、机器码等,重点讲解二进制、八进制、十六进制的概念与转换方法,并介绍C语言的结构、数据类型和标识符规范。
|
27天前
|
存储 人工智能 C语言
C语言程序设计核心详解 第八章 指针超详细讲解_指针变量_二维数组指针_指向字符串指针
本文详细讲解了C语言中的指针,包括指针变量的定义与引用、指向数组及字符串的指针变量等。首先介绍了指针变量的基本概念和定义格式,随后通过多个示例展示了如何使用指针变量来操作普通变量、数组和字符串。文章还深入探讨了指向函数的指针变量以及指针数组的概念,并解释了空指针的意义和使用场景。通过丰富的代码示例和图形化展示,帮助读者更好地理解和掌握C语言中的指针知识。
|
27天前
|
存储 人工智能 C语言
C语言程序设计核心详解 第六章 数组_一维数组_二维数组_字符数组详解
本章介绍了C语言中的数组概念及应用。数组是一种存储同一类型数据的线性结构,通过下标访问元素。一维数组定义需指定长度,如`int a[10]`,并遵循命名规则。数组元素初始化可使用 `{}`,多余初值补0,少则随机。二维数组扩展了维度,定义形式为`int a[3][4]`,按行优先顺序存储。字符数组用于存储字符串,初始化时需添加结束符`\0`。此外,介绍了字符串处理函数,如`strcat()`、`strcpy()`、`strcmp()` 和 `strlen()`,用于拼接、复制、比较和计算字符串长度。
|
27天前
|
存储 C语言
C语言程序设计核心详解 第九章 结构体与链表概要详解
本文档详细介绍了C语言中的结构体与链表。首先,讲解了结构体的定义、初始化及使用方法,并演示了如何通过不同方式定义结构体变量。接着,介绍了指向结构体的指针及其应用,包括结构体变量和结构体数组的指针操作。随后,概述了链表的概念与定义,解释了链表的基本操作如动态分配、插入和删除。最后,简述了共用体类型及其变量定义与引用方法。通过本文档,读者可以全面了解结构体与链表的基础知识及实际应用技巧。
|
27天前
|
C语言
C语言程序设计核心详解 第二章:数据与数据类型 4种常量详解 常见表达式详解
本文详细介绍了C语言中的数据与数据类型,包括常量、变量、表达式和函数等内容。常量分为整型、实型、字符型和字符串常量,其中整型常量有十进制、八进制和十六进制三种形式;实型常量包括小数和指数形式;字符型常量涵盖常规字符、转义字符及八进制、十六进制形式;字符串常量由双引号括起。变量遵循先定义后使用的规则,并需遵守命名规范。函数分为标准函数和自定义函数,如`sqrt()`和`abs()`。表达式涉及算术、赋值、自增自减和逗号运算符等,需注意运算符的优先级和结合性。文章还介绍了强制类型转换及隐式转换的概念。
下一篇
无影云桌面