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;
}
相关推荐
一只自律的鸡4 小时前
【python】从Hello World到数据类型
开发语言·python
寻星探路4 小时前
Java EE初阶启程记05---线程安全
java·开发语言·java-ee
xb11324 小时前
C#——方法的定义、调用与调试
开发语言·c#
froginwe115 小时前
MVC HTML 帮助器
开发语言
范纹杉想快点毕业5 小时前
单片机开发中的队列数据结构详解,队列数据结构在单片机软件开发中的应用详解,C语言
c语言·数据库·stm32·单片机·嵌入式硬件·mongodb·fpga开发
王嘉俊9255 小时前
ThinkPHP 入门:快速构建 PHP Web 应用的强大框架
开发语言·前端·后端·php·框架·thinkphp
syty20205 小时前
AST语法树应用于sql检查
java·开发语言·ast
倔强菜鸟5 小时前
2025.8.10-学习C++(一)
开发语言·c++·学习
ZXF_H6 小时前
C/C++预定义宏与调试日志输出模板
开发语言·c++·日志·调试·预定义宏