函数的基本结构:
C语言中的函数通常具有以下几个部分:
1.返回类型:
函数执行完毕后返回给调用者的值的类型。如果函数不返回任何值,则使用void
关键字。
2.函数名:
函数的唯一标识符,用于调用函数。
3.参数列表:
在函数名后的括号中,可以定义零个或多个参数,这些参数是传递给函数的值或变量的引用。参数列表用逗号分隔。如果函数不接受任何参数,则参数列表为空。
4.函数体:
由花括号{}
包围的代码块,包含了函数要执行的语句。
C语言要求,在程序中用到的所有函数,必须“先定义,后使用”。例如想用 max 函数去求两个数中的大者,必须事先按规范对它进行定义,指定它的名字、函数返回值类型、函数实现的功能以及参数的个数与类型,将这些信息通知编译系统。这样,在程序执行 max时,编译系统就会按照定义时所指定的功能执行。如果事先不定义,编译系统怎么能知道 max 是什么、要实现什么功能呢!
函数的分类:
- 标准库函数:C语言标准库提供了一系列预定义的函数,如输入输出函数(
printf
,scanf
)、字符串处理函数(strcpy
,strlen
)等。 - 用户自定义函数:开发者根据自己的需求编写的函数。
函数的优点:
- 代码重用:通过调用函数,可以在程序的多个地方重复使用相同的代码,而无需重复编写。
- 模块化:将程序分解成独立的函数,有助于理解和维护复杂的程序。
- 提高可读性:函数名通常能够清晰地描述其功能,使得代码更加易于理解。
- 便于调试:当程序出现问题时,可以更容易地定位到特定的函数并进行调试。
函数的定义和调用:
1.定义无参函数:
#include<stdio.h>
void printWelcome()
{
printf("======================\n");
printf("欢迎来到我的代码\n");
printf("======================\n");
}
int main()
{
printWelcome();
return 0;
}
2.定义有参数有返回值的函数:
#include<stdio.h>
int fuctWithData(int x) //形式参数,需要包含变量类型、变量名
{
int y;
y = x-1;
return y;
}
int main()
{
int x;
int y;
printf("请输入一个数:");
scanf("%d",&x);
y = fuctWithData(x);
printf("x=%d,y=%d",x,y);
return 0;
}
3.函数调用的条件:
(1)函数已经被定义
(2)调用库函数:如果使用库函数,应该在本文件开头用#include指令将调用有关库函数时所需用到的信息“包含”到本文件中来。例如:#include <stdio. h>
其中“stdio.h”是一个“头文件”。在stdio.h文件中包含了输人输出库函数的声明。如果不包“stdio.h"文件中的信息,就无法使用输人输出库中的函数。同样,使用数学库中的函数,应该用#include<math.h>。h是头文件所用的后缀,表示是头文件(header file)。
(3)函数的声明:如果使用用户自己定义的函数,而该函数的位置在调用它的函数(即主调函数)的后面(在同一个文件中),应该在主调函数中对被调用的函数作声明(declaration)。声明的作用是把函数名、函数参数的个数和参数类型等信息通知编译系统,以便在遇到函数调用时,编译系统能正确识别函数并检查调用是否合法。
注意:
1.调用函数时,错误带了返回值类型,例如:int add(2,3)
2.调用函数时,错误使用形参带类型,例如:add(int a,int b)
3.函数可以当做表达式中的一部分直接使用,也可以当做一个参数直接被其他函数调用使用。
形参与实参:
在调用有参函数时,主调函数和被调用的数之间有数据传递关系。从前面已知:在定义函数时函数名后面括号中的变量名称为“形式参数”(简称“形参”)或“虚拟参数”。在主调函数中调用一个函数时,函数名后面括号中的参数称为“实际参数”(简称“实参”)。实际参数可以是常量、变量或表达式。
我们做一个简单的测试,打印出变量名一样的两个变量的值和地址:
#include<stdio.h>
int test(int x)
{
printf("test函数中形参x的内存地址为:%p,x的值为:%d\n",&x,x);
return 0;
}
int main()
{
int x;
int y;
printf("请输入一个数:");
scanf("%d",&x);
test(x);
printf("main函数中实参x的内存地址为:%p,x的值为:%d\n",&x,x);
return 0;
}
输出将是:
请输入一个数:6
test函数中形参x的内存地址为:000000000061FDF0,x的值为:6
main函数中实参x的内存地址为:000000000061FE1C,x的值为:6
形参x和实参x的变量名、值、类型都一样,但是地址不一样,所以他们不是同一个变量。
函数调用过程的参数传递:
(1)内存空间:在定义函数中指定的形参,在未出现函数调用时,它们并不占内存中的存储单元。在发生函数调用时,函数的形参被临时分配内存单元。
(2)值传递:将实参对应的值传递给形参。实参的值为2,把2传递给相应的形参x,这时形参x就得到值2。
(3)值返回:通过return语句将函数值带回到主调函数。在return语句中指定的返回值是z,这个z就是函数max的值(又称返回值)。执行return语句就把这个函数返回值带回主调函数main。应当注意返回值的类型与函数类型一致。如max函数为int型,返回值是变量z,也是int型。二者一致。
(4)内存释放:调用结束,形参单元被释放。注意:实参单元仍保留并维持原值,没有改变。如果在执行一个被调用函数时,形参的值发生改变,不会改变主调函数的实参的值。例如,若在执行max函数过程中x和y的值变为10和15,但a和b仍为2和3。这是因为实参与形参是两个不同的存储单元。
局部变量(Local Variables):
它指的是在函数(或称为方法、子程序等,取决于具体的编程语言)内部定义的变量。这些变量的作用域被限制在定义它们的函数内部,意味着它们只能在该函数内部被访问和修改。一旦函数执行完毕,这些局部变量就会被销毁,除非它们被作为返回值返回给函数的调用者。
局部变量有以下几个主要特点:
-
作用域限制:局部变量的作用域是从它被声明的点开始,到包含它的代码块(如函数体)结束。一旦离开这个作用域,该变量就不能再被访问。
-
生命周期:局部变量的生命周期是从它被创建(即函数开始执行,并且执行到其声明点)开始,到包含它的函数执行完毕时结束。函数执行完毕后,局部变量所占用的内存空间会被释放。
-
隐藏性:如果在一个函数内部定义了一个局部变量,它与该函数外部定义的任何同名变量都没有关系。这被称为变量的隐藏(或遮蔽)。这意味着在函数内部,局部变量的值会覆盖同名的全局变量或外部变量的值(如果有的话),但仅限于函数内部。
-
初始值:局部变量在使用前必须显式初始化,否则可能会导致编译错误或运行时错误。这是因为局部变量不会自动初始化为任何默认值(如0或null),它们的初始值是未定义的。
-
存储类:虽然C语言中的局部变量通常具有自动存储期(即它们在函数被调用时自动分配内存,在函数返回时自动释放内存),但也可以通过
static
关键字将局部变量的存储期改为静态存储期。静态局部变量的值在函数调用之间会保持不变,但它们的作用域仍然被限制在声明它们的函数内部。
局部变量是编程中用来管理函数内部状态和数据的重要工具。它们使得函数更加独立和可重用,因为每个函数都拥有自己的一组局部变量,这些变量不会被函数外部的代码直接访问或修改。这样有助于减少不同函数之间的耦合,使得代码更加清晰和易于维护。
函数嵌套:
求四个数中的最大值
#include<stdio.h>
int getBiggerFromTwo(int a,int b)
{
return a > b ? a:b; //三目运算符:如果a>b成立,返回值为a,否则为b
}
int getBiggest(int a,int b,int c,int d)
{
int biggest;
biggest = getBiggerFromTwo(a,b);
biggest = getBiggerFromTwo(biggest,c);
biggest = getBiggerFromTwo(biggest,d);
return biggest;
}
int main()
{
int data1;
int data2;
int data3;
int data4;
int dataBiggest;
printf("请输入四个数:\n");
scanf("%d%d%d%d",&data1,&data2,&data3,&data4);
dataBiggest = getBiggest(data1,data2,data3,data4);
printf("其中最大的数为:%d\n",dataBiggest);
return 0;
}