C语言——深入理解函数声明定义和调用访问

🏡润下个人主页
🔥个人专栏 : 《C语言基础》

🏔️山高万仞,只登一步!


文章目录

  • 一.嵌套调用
  • [二. 链式访问](#二. 链式访问)
  • 三.函数的声明和定义
    • [3.1 单个文件](#3.1 单个文件)
    • [3.2 多个文件](#3.2 多个文件)
    • [3.3 static和extern](#3.3 static和extern)
      • [3.3.1 作用域和生命周期](#3.3.1 作用域和生命周期)
      • [3.3.2 static修饰局部变量](#3.3.2 static修饰局部变量)
      • [3.3.3 static修饰全局变量](#3.3.3 static修饰全局变量)
      • [3.3.4 static修饰函数](#3.3.4 static修饰函数)
  • [四. 练习](#四. 练习)

前面我分享了函数的一些基本概念以及易混淆点,但函数最重要的是如何调用和实现,接下来我将分享一些实例,更进一步理解函数的运用!

上篇函数 介绍点击下方链接
C语言------函数(超详细分析)

一.嵌套调用

嵌套调用时函数之间的相互调用,我们可以把函数间的调用类比成用乐高零件拼玩具,每个函数就像是一个乐高零件,只不过这种零件可以重复使用,一个个函数 "堆积" 成一个大的程序。
:计算某年某月多少天,可以定义两个函数

is_leap_year() 根据年份确定是否是闰年

get_day_of_year() 调用is_leap_year(),再计算每月天数

c 复制代码
 int is_leap_year(int y)
{
	if (((y % 4 == 0) && (y % 100 != 0)) || (y % 400 == 0))
		return 1;
	else
		return 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  12
	int 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都是嵌套调用

但是函数不能嵌套定义

二. 链式访问

链式访问就是把函数的返回值最为另外一个函数的参数,这样像链条一样把函数串起来就是函数的链式访问

c 复制代码
#include<stdio.h>
int main()
{
	int len = strlen("abcdefg");
	printf("%zu\n", len);
	return 0;
}

如果直接把上述代码中strlen的返回值作为函数的参数,这样就是链式访问
没有中间商赚差价😆

c 复制代码
#include<stdio.h>
int main()
{
	printf("%zu\n", strlen("abcdefg"));
	return 0;
}

一个有趣的例子:

c 复制代码
#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

c 复制代码
#include<stdio.h>
int main()
{
	printf("%d ", printf("%d ", printf("%d ", 43)));
	return 0;
}

这段代码打印多少可以猜测一下

三.函数的声明和定义

3.1 单个文件

一般我们使用函数的时候直接就可以使用

例:假如我们写一个函数判断是否是闰年

c 复制代码
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)是函数的调用

这样正常的写没事,但是把函数的定义放在调用的后面就会出现警告

c 复制代码
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

c 复制代码
int Add(int x, int y);

add.c

c 复制代码
int Add(int x, int y)
{
	return x + y;
}

test.c

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的作用

c 复制代码
void test()
{
	int i = 0;
	i++;
	printf("%d\n", i);
}
int main()
{
	int i = 0;
	for ( i = 0; i < 5; i++)
	{
		test();
	}
	return 0;
}
c 复制代码
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() 函数完成数组元素的逆置。

c 复制代码
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;
}
相关推荐
一个很帅的帅哥15 分钟前
JavaScript事件循环
开发语言·前端·javascript
驰羽16 分钟前
[GO]gin框架:ShouldBindJSON与其他常见绑定方法
开发语言·golang·gin
摇滚侠16 分钟前
Spring Boot 3零基础教程,WEB 开发 自定义静态资源目录 笔记31
spring boot·笔记·后端·spring
摇滚侠17 分钟前
Spring Boot 3零基础教程,WEB 开发 Thymeleaf 遍历 笔记40
spring boot·笔记·thymeleaf
程序员大雄学编程22 分钟前
「用Python来学微积分」5. 曲线的极坐标方程
开发语言·python·微积分
Code小翊27 分钟前
C语言bsearch的使用
java·c语言·前端
Jose_lz1 小时前
C#开发学习杂笔(更新中)
开发语言·学习·c#
Chloeis Syntax1 小时前
接10月12日---队列笔记
java·数据结构·笔记·队列
QT 小鲜肉1 小时前
【个人成长笔记】Qt 中 SkipEmptyParts 编译错误解决方案及版本兼容性指南
数据库·c++·笔记·qt·学习·学习方法
一位代码1 小时前
python | requests爬虫如何正确获取网页编码?
开发语言·爬虫·python