目录
函数基础
函数的概念
函数是c语言的功能单位,实现一个功能可以封装一个函数来实现。
定义函数的时候一切以功能为目的,根据功能去定函数的参数和返回值。
形参和实参
形参:形式参数,主要用于保存实参传递的值,本质跟实参没有任何关系,只是值的传递
实参:实际参数,本质就是在函数调用候将实参的值传递给形参
注意事项:实参可以是任意类型的数据。形参可以是变量,或缺省常量,不可以是表达式。
函数分类
从定义角度分类(即函数是谁实现的)
(1)库函数(c 库实现的,例如stdio.h) 用 < >
(2)自定义函数(编码人自己实现的函数) 用" "
(3)系统函数(操作系统实现的函数)
从参数角度分类
(1)有参函数
函数有形参,可以是一个,或者多个,参数的类型随便
完全取决于函数的功能
(2)无参函数
函数没有参数,在形参列表的位置写个void或什么都不写
从返回值角度分类
(1)带返回值的函数
在定义函数的时候,必须带着返回值类型,在函数体里,必须有return
如果没有返回值类型,默认返回整型。
char fun(形参列表)//定义了一个返回值为字符型的函数
{
函数体
return X;//X为字符型
}
注意:如果把函数的返回值类型省略了,默认返回整型
在定义函数的时候,函数的返回值类型,到底是什么类型的,取决于函数的功能。
(2)不带返回值的函数
在定义函数的时候,函数名字前面加void
在函数里不需要return或者直接return ;什么都不返回就可以了
void fun(形参列表)
{
函数体
return ;//或者直接不写return
}
函数使用
函数的定义
用法格式
返回值类型 函数名(形参列表)
{
函数体 - 函数的功能在函数体里实现
}
函数的声明
有些情况下,如果不对函数进行声明,编译器在编译的时候,可能不认识这个函数,因为编译器在编译c程序的时候,从上往下编译的。而且函数的声明可以声明多次
- 何时声明
(1)当主调函数和被调函数在同一个.c 文件中时:
如果被调函数在上,主调函数在下,这种情况不需要声明
如果被调函数在下,主调函数在上需要声明
(2)主调函数和被调函数不在同一个.c 文件中的时候一定要声明
- 声明方法
(1)直接声明法
主调函数和被调函数在同一个.c 文件中的时,用直接声明法。
方法1:直接将被调用的函数的第一行拷贝过去后面加分号,即
返回值类型 函数名(形参列表);
方法2:可以将形参列表换为数据类型,即
返回值类型 函数名(数据类型);
示例:
#include<stdio.h>
void GOOD(int a,int b); //直接声明法-1
void GOOD(int,int); //直接声明法-2
int main()
{
主体函数
}
void GOOD(int a,int b)
{
函数体
}
(2)间接声明法
主调函数和被调函数不在同一个.c 文件中的时:
1.将被调用的函数的第一行拷贝过去,后面加分号,前面加extern
2.将函数的声明放在头文件中 .c 程序包含头文件
注意:要向头文件声明,借助头文件实现间接声明,向头文件声明时,要用" "(自定义函数)而非< >(库函数)
函数的调用
- 调用方法
(1)带返回值的:
变量= 函数名(实参列表);
(2)不带返回值的
函数名(实参列表);
- 代码案例
1.没有参数,没有返回值
直接写函数名,并且要在后面加括号
void fun();
int main()
{
fun();//函数的调用
}
void fun()
{
语句块
}
2.有参数,没有返回值
需要在函数名右边括号中传入参数,参数可以是常量表达式,也可以是变量表达式
void sum(int x,int y);
int main()
{
sum(4,5);//方式1
int a=4,b=5;//方式2
sum(a,b);
return 0;
}
void sum(int x,int y)
{
int sum;
sum=x+y;
printf("%d\n",sum);
}
3.有参数,有返回值
可以使用一个变量接收函数执行结果(返回值),或者直接使用也可以
int C(int m,int n);//声明C
int main()
{
int H=C(432,17);//有参数,有返回值-方式1
printf("%d\n",H);
printf("%d\n",C(325,3));//有参数,有返回值-方式2
return 0;
}
int C(int m,int n)
{
int l;
l=m%n;
return l;
}
4.没有参数,有返回值
int D();//声明D
int main()
{
int U=D();//没有参数,有返回值
printf("%d\n",U);//方式1
printf("%d\n",D());//方式2
return 0;
}
int D()
{
int s=76,p=18;
return s*p;
}
函数实践
不声明类型
即下面这段代码是可行的
#include<stdio.h>
test(x,y); //返回值类型和参数类型不写,默认是int型
int main()
{
test(5,6,7); //输出11
return 0;
}
void test(x,y)
{
int sum=x+y;
printf("%d\n",sum);
}
函数的嵌套使用
虽然C语言的函数定义是互相平行、独立的,不能嵌套定义函数,但可以嵌套调用函数
案例:求四个数的最大数
函数的递归使用
直接或间接的地调用函数本身,称为函数的递归调用。C语言的特点之一就是允许函数的递归调用
案例:求n!
数组作函数参数
1.数组传参时,为了更好的使用和修改,一般不设置数组宽度
例如: void test(char str[])
案例:求10个数的平均值
2.给函数传数组的时候,没法一下将数组的内容作为整体传进去,只能传数组的地址,即只能传数组首元素的指针。将数组作为参数传递给函数
代码示例:
#include<stdio.h>
void f1(int m[]);//传一维数组_1
void f2(int *m);//传一维数组_2(常用)
void f3(int n[][4]);//二维传参_1
void f4(int (*n)[4]);//二维传参_2(常用)
void f5(char **x);//传指针数组
int main()
{
//传一维数组
int a[10]={1,2,3,4,5,6,7,8,9,10};
f1(a); f2(a);
puts("***************************************");
//传二维数组
int b[2][4]={1,3,5,7,2,4,6,8};
f3(b); f4(b);
puts("***************************************");
//传指针数组
char *w[4]={"abcdef","hijklmn","jhkjbhjbhb","sklqkddi"};
f5(&w);
return 0;
}
void f1(int m[])//传一维数组_1
{
printf("%d\t",m[2]);
printf("%d\n",m[5]);
}
void f2(int *m)//传一维数组_2(常用)
{
printf("%d\t",m[1]);
printf("%d\n",*(m+3));
}
void f3(int n[][4])//二维传参_1
{
printf("%d\t",n[1][3]);
printf("%d\n",n[0][3]);
}
void f4(int (*n)[4])//二维传参_2(常用)
{
printf("%d\t",n[0][2]);
printf("%d\n",*(*(n+1)+1)); //p[x][y]<==> *(*(p + x) + y
}
void f5(char **x)//传指针数组
{
printf("%s\n",x[2]);
}
局部变量和全局变量
全局变量
- 普通全局变量
普通全局变量在整个工程文件内都有效
int num=100;//num就是一个普通的全局变量
- 静态全局变量
静态全局变量只在定义的文件内有效
定义静态全局变量的时候,前面要用static修饰
static int x=100;//x就是一个静态全局变量
- 全局变量小结
- 生命周期:程序运行的整个过程,一直存在,直到程序结束。第一次调用函数的时候,开辟空间赋值,函数结束后,不释放,以后再调用函数的时候,就不再为其开辟空间,也不赋初值,用的是以前的那个变量。
- 作用域:普通全局变量在整个工程文件内都有效。静态全局变量只在定义的文件内有效
- 在函数外部定义的变量,即在main函数和子函数外部定义的变量叫做全局变量
- 定义全局变量时,如果不赋初值它的值默认为0,即如果全局变量没有进行初始化,则系统自动将其初始化为0
全局变量跨文件使用之前需要用extern声明,而且不能进行赋值操作
局部变量
- 普通局部变量
生命周期:
在函数(复合语句)调用之前局部变量不占用空间,调用函数(复合语句)的时候才为局部变量开辟空间,函数(复合语句)结束时,局部变量也紧接着释放。
- 静态局部变量
定义静态局部变量的时候,前面加static修饰
生命周期:
第一次调用函数(复合语句)时开辟空间赋值,函数(复合语句)结束后,不释放,以后再调用函数时就不再为其开辟空间,也不赋初值,用的还是以前的那个变量,直到程序结束。
- 局部变量小结
- 在函数(复合语句)内部定义的变量叫做局部变量
- 生命周期:普通局部变量在定义它的局部范围内有效,每次使用完后释放,下次使用时会重新初始化。静态局部变量程序仅分配一次内存,函数返回后,该变量不会消失,下次使用时继续使用此变量
- 普通局部变量不进行初始化,默认是随机值(有些编译器会报错)。静态局部变量不进行初始化,默认是0
- 作用域:局部变量只能在定义的函数(复合语句)内部使用,生命周期相对较短
内部函数和外部函数
外部函数
普通定义的函数,都是外部函数。即外部函数可以在程序的任何一个文件中调用
在分文件编程中,只需要将函数的实现过程写在指定的.c文件中,然后将其声明写在指定的.h文件中,其他文件只要包含了头文件,就可以使用外部函数
内部函数
内部函数也称之为静态函数,就是用static修饰的函数,即返回值类型前面加static修饰的函数被称为内部函数
static void fun()//内部(静态)函数
{
函数体
}
static 限定了函数的作用范围,只在所定义的.c文件中有效
内部函数和外部函数的区别
外部函数,不需要修饰,在所有地方都可以调用
内部函数,需要用static修饰,只能在所定义的.c文件中的函数调用
注意事项
- 函数名字是标识符,所以需要满足标识符的命名规则
- 函数传参时即可传变量也可传常量
- 有返回值的函数,最终返回的是变量所存储的数据的值,而不是变量
- 不同的形参之间要用逗号隔开。形参可以有,也可以没有,也可以有多个。但即使没有,函数名字后面也必须加括号。而且函数体上下位置必须有大括号
- 如果要返回函数执行的结果,即函数有返回值,则return后面跟的变量或者值的类型,必须与返回值类型保持一致
- 函数的定义不能嵌套,即不能在一个函数体内定义另外一个函数。所有的函数的定义是平行的,相同的函数名只能出现一次
- C语言函数的返回值和形参是可以不写的,不写的情况下默认是int类型。
- C语言下函数调用时不会对实参个数进行检测。
- C语言下void型函数可以写返回值。
- 函数的形参为数组时相当于指针,例如 void test(int a[20]) 函数中的参数相当于int*a,所占大小为四字节
- 函数的形参为数组时可以不指定数组个数,例如 void test(int num[])
-
在同一作用范围内,不允许变量重名。但作用范围不同的可以重名。
-
局部范围内,重名的全局变量不起作用。(即向上就近原则)
int num = 100; //全局变量 int main() { printf("%d\n",num);// 1 int num = 999; //局部变量 printf("%d\n",num);// 2 return 0; }
代码分析:第一个num是全局变量,存放在静态全局区;第二个num是局部变量,存放在栈区。所以两个num虽然变量名相同,但不是在同一作用范围内,所以可以重名。而其中的向上就近原则指的是——相同的变量名在使用的时候以上方最近的那个为准,例如第一个printf输出100,第二个printf输出999