第六章:函数
文章目录
- 第六章:函数
-
- 1. 库函数
- 2. 自定义函数
-
- 2.1 形参和实参
- 2.2 return 语句
- 2.3 数组做函数参数
- 3. 嵌套调用和链式访问
-
- 3.1 嵌套调用
- 3.2 链式访问
- 4. 函数声明和定义
-
- 4.1 单个文件
- 4.2 多个文件
- 4.3 关键字使用
-
- 4.3.1 extern
- 4.3.2 static
- 声明
C 语言中的函数就是一个完成某项特定任务的一小段代码。
1. 库函数
不同编译器提供的一系列函数的实现,就被称为库函数。这些库函数根据功能的划分,都在不同的头文件中进行声明。
库函数相关头文件:C Standard Library headers – cppreference.com
2. 自定义函数
语法形式
函数返回类型 函数名(形式参数)
{
}
2.1 形参和实参
- 形参:在自定义函数的括号中写的参数,称为形式参数
- 实参:真是传递给函数的参数
- 函数的形式参数要和和实参个数匹配
只有在函数被调用的过程中存放实参传递过来的值,才会向内存申请空间
加法函数,完成两个整形变量的加法操作:
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdbool.h>
//第六章:函数
//此时是形参,在不调用此函数时,不会分配内存空间,
//当调用函数时,形参便会分配内存空间(和实参各自有独立的空间)
int add(int a, int b)
{
return a + b;//将a+b的值返回出去
}
int main()
{
int a = 0;
int b = 0;
int c = add(a, b);//调用函数,将变量传递进去,此时是实参
scanf("%d", &c);
printf("两个数之和是:%d", c);
return 0;
}
2.2 return 语句
- return后边可以是⼀个数值,也可以是⼀个表达式,如果是表达式则先执⾏表达式,再返回表达式 的结果。
- return后边也可以什么都没有,直接写 return; 这种写法适合函数返回类型是void的情况。
- return语句执⾏后,函数就彻底返回,后边的代码不再执⾏。
- return返回的值和函数返回类型不⼀致,系统会⾃动将返回的值隐式转换为函数的返回类型。
- 如果函数中存在if等分⽀的语句,则要保证每种情况下都有return返回,否则会出现编译错误。
- 函数的返回类型如果不写,编译器会默认函数的返回类型是int。
- 函数写了返回类型,但是函数中没有使⽤return返回值,那么函数的返回值是未知的。
2.3 数组做函数参数
- 函数的实参是数组,形参也是可以写成数组形式的
- 形参如果是⼀维数组,数组⼤⼩可以省略不写
- 形参如果是⼆维数组,⾏可以省略,但是列不能省略
- 数组传参,形参是不会创建新的数组的
- 形参操作的数组和实参的数组是同⼀个数组
写一个函数将一个整形数组的内容,全部置为 -1,再写一个函数打印数组内容
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdbool.h>
//数组做函数参数,数组的实参和形参是同一块内存空间
//将数组内容全部置换为 -1
void set_arr(int arr[], int sz)//此时不设置长度,根据传过来的大小底层自动推断
{
for (int i = 0; i < sz; i++)
{
arr[i] = –1;
}
}
//打印数组
void print_arr(int arr[], int sz)
{
for (int i = 0; i < sz; i++)
{
printf("%d ", arr[i]);
}
}
int main()
{
//定义数组
int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
//计算数组长度
int sz = sizeof(arr) / sizeof(arr[0]);
//调用置换数组函数
set_arr(arr,sz);
//调用打印数组函数
print_arr(arr,sz);
return 0;
}
3. 嵌套调用和链式访问
3.1 嵌套调用
函数之间的互相调用称为嵌套调用。
实现计算某年某月有多少天:
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdbool.h>
//嵌套调用
//定义函数判断是否为闰年
bool is_leap_year(int year)
{
if ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0)
{
return true;
}
return false;
}
//定义函数计算某年某月有多少天
int get_days(int year, int month)
{
// 0 1 2 3 4 5 6 7 8 9 10 11 12
int days[] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 };//前面补0,方便月份和下标对应
if (is_leap_year(year) && month == 2) //嵌套调用判断是否是闰年和2月
{
days[month] += 1;//如果是闰年2月,则天数加1
return days[month];
}
return days[month];//不是闰年,则直接返回对应月份的天数
}
int main()
{
int year = 0;
int month = 0;
scanf("%d%d", &year, &month);
int days = get_days(year,month);
printf("%d年%d月有%d天\\n", year, month, days);
return 0;
}
3.2 链式访问
谓链式访问就是将⼀个函数的返回值作为另外⼀个函数的参数,像链条⼀样将函数串起来就是函数 的链式访问
//链式访问
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
//printf的返回值是输出字符的个数,包括换行符和空格
//输出 43 3 2
printf("%d ", printf("%d ", printf("%d ", 43)));
return 0;
}
4. 函数声明和定义
4.1 单个文件
当我们将自定义的函数放在 main 函数后,也就是调用此函数之后,需要在函数调用前声明一下此函数。函数的调用要满足先声明后使用,函数的定义是一种特殊的声明。
- 函数声明:函数返回类型 函数名(函数参数)
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdbool.h>
bool is_leap_year(int year); //函数声明
int main()
{
int year = 0;
int month = 0;
scanf("%d%d", &year, &month);
int days = get_days(year,month);
printf("%d年%d月有%d天\\n", year, month, days);
return 0;
}
//定义函数判断是否为闰年
bool is_leap_year(int year)
{
if ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0)
{
return true;
}
return false;
}
4.2 多个文件
函数的声明和类型的声明放在头文件中 (.h) ,函数的实现放在其他源文件中 (.c) ,此时 main 函数所在的源文件要包括自定义的头文件。
add.h
#pragma once
//头文件用于声明函数、类、变量等,防止重复包含
int add(int a, int b);
add.c
#define _CRT_SECURE_NO_WARNINGS
//函数的定义
int add(int a, int b)
{
//将a+b的值返回出去
return a + b;
}
test.c
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
//包含自定义头文件,注意使用双引号
#include "add.h"
int main()
{
int a = 0;
int b = 0;
scanf("%d%d", &a, &b);
int c = add(a,b);
printf("和是:%d\\n", c);
}
4.3 关键字使用
作用域:一段程序中所用到的名字并不是总有效,可用性的代码范围称为作用域
- 局部变量:变量所在的局部范围
- 全局变量:整个项目
生命周期:变量的创建(申请内存)到变量的销毁(收回内存)之间的一个时间段
- 局部变量:进入作用域变量创建,生命周期开始,出作用域生命周期结束
- 全局变量:整个程序的声明周期
4.3.1 extern
用于声明外部符号,如果一个全局的符号在A文件中定义,在B文件中想使用,就需要使用 extern 进行声明。
4.3.2 static
修饰局部变量
static 修饰局部变量改变了变量的⽣命周期,⽣命周期改变的本质是改变了变量的存储类型,本来⼀个局部变量是存储在内存的栈区的,但是被 static 修饰后存储到了静态区。存储在静态区的变量和全局变量是⼀样的,⽣命周期就和程序的⽣命周期⼀样了,只有程序结束,变量才销毁,内存才 回收。但是作用域不变的。
修饰全局变量或函数
⼀个全局变量被static修饰,使得这个全局变量只能在本源⽂件内使⽤,不能在其他源⽂件内使⽤。 本质原因是全局变量默认是具有外部链接属性的,在外部的⽂件中想使⽤,只要适当的声明就可以使⽤;但是全局变量被 static 修饰之后,外部链接属性就变成了内部链接属性,只能在⾃⼰所在的源⽂件内部使⽤了,其他源⽂件,即使声明了,也是无法正常使⽤的。函数同理
test2.c
#define _CRT_SECURE_NO_WARNINGS
//定义全局变量
//全局变量默认具有链接属性,其他文件可以通过extern进行访问
//如果此时加上static,则变为内部链接属性,其他源文件无法访问
//函数原理同上
int num = 10;
test.c
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
//static 和 extern关键字的使用
void test();//函数声明
extern int num;//声明外部变量
int main()
{
for (int i = 0; i < 5; i++)
{
test();
}
printf("num = %d\\n", num);
return 0;
}
void test()
{
//静态变量,只初始化一次(在程序编译时),仅改变生命周期,生命周期为整个程序运行期间
//如果不加入static关键字,则每次调用变量时,变量都会重新初始化,即局部变量
static int a = 0;
a++;
printf("%d\\n", a);
}
数组和函数实践:简易扫雷游戏实现
声明
本文是在鹏哥 C 语言集训营学习过程中所记录的学习笔记,梳理了核心知识点,同时也记录了本人实操验证的代码案例,供后续学习复盘使用。
网硕互联帮助中心




评论前必须登录!
注册