首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >##C语言——学习攻略 :函数部分实用指导

##C语言——学习攻略 :函数部分实用指导

作者头像
雾忱星
发布2025-12-23 13:13:02
发布2025-12-23 13:13:02
1410
举报
文章被收录于专栏:C++C++

前言:在前一章,学习了数组相关的知识,在本章将会学到函数相关知识 ,一起加油吧!

1. 关于什么是函数

--关于函数,学习数学时就对函数有过接触。就比如一次函数:y=kx+b;

--在C语言中也引入了函数概念,我们可以将其理解成“子程序”,意味着函数只是完成某个要实现的功能的一段代码。

C语言中函数会见到两类:

  • 库函数
  • 自定义函数

2. 两类函数(相关知识)

2.1 库函数

--库函数是由C语言标准库提供的,我们可以直接调用(之前用到的;main、scanf等);

当我们先想学习库函数时,提供了一些网站:

C/C++官方:https://en.cppreference.com/w/c/header.html

cplusplus.com:C library - C++ Reference

2.1.1 库函数举例——sqrt
代码语言:javascript
复制
形式:
double sqrt (double x);
//sqrt 是函数名;
//x 是函数参数,这表示调用sqrt函数时需要传递一个double类型的数值;、
//double 是返回值类型-表示函数计算结果使都变了类型;
  • 功能

Compute square root 计算平方根 Returns the square root of x. (返回平方根)

  • 头文件包括

库函数相关头文件:https://zn.cppreference.com/w/c/header.html

因为函数是在标准库中对应头文件中声明的,所以在使用时要包含相应头文件;

  • 实践应用
代码语言:javascript
复制
#include <stdio.h>
#include <math.h>//包含头文件
int main()
{
	//调用函数提供一个double的数值
	double d = 16.0;
	//调用函数,将d传递给函数
	double r = sqrt(d);
	printf("%lf", r);//打印double用lf
	return 0;
}

打印:4.000000

注:更多详细内容请移步上面网址 ( ͡° ͜ʖ ͡°)

2.2 自定义函数

--显然,标准库所提供函数并不能满足所有需求,这时候就需要重视自定义函数的作用;

代码语言:javascript
复制
形式:
return _type fun_name(形式参数)
{
    函数体


    return 返回值;
}

·return _type 是函数返回类型
·fun_name 是函数名

补充说明:

  • 函数无法返回数组;
  • 省略返回值时,在C89中默认为int类型
  • 当指定函数返回类型是void型,说明函数无返回值;
  • 对于void型函数,函数体可以为空;
  • 形式参数在设定时,需要依次指定类型 ,且多个形式参数间需要“”;没有形式参数时在()中出现void
2.2.1 函数调用

--在说自定义函数时,先了解函数在程序中的调用! (≧∇≦)ノ

--前面说函数是如何创建的,函数调用就是用函数名和实参构成,其中实参用()括起; (关于实参的介绍在后面) (¬‿¬)

代码语言:javascript
复制
Add(x, y)
average(a, b)

--对于voide函数,要在函数调用后面始终加“” ,使其成为一个语句;

代码语言:javascript
复制
printf_pun();
对于将函数值赋给变量时,需要加分号;
avg = average(x, y);
2.2.2 自定义函数举例

--在定义函数时,我们先将基本形式写出来,再对照思路写函数。

例1——写一个函数实现加法运算

思路:首先写通常情况下实现加法的代码,再据思路写函数

代码语言:javascript
复制
#include <stdio.h>
int main()
{
	//首先给出两个变量
	int a = 0;
	int b = 0;
	//输入想要加法运算的数值
	scanf("%d %d",&a, &b);
	//将输入的数值进行加法后赋值给c
	int c = a + b;
	//打印出最后加法结果
	printf("%d", c);
	return 0;
}

--将加法函数命名Add,返回类型int,可知形参为两个int;

代码语言:javascript
复制
int Add(int a, int b)
{
	return a + b;
}

整合后:

代码语言:javascript
复制
#include <stdio.h>
int Add(int a, int b)
{
	return a + b;

}

int main()
{
	int x = 0;
	int y = 0;
	scanf("%d %d", &x, &y);
	//调用加法函数
	printf("%d\n", Add(x, y));
	return 0;
}
2.2.3 函数声明

--对于函数的定义放置位置没有严格规定,可以放在调用点的前面或后面;

--但放在后面时,最好对函数进行声明,以便函数调用时可以知道函数的信息;

函数声明类似于函数定义的第一行:

代码语言:javascript
复制
return _type fun_name(形参);
函数返回类型    函数名

