C语言——深入理解函数声明定义和调用访问
文章目录
- 一.嵌套调用
- 二. 链式访问
- 三.函数的声明和定义
- 3.1 单个文件
- 3.2 多个文件
- 3.3 static和extern
- 3.3.1 作用域和生命周期
- 3.3.2 static修饰局部变量
- 3.3.3 static修饰全局变量
- 3.3.4 static修饰函数
- 四. 练习
前面我分享了函数的一些基本概念以及易混淆点,但函数最重要的是如何调用和实现,接下来我将分享一些实例,更进一步理解函数的运用!
上篇函数介绍点击下方链接
C语言——函数(超详细分析)
一.嵌套调用
嵌套调用时函数之间的相互调用,我们可以把函数间的调用类比成用乐高零件拼玩具,每个函数就像是一个乐高零件,只不过这种零件可以重复使用,一个个函数 “堆积“ 成一个大的程序。
例:计算某年某月多少天,可以定义两个函数
is_leap_year() 根据年份确定是否是闰年
get_day_of_year() 调用is_leap_year(),再计算每月天数
int is_leap_year(int y)
{if (((y % 4 == 0) && (y % 100 != 0)) || (y % 400 == 0))return 1;elsereturn 0;
}
int get_days_of_year(int y, int m)
{int days[] = {0, 31,28,31,30,31,30 ,31,31,30,31,30,31 };// 0 1 2 3 4 5 6 7 8 9 10 11 12int day = days[m];if (is_leap_year(y) && m==2)day += 1;return day;
}
int main()
{int y = 0;int m = 0;scanf("%d %d", &y, &m);int d=get_days_of_year(y,m);printf("%d\n", d);return 0;
}
调用的scanf,printf,get_days_of_year
get_days_of_year调用的is_leap_year都是嵌套调用
但是函数不能嵌套定义
二. 链式访问
链式访问就是把函数的返回值最为另外一个函数的参数,这样像链条一样把函数串起来就是函数的链式访问
例
#include<stdio.h>
int main()
{int len = strlen("abcdefg");printf("%zu\n", len);return 0;
}
如果直接把上述代码中strlen的返回值作为函数的参数,这样就是链式访问
没有中间商赚差价😆
#include<stdio.h>
int main()
{printf("%zu\n", strlen("abcdefg"));return 0;
}
一个有趣的例子:
#include<stdio.h>
int main()
{printf("%d", printf("%d", printf("%d", 43)));return 0;
}
可以预测一下这段代码打印输出的结果
首先了解printf函数的返回值是什么
返回打印在屏幕上字符的个数
第三个printf函数打印43,屏幕上打印2字符返回值2;
第二个printf函数打印2,屏幕上打印1字符返回值1;
第一个printf函数打印1
最终打印4321
#include<stdio.h>
int main()
{printf("%d ", printf("%d ", printf("%d ", 43)));return 0;
}
这段代码打印多少可以猜测一下
三.函数的声明和定义
3.1 单个文件
一般我们使用函数的时候直接就可以使用
例:假如我们写一个函数判断是否是闰年
int test(int y)
{if (((y%4==0)&&(y%100!=0))||(y%400==0)){return 1;}else{return 0;}
}
int main()
{int y = 0;scanf("%d", &y);test(y);if (y==1){printf("是闰年\n");}else {printf("不是闰年\n");}return 0;
}
上面test()部分包括下面的函数体
是函数的定义,test(y)
是函数的调用
这样正常的写没事,但是把函数的定义放在调用的后面就会出现警告
int main()
{int y = 0;scanf("%d", &y);test(y);if (y==1){printf("是闰年\n");}else {printf("不是闰年\n");}return 0;
}
int test(int y)
{if (((y % 4 == 0) && (y % 100 != 0)) || (y % 400 == 0)){return 1;}else{return 0;}
}
因此我们要在函数调用前先进行声明
即:
函数要满足先声明再使用,函数的定义也是一种特殊的声明,所以函数定义放在调用之前是可以的
声明格式
int test (int )
1.有返回类型
2.参数的个数和类型
3.要有函数名,参数名可以省略
3.2 多个文件
但是在实际生活中写代码数量很大,因此会把代码都放在多个文件中,根据所写程序的功能和特点进行分类。
方便模块化编程
方便协作
函数的声明,类型的声明放在头文件.h
中,函数的实现文件放在.c
文件中
add.h
int Add(int x, int y);
add.c
int Add(int x, int y)
{return x + y;
}
test.c
#include<stdio.h>
#include"add.h"
int main()
{int a = 10;int b = 20;int c = Add(a, b);printf("%d\n", c);return 0;
}
3.3 static和extern
static和extern 都是C语言中的关键字
static是修饰静态变量,可以用来:
修饰局部变量
修饰全局变量
修饰函数
extern 是用来声明外部符号
外部符号可以是全局变量和函数等
3.3.1 作用域和生命周期
作用域:是程序设计的概念,一段代码中所用到的名字并不总是有效的,这个名字有效的范围就是作用域 即:可以使用的范围
1.局部变量作用域:变量所在的局部范围
2.全局变量的作用域是整的工程(项目)
例1
例2
**生命周期:**是指变量的创建(申请内存)到变量的销毁(收回内存)的一个时间段
1.局部变量的生命周期是:进入作用域变量创建生命周期开始,出作用域生命周期结束
2.全局变量的生命周期是:整个程序的生命周期(main函数的生命周期)
3.3.2 static修饰局部变量
两个例子了解static的作用
void test()
{int i = 0;i++;printf("%d\n", i);
}
int main()
{int i = 0;for ( i = 0; i < 5; i++){test();}return 0;
}
void test()
{static int i = 0;i++;printf("%d\n", i);
}
int main()
{int i = 0;for ( i = 0; i < 5; i++){test();}return 0;
}
结果
static修饰局部变量改变了变量的生命周期 加static之后出函数的时候是不会销毁的,重新进入函数也不会创建新的变量,直接上次累加的值进行计算
3.3.3 static修饰全局变量
全局变量具有外部连接属性,static修饰之后变成内部连接属性,不能被其他文件访问
3.3.4 static修饰函数
函数同样具有外部连接属性,static修饰之后变成内部连接属性,不能被其他文件访问
static 会把具有外部链接属性的全局变量和函数,变成内部连接属性只能被本身源文件使用,如果不想被其他文件发现就可以使用static修饰
static改变了具有局部变量的存储类型由栈区改为了静态区,局部变量就和全局变量一样,出这个作用域不会销毁等到下次进入函数再次使用。
四. 练习
创建一个整形数组,完成对数组的操作
实现函数init() 初始化数组为全0
实现print() 打印数组的每个元素
实现reverse() 函数完成数组元素的逆置。
void Init(int arr[], int sz, int set)
{int i = 0;for (i = 0; i < sz; i++){arr[i] = set;}
}void Print(int arr[], int sz)
{int i = 0;for (i = 0; i < sz; i++){printf("%d ", arr[i]);}printf("\n");
}
void Reverse(int arr[], int sz)
{int left = 0;int right = sz - 1;while (left < right){int tmp = arr[left];arr[left] = arr[right];arr[right] = tmp;left++;right--;}
}
int main()
{int arry[] = { 0,1,2,3,4,5,6,7,8,9 };int sz = sizeof(arry) / sizeof(arry[0]);Print(arry, sz);Reverse(arry, sz);Print(arry, sz);Init(arry, sz, 0);Print(arry, sz);return 0;
}