一、函数基础
1.1 什么是函数
逻辑上:
1、能够完成特定功能的独立的代码单元。
物理上:
1、能够接受数据。
2、能够对接收的数据进行处理。
3、能够将数据处理的结果返回(也可以不返回任何值)。
总结:函数是一个工具,它是为了解决大量类似问题而设计的。函数可以当作一个黑匣子。
1.2 为什么使用函数
1、避免了重复性编写代码的操作。
2、有利于程序的模块化。
1.3 定义函数
1.3.1 函数定义格式
函数定义的本质是详细描述函数之所以能够实现某个特定功能的具体方法。
函数返回值类型 函数名字(函数的形参列表)
{
函数的执行体;
return 待返回的值;
}
注意:
- 函数定义时必须指定函数的类型,带返回值的函数类型应该与其返回值类型相同。函数返回值的类型也称为函数的类型,因为如果函数名前面的返回值类型和函数执行体中的表达式的类型不同的话,最终函数返回值的类型以函数名前的返回值类型为准。而没有返回值的函数类型应该定义为void类型。
- 函数名的命名方法与命名变量的方法一样,不能以数字开头,只能以字母和下划线开头,且遵循驼峰原则。
- 函数名的命名不能使用C语言的关键字进行命名。
- 函数名的命名应尽量简单易懂。
1.3.1.1 函数定义示例
1.3.1.1.1 int返回类型的函数定义
#include <stdio.h>
// 主函数就是一个int返回类型的函数。int表示该函数的返回值为int类型
// main为函数名
// 圆括号中的void表示该函数无参数
int main(void){
return 0; // 0表示主函数返回值为0
}
# include <stdio.h>
// 定义f()函数
int f()
{
return 10.8; //因为函数的返回值类型为int,所以最终f的返回值是10而不是10.8
}
// 定义主函数
int main(void)
{
int x = 0;
x = f();
printf("%d\n", x); // x的值为10
return 0;
}
1.3.1.1.2 无返回类型的函数定义
// void表示该函数无返回数据类型。
// Info表示函数名。
// 圆括号中的void表示该函数无参数。
void Info(void){
printf("This is info");
return; // 表示终止函数,而不是返回类型值。return也可不写。
}
1.3.1.1.3 double返回类型的函数定义
// double表示该函数的返回类型为double类型
// Div表示函数名
// void表示该函数无参数
double Div(void){
double num = 10.0;
double div = 5.0;
double ret = 0;
ret = num / div;
return ret;
}
1.3.2 return表达式含义
1、终止被调函数,向主调函数返回表达式的值。
2、如果return后的表达式为空,则只终止函数,不向被调用函数返回任何值。
3、break是用来终止循环和switch,return是用来终止函数的。
1.3.2.1 return表达式示例
# include <stdio.h>
void f(void)
{
int i;
for (i = 0; i < 5; ++i)
{
printf("同志们辛苦了\n");
break; //终止循环
return; //终止函数
}
printf("同志们好\n");
}
int main(void)
{
f();
return 0;
}
二、函数的分类
1、有参函数和无参函数
2、有返回值和无返回值
3、库函数和用户自定义函数
4、值传递函数和地址传递函数
5、普通函数和主函数(main函数)
一个程序必须有且只能有一个主函数。主函数可以调用普通函数,普通函数不能调用主函数。普通函数可以相互调用。主函数是程序的入口,也是程序的出口。
2.1 有参函数和无参函数
有参函数就是在函数名后的括号中有参数的定义。在函数名后面括号中的参数称为形参。在函数调用时传递的具体值是实参。无参函数就是在函数名后的括号中为void或为空的函数。
2.1.1 有参数函数示例
示例1:比较两个数的最大值
# include <stdio.h>
// i,j为函数的形参
int max(int i, int j)
{
if (i > j)
return i;
else
return j;
}
int main(void)
{
int a, k, m;
while (1)
{
printf("请输入数值:\n");
scanf("%d %d", &k, &m);
// k,m为函数调用时传入的实参
a = max(k, m);
printf("%d\n", a);
}
return 0;
}
示例2:判断是否为素数
# include <stdio.h>
# include <stdbool.h>
// bool表示返回值为布尔类型
// val为函数的形参,int为形参的数据类型
bool Isprime(int val)
{
int i;
for (i = 2; i < val; ++i)
{
if (val % i == 0)
break;
}
if (i == val)
return true;
else
return false;
}
int main(void)
{
int val = 0;
scanf("%d", &val);
// val为函数的实参
if (Isprime(val))
printf("Yes");
else
printf("No");
return 0;
}
2.1.2 函数调用和函数定义的顺序
如果函数调用写在了函数定义的前面,则必须加函数前置声明
函数前置声明:
1、告诉编译器即将可能出现的若干字母代表的是一个函数。
2、告诉编译器即将可能出现的若干字母所代表的函数的形参和返回值的具体情况。
3、函数声明是一个语句,末尾必须加分号。
4、对库函数的声明是通过# include <库函数所在的文件的名字.h>来实现。
2.1.3 形参和实参
1、个数相同
2、位置一一对应
3、数据类型必须相互对应
三、函数变量
在计算机中,保存变量当前值的存储单元有两类,一类是内存,另一类是CPU的寄存器。变量的存储类型关系到变量的存储位置,C语言中定义了4种存储属性,即自动变量(auto)、外部变量(extern)、静态变量(static)和寄存器变量(register),它关系到变量在内存中的存放位置,由此决定了变量的保留时间和变量的作用范围。
3.1 变量作用域
3.1.1 全局变量
在所有函数外部定义的变量。
全局变量使用范围:从定义位置到整个程序结束。
示例:
# include <stdio.h>
int AllArea = 100; // 全局变量,定义在所有函数的外面
// int i,int j为函数的形参
int max(int i, int j)
{
if (i > j)
return i;
else
return j;
}
int main(void)
{
int a, k, m;
while (1)
{
printf("请输入数值:\n");
scanf("%d %d", &k, &m);
// k,m为函数调用时传入的实参
a = max(k, m);
printf("%d\n", a);
}
return 0;
}
3.1.1.1 extern关键字
如果全局变量不是在程序的开始位置定义,其有效的作用范围只限于定义处到程序末尾。如果在定义点之前的函数想引用该外部变量,则应该在引用之前用关键字extern对该变量进行“外部变量声明”,表示该变量是一个已经定义的外部变量。有了此声明,就可以从“声明”处起,合法地使用该外部变量。
示例:
#include<stdio.h>
int MaxValue(int a, int b){
// 在声明之前使用num需要加上extern关键字
extern num;
num = a > b ? a:b;
return num;
}
// num 虽然为全局变量,但声明的位置靠后,不能在声明之前的位置使用,如果在
// 声明之前的位置使用,需要加上extern关键字。
int num;
int main(void){
int ret = 0;
ret = MaxValue(2, 5);
printf("ret = %d", ret);
return 0;
}
3.1.2 局部变量
在一个函数内部定义的变量或者函数的形参。
局部变量的使用范围:只能在本函数的内部使用
局部变量在函数调用时创建,在函数结束后销毁。
示例:
# include <stdio.h>
int AllArea = 100; // 全局变量,定义在所有函数的外面
// int i,int j为函数的形参,也是该函数的局部变量
int max(int i, int j)
{
if (i > j)
return i;
else
return j;
}
int main(void)
{
int a, k, m; // 局部变量,声明在主函数的内部,不能在函数的外面使用
while (1)
{
printf("请输入数值:\n");
scanf("%d %d", &k, &m);
// k,m为函数调用时传入的实参
a = max(k, m);
printf("%d\n", a);
}
return 0;
}
函数中的局部变量,如不专门声明为static存储类别,都是动态地分配存储空间的,数据存储在动态存储区中。函数中的形参和在函数中定义的变量(包括在复合语句中定义的变量)都属此类,在调用该函数时系统会给它们分配存储空间,在函数调用结束时就自动释放这些存储空间,这类局部变量称为自动变量。
3.1.3 注意
在一个函数内部如果定义的局部变量的名字和全局变量的名字一样时,局部变量会屏蔽全局变量。
3.2 变量的生存周期
变量的保留时间又称为生存期,从时间角度,可将变量分为静态存储和动态
3.2.1 静态存储
静态存储是指变量存储在内存的静态存储区,在编译时就分配了存储空间,在整个程序的运行期间,该变量占有固定的存储单元,程序结束后,这部分空间才释放,变量的值在整个程序中始终存在。
3.2.1.1 静态局部变量static
有时希望函数中的局部变量的值在函数调用结束后不消失而保留原值,这时就应该指定局部变量为静态局部变量,用关键字static进行声明。
通过用static类型声明后的变量,其变量的内存空间位于内存的全局静态区,仅会初始化一次。
#include<stdio.h>
int MaxValue(int a, int b){
// static声明静态局部变量num
static int num;
num = a > b ? a:b;
return num;
}
int main(void){
int ret = 0;
ret = MaxValue(2, 5);
printf("ret = %d", ret);
return 0;
}
3.2.2 动态存储
动态存储是指变量存储在内存的动态存储区,在程序的运行过程中,只有当变量所在的函数被调用时,编译系统才临时为该变量分配一段内存单元,函数调用结束,该变量空间释放,变量的值只在函数调用期存在。
3.3 寄存器变量register
C语言允许将局部变量的值存放在CPU的寄存器中,这种变量叫做寄存器变量,用关键字register声明。
使用寄存器变量需要注意以下几点:
(1)只有局部自动变量和形式参数可以作为寄存器变量。
(2)一个计算机系统中的寄存器数目有限,不能定义任意多个寄存器变量。
(3)不能使用取地址运算符“&”求寄存器变量的地址。
示例:
register int num = 0;