(ノ`Д´)ノ 特别注意:声明在结尾处有分号。


3. 形式参数和实际参数

3.1 形参

--形参出现在函数定义中,以假名字来表示函数调用时需要提供的值,在前面定义函数时,int Add(int a, int b),其中a、b就是形参;

--对于形参,在不调函数时,不会真实存在;当调函数时,形参用来存放实参数传递过来的值,并且向内存申请空间。

3.2 实参

--在前面,printf语句中调用函数:Add(x, y) , x、y称为实参。

实参是通过值传递的:调用函数时,计算每个实参的值并赋给相应的形参。

3.3 关系

对比项

形参

实参

联系

定义

函数定义中所声明的变量,用于接收实际参数传递值

在调用函数时传递的具体内容

实参数值会传递给形参

位置

函数声明部分

函数调用时的参数

在调用函数时,实参和形参一一对应

变量名

形参名只在函数内部有效,可以与实参名不同

当然,也存在数组做实参的情况,会在后面介绍; (ノ◕ヮ◕)ノ*:・゚✧


4. return 语句(程序终止)

代码语言:javascript
复制
形式:
return 表达式;

--表达式通常是常量或变量,也可能是更复杂的表达式。

注意事项:

  • return语句后可以是数值或表达式;如果是表达式,则会先执行表达式再返回算出的结果;
  • return语句返回的值和函数返回类型不一致,系统会将返回的值转换成函数返回类型;
  • return语句执行后,函数彻底返回,不管后面有没有代码,都不执行;
  • 函数中存在分支语句,必须确保每种情况下都有return返回;

对于return返回void——

代码语言:javascript
复制
void greet()
{
	printf("Hello everyone!");
    return;//可以省略
}

5. 数组做函数参数

--数组经常被当作实参;当形参是一维数组时,通常不说明数组长度。

代码语言:javascript
复制
int f(arr[])
{
......
}

--那么就有一个问题,函数怎么知道数组长度呢? (・_・?)

--由于C语言没有提供方法来确定长度,需要自己来提供长度。这时可能想到在数组部分使用了 sizeof,比如int len = size of(arr) / size of(arr[0]) ,但在函数内部使用是错误的。(原因在指针方面会提到)

--折中的办法是,在main函数中计算出数组长度赋给一个变量,将变量作参数传给函数。

这里先说一下数组作参数的注意事项: (⁄ ⁄>⁄ ▽⁄<⁄ ⁄)

  • 当形参是一维数组时,通常不说明长度;多维数组只能省略第一维长度。
  • 当把数组名传递给函数时,不用放置 [ ] 。

例2——将整型数组内容全置-1、打印出数组的内容;

思路——

  1. 先写main函数的基本形成,写出要调用的函数形式;
  2. 根据基本代码先写出函数定义;
  3. 将二者整合后形成最终代码;
代码语言:javascript
复制
#include <stdio.h>
main函数基本实现
int main()
{
	int arr[] = {1,2,3,4,5,6,7,8,9,10};
	int sz = sizeof(arr) / sizeof(arr[0]);
	
	//将数组元素全部置-1
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		arr[i] = -1;
	}
	//打印数组元素
	for (i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}
	return 0;
}

函数定义
void set_arr(int arr[], int sz)
{
	int i = 0;
	//下面就要利用循环使数组全部置为-1
	for (i = 0; i < sz; i++)
	{
		arr[i] = -1;
	}
	
}

void printf_arr(int arr[], int sz)
{
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}
	printf("\n");
}

整合
int main()
{
	int arr1[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
	int sz = sizeof(arr1) / sizeof(arr1[0]);

	//调用函数使数组内容置-1
	set_arr(arr1, sz);

	//调用函数,打印数组内容
	printf_arr(arr1, sz); 
	return 0;
}

6. 嵌套调用和链式访问

6.1 嵌套调用

--函数嵌套调用:指函数之间互相调用,形成大型代码程序;但函数不能嵌套定义。

嵌套定义:在一个函数内部定义另一个函数。(在C语言中是不允许的)

例3——计算某年某月有多少天? 。ヾ(◍°∇°◍)ノ✧

思路——

  1. 因为年份分平年与闰年导致2月天数不同;首先定义函数判断闰年。
  2. 再定义函数,用来接收输入的数值(年、月份),输出天数;可以创建一个数组存储天数;因为2月份的特殊性,需要针对判断。
代码语言:javascript
复制
#include <stdio.h>//函数1
int is_leap_year(int y)
{
	if ((y % 4 == 0) && (y % 100 != 0) || (y % 400 == 0))
	{
		return 1;
	}
	else
	{
		return 0;
	}
}

//函数2
int get_days_of_month(int y, int m)
{
	int days[] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
    //因为输入时给访问数组的地址,从0开始,而我们要从1开始输入,那可以将第0位占用
	int day = days[m];
	if (is_leap_year(y) == 1 && m == 2)
	{
		day++;
	}
	return day;
}

//主体
int main()
{
	int y = 0;
	int m = 0;
	scanf("%d %d", &y, &m);
	int d = get_days_of_month(y, m);
	printf("%d \n", d);
	return 0;
}

6.2 链式访问(strlen函数)

--链式访问:将函数的返回值作为另一个函数的参数。

比如——

代码语言:javascript
复制
#include <stdio.h>
#include <string.h>
int main()
{
	size_t len = strlen("abcdef");
	printf("%zu\n", len);
	return 0;
}

//将strlen
int main()
{
	printf("%zu\n", strlen("abcdef"));
	return 0;
}

对与strlen函数,这里做点笔记:

  • 头文件—<string.h>
  • 返回类型—size_t(如果不包含头文件,则会返回int)(size of也是返回size_t)
  • 打印时使用—%zd/%zu
  • 计算字符串—不算"\0"(sizeof会计算)

(这是因为strlen计算的是字符串长度,而sizeof计算的是内存大小


7. 函数的声明和定义

--对于单个文件,前面的( ͡° ͜ʖ ͡°) 2.2.3 有介绍,不在赘述;

7.1 多个文件

--在实际中,不会将函数的定义、声明、调用等放在同一源文件中。

通常是将它们分在.h文件.c文件中 ——(以自定义add函数示范/自行添加文件)

  • 函数声明放置在.h文件
代码语言:javascript
复制
add.h
int add(int x, int y);
  • 函数定义和调用分别放置在不同的.c文件
代码语言:javascript
复制
add.c
int add(int x, int y)
{
    return x + y;
}
代码语言:javascript
复制
test.c
#include <stdio.h>
#include "add.h"

int main()
{
    int c = 10;
    int d = 20;
    int sum = add(c, d);
    printf("%d\n", sum);
    return 0;
}

请注意:包含自定义的头文件时,用""


8. static和extern

--在对解释两种函数作用是,先简单来点铺垫工作。 (。-ω-)zzz`

8.1 变量的作用域和生命周期

作用域:限定变量可用范围;生命周期:变量从生成到撤销的时间段(内存分配到内存释放/变量占用内存的时间)

局部变量:作用域—创建变量坐在的小区间;生命周期—进入变量的作用域时开始,出作用域时结束。

全局变量;作用域—整个项目程序;生命周期—整个程序的生命周期。

8.2 static修饰局部变量

--static会显著改变变量的存储期、生命周期,不会改变作用域;发生改变实际是延长了生命周期,普通变量存储在栈内存中(函数调用时创建,返回时销毁/生命周期=函数的执行期间)每次调用函数都会重新创建变量,而static修饰变量存储在静态存储区(程序启动时分配,结束释放/生命周期=程序的运行时间)重新调用函数不会再创建变量,对上次结果累加计算。

--static局部变量只初始化一次,在后续函数的调用会保留上一次修改后的数值。

代码实现——

代码语言:javascript
复制
void test()
{
	int i = 0;
	i++;
	printf("%d", i);
}
int main()
{
	int i;
	for (i = 0; i < 5; i++)
	{
		test();
	}
	return 0;
}
输出:11111
代码语言:javascript
复制
void test()
{
	static int i = 0;
	i++;
	printf("%d", i);
}
int main()
{
	int i;
	for (i = 0; i < 5; i++)
	{
		test();
	}
	return 0;
}
输出:12345

8.3 static修饰全局变量(提到extern)

--被static修饰的全局变量只能在所在的源文件(.c文件)中使用。其本质:全局变量默认是有外部链接属性的,当想在其他源文件中使用只需要 extern 进行外部声明;但是static修饰后,外部链接属性就改成了内部链接属性,即使进行 extern 也没用。

--"extern"——“外部的”,即用来声明外部符号的。

8.4 static修饰函数

--static修饰函数本质上与修饰全局变量一样,函数在整个工程中进行适当声明后都能使用(分为几个.c文件),而修饰后只能在一个文件中使用

结语:本篇文章到此结束,主要讲了函数的一些内容,后续会继续学习函数递归;如果这篇文章对你的学习有帮助的话,欢迎三连~~~

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2025-12-04,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1. 关于什么是函数
  • 2. 两类函数(相关知识)
    • 2.1 库函数
      • 2.1.1 库函数举例——sqrt
    • 2.2 自定义函数
      • 2.2.1 函数调用
      • 2.2.2 自定义函数举例
      • 2.2.3 函数声明
  • 3. 形式参数和实际参数
    • 3.1 形参
    • 3.2 实参
    • 3.3 关系
  • 4. return 语句(程序终止)
  • 5. 数组做函数参数
  • 6. 嵌套调用和链式访问
    • 6.1 嵌套调用
    • 6.2 链式访问(strlen函数)
  • 7. 函数的声明和定义
    • 7.1 多个文件
  • 8. static和extern
    • 8.1 变量的作用域和生命周期
    • 8.2 static修饰局部变量
    • 8.3 static修饰全局变量(提到extern)
    • 8.4 static修饰函数
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档