为什么会有函数
- 封装一些常用的功能,在使用时只需要调用函数即可,不需要了解其中的具体复杂代码实现,使主程序的结构更清晰
- 比如我们经常使用printf()来进行在控制台打印,不需要知道printf的具体实现,只需要调用即可,并且在主程序中看到printf就知道是要打印信息用,程序清晰明了
- 模块化一些经常需要复用的代码,复用时不需要复制整个代码,只需要调用函数即可,使程序编写更加高效、易用
- 便于将程序组织到多个文件中程序结构更加合理自然
- 比如我们在调用printf时,需要先导入库函数中的头文件stdio.h,只有导入了这个头文件,我们才能使用printf函数,c语言已经帮我们做好了很多函数功能,放在不同的文件中。
函数基础
函数语法
函数需要先声明后使用。
- 函数声明在前,中间可调用,定义实现在后
- 函数不声明,直接定义实现在前,随后有调用
声明格式
函数返回值类型 函数名(函数接收的参数类型 参数名,....可以有多个参数);
- 其中函数返回类型可以省略,省略的话默认返回是int类型
- 函数接受参数列表也是可以省略的,省略的话表示该函数直接调用即可,不需要向函数中传递参数
- 注意,不能在一个函数中定义另一个函数,main也是一个函数,是C程序执行的入口函数
函数定义实现
函数返回值类型 函数名(函数接收的参数类型 参数名,....可以有多个参数){
函数具体的实现代码
.....
return 表达式; //如果函数需要有返回值的话,需要通过return返回值,该值会被转换成返回值类型
}
- 就是将声明结尾的;替换为{代码实现}
代码示例
#include <stdio.h>
int sum(int max); //声明一个sum函数,计算0+1+2+...+max的值并返回
int main(int argc, char const *argv[])
{
//调用函数
printf("sum = %d\n", sum(10));
//再次调用
int a = sum(200); //函数返回值可以赋值给变量之后使用
printf("a = %d\n", a);
return 0;
}
// 函数定义功能实现
int sum(int max){
int sumValue = 0;
for(int i = 0; i <= max; i++){
sumValue += i;
}
return sumValue;
}
运行结果:
sum = 55
a = 20100
内部变量
定义在函数内部或者函数参数列表部分的变量
- 注意,由于main也是函数,所以在main中定义的变量也是内部变量
可使用范围(作用域) - 只能在声明变量的函数内部使用
- 多个不同的函数中可以声明相同名字的内部变量,互不影响
生命周期 - 从函数开始被调用开始,到函数退出,变量消失
代码示例
#include <stdio.h>
int sum(int max); //声明一个sum函数,计算0+1+2+...+max的值并返回
int main(int argc, char const *argv[])
{
int a = 10; //内部变量
printf("main-a is %d\n", a);
sum(10); // main中的a和sum中的a互不干扰
printf("main-a is %d\n", a);
sum(20); // 上一次函数退出后a和b重新初始化
return 0;
}
int sum(int max){
int a = 0; //不同函数中可以声明同名内部变量
int b = 100;
for(int i = 0; i <= max; i++){
a += i;
}
printf("sum-a is %d\n", a);
printf("sum-b is %d\n", b);
return a;
}
运行结果:
main-a is 10
sum-a is 55
sum-b is 100
main-a is 10
sum-a is 210
sum-b is 100
外部变量
定义在函数外部的变量。
可使用范围(作用域)
- 声明的位置到文件末尾的全局都可以使用,可以在多个函数中和外部使用
- 一个程序的多个源文件中不能定义同名外部变量,可以使用extern关键字声明使用其他文件中定义的外部变量
- 当内部变量与外部变量同名时,内部变量优先级高,这种情况要避免出现
生命周期 - 程序运行整个周期中都存在,不会因为某一个使用外部变量的函数退出而消失
代码示例
#include <stdio.h>
int sum(int max); //声明一个sum函数,计算0+1+2+...+max的值并返回
int a = 20; //外部变量a,之后的函数中都可以使用
int main(int argc, char const *argv[])
{
int a = 10;
printf("main-a is %d\n", a);//内部变量a的优先级更高
sum(10);
sum(1); //验证外部变量不会因为某一函数退出而消失
return 0;
}
int sum(int max){
for(int i = 0; i <= max; i++){
a += i;//修改外部变量
}
printf("sum-a is %d\n", a);
return a;
}
运行结果:
main-a is 10
sum-a is 75
sum-a is 76
关键字extern
作在外部变量还没有定义时使用该变量,或者外部变量的定义和使用不在同一个源文件中时,在变量声明时,必须加上extern关键字。
变量声明
- 说明变量的类型,不会实际为变量分配存储空间
extern int a;//这种就只是在使用外部变量时先声明,但是不会给变量分配存储空间
变量定义
- 声明变量类型,并为变量实际分配存储空间
int a;//在外部变量真正定义的地方,才会对外部变量进行存储空间的分配
代码示例
#include <stdio.h>
int main(int argc, char const *argv[])
{
extern int a; //声明使用之后定义的外部变量,如果不写这句将不在外部变量a的作用域内
printf("a is %d\n", a);
return 0;
}
int a = 100; //外部变量定义
运行结果:
a is 100
静态变量
static修饰的变量或函数。
- 修饰外部变量
- 该外部变量只能在被定义的源文件中被使用,不能被其他文件访问
- 这样其他文件中就可以声明同名外部变量
代码示例
源文件1:exercise2-2.h
int function1(int a, int b);
源文件2:exercise2-2.c
#include "exercise2-2.h"
static int s = 100;
int function1(int a, int b){
return s; //静态外部变量只能在当前源文件中使用
}
源文件3:exercise2-1.c
#include <stdio.h>
#include "exercise2-2.h"
int main(int argc, char const *argv[])
{
// extern int s;
// printf("s = %d\n", s);// 直接访问static外部变量是不行的
printf("function result is %d\n", function1(1, 2));
return 0;
}
运行结果:
function result is 100
- 修饰函数
- 该函数只能在被定义的源文件中被使用,不能被其他文件访问
- 其他文件中也可以声明同名函数
代码示例
源文件1:exercise2-2.h
static int function1(int a, int b);
int function2(int a, int b);
源文件2:exercise2-2.c
#include "exercise2-2.h"
static int function1(int a, int b){ //静态函数,只能当前源文件中使用
return (a + b) / 2;
}
int function2(int a, int b){
return function1(a, b); //同一文件中可以使用function1
}
源文件3:exercise2-1.c
#include <stdio.h>
#include "exercise2-2.h"
int main(int argc, char const *argv[])
{
// function1(1,2); //由于function1只能在源文件2中使用,该语句会引起编译器报错
int s = function2(1, 2); // function2可以正常调用
printf("s = %d\n", s);
return 0;
}
运行结果:
s = 1
- 修饰内部变量
- 变量在函数生命周期结束后依然存在,再次进入函数时变量保持上次退出时的值,不新做初始化
- 不过它的作用域依然只限定在函数内使用
代码示例
#include <stdio.h>
int sum(int max);
int main(int argc, char const *argv[])
{
sum(10);
sum(1);
return 0;
}
int sum(int max){
static int a = 1; //静态内部变量,不会随着函数的结束而消失
for(int i = 0; i <= max; i++){
a += i;
}
printf("sum-a is %d\n", a);
return a;
}
运行结果:
sum-a is 56
sum-a is 57
各种类型变量的初始化情况
- 在不进行显示初始化时
- 外部变量和静态变量默认值为0
- 内部变量值为不确定值
- 初始化
- 外部变量和静态变量初始化时必须使用常量表达式,且只能初始化一次
- 内部变量则随函数的调用反复进行初始化,初始化表达式中可以用之前已经定义过的任意值和常量
代码示例
#include <stdio.h>
int ex_a; //外部变量不显示初始化,默认为0
int ex_b = 100;
int main(int argc, char const *argv[])
{
int a;//内部变量不显示初始化,值不确定
int b = 10;
printf("ex_a = %d\n", ex_a);
printf("ex_b = %d\n", ex_b);
printf("a = %d\n", a);
printf("b = %d\n", b);
return 0;
}
运行结果:
ex_a = 0
ex_b = 100
a = 1 //这个值大家运行结果可能会不同
b = 10