☰
【C语言】函数全解析
2026/10/3 2:10:25 网站建设 项目流程

目录

1.函数的概念

2.库函数

3.自定义函数

4.形参和实参

5.return语句

6.数组做函数的参数

7.嵌套调用和链式访问

8.函数的声明和定义


1.函数的概念

函数也叫子程序,C语言的函数就是一个完成某项特定任务的一小段代码。

C语言中的两类函数:

  • 库函数
  • 自定义函数

2.库函数

库函数的学习工具:

  • C/C++官⽅的链接:https://zh.cppreference.com/w/c/header
  • 中⽂版:https://cppreference.cn/w/
  • cplusplus.com:https://legacy.cplusplus.com/reference/clibrary/

3.自定义函数

3.1函数的语法形式

ret_type fun_name(形式参数)

{

}

函数头由函数名、参数、返回值类型组成

  • ret_type:表示计算结果的类型;返回类型是void,表示什么都不返回
  • fun_name:函数名,尽量有意义
  • 函数的参数:函数如果有参数,就需要交代清楚参数的类型和名字,以及参数个数;函数的参数也可以是void,表示函数没有参数
  • { }:括起来的是函数体,完成计算过程
  • return语句:函数中可能有也可能没有return语句(返回类型是void时,return语句可以不写,也可以写成 return;)

注:一个函数必须要有函数名、返回类型(/void)和函数体

3.2举例

写⼀个加法函数,完成2个整型变量的加法操作

#include<stdio.h> int Add(int x, int y) { int z = x + y; return z; } int main() { int a = 0; int b = 0; scanf("%d%d", &a, &b); int s = Add(a, b);// a,b的和,调用加法函数,完成a和b的相加 printf("%d\n", s); return 0; }

add函数部分也可以简化为

int Add(int x,int y) { return x + y;//return后边可以是数值也可以是表达式,如果是表达式则先执行表达式,再返回表达式的结果 }

4.形参和实参

实参:实参是真实传递给函数的参数

形参:上述例子中,在函数名Add 后的括号中写的 x 和 y 就是形参。

当没有调用Add函数时,不会创建x,y变量,只是形式上存在,不会向内存申请空间—形参是实参的临时拷贝;

只有在函数被调用的过程中,为了存放实参传来的值才向内存申请空间,这就是形参的实例化。

形参和实参的关系:实参是传递给形参的,但是形参和实参各自是独立的内存空间

int Add(int x, int y)//形参有自己独立的空间,是实参的临时拷贝 { return x + y; } int main() { int a = 0; int b = 0; scanf("%d%d", &a, &b);//实参是传递给形参的,但是形参和实参各自是独立的内存空间 int c = Add(a, b); printf("%d\n", c); return 0; }


5.return语句

(1)函数返回类型是void时,return语句可以不写,也可以写成 return;

(2)返回类型是非void时,若没有使用return语句,那么函数的返回值是未知的;

(3)return后边可以是⼀个数值,也可以是⼀个表达式,如果是表达式则先执⾏表达式,再返回表达式的结果

(4)return语句执⾏后,函数就彻底返回,后边的代码不再执⾏

(5)return返回的值和函数返回类型不⼀致,系统会⾃动将返回的值隐式转换为函数的返回类型

int test() { return 3.14; } int main() { int r = test(); printf("%d\n", r);//3 return 0; }

(6)如果函数中存在if等分⽀的语句,则要保证每种情况下都有return返回,否则会出现编译错误

(7)函数的返回类型如果不写,编译器会默认函数的返回类型是int

(8)小练习

判断一个年份是否是闰年

int is_leap_year(int y) { if ((y % 4 == 0) && (y % 100 != 0) || (y % 400 == 0)) return 1; else return 0; } int main() { int year = 0; scanf("%d", &year); if (is_leap_year(year) == 1) { printf("是闰年\n"); } else { printf("不是闰年\n"); } return 0; }

6.数组做函数的参数

将数组作为参数传递给函数,在函数内部对数组进⾏操作

写⼀个函数将⼀个整型数组的内容,全部置为-1,再写⼀个函数打印数组的内容

void set_arr(int arr[],int sz) { int i = 0; for (i = 0;i < sz;i++) { arr[i] = -1; } } print_arr(int arr[],int sz) { int i = 0; for (i = 0;i < sz;i++) { printf("%d ", arr[i]); } } int main() { int arr[10] = { 0 }; int sz = sizeof(arr) / sizeof(arr[0]); set_arr(arr,sz); print_arr(arr, sz); return 0; }

数组传参:

• 函数的形式参数要和函数的实参个数匹配

• 函数的实参是数组,形参也是可以写成数组形式的

• 形参如果是⼀维数组,数组大小可以省略不写

• 形参如果是二维数组,行可以省略,但是列不能省略

•数组传参,形参是不会创建新的数组的

•形参操作的数组和实参的数组是同⼀个数组


7.嵌套调用和链式访问

(1)嵌套调用就是函数之间的互相调用

假设我们计算某年某月有多少天?如果要函数实现,可以设计2个函数

//确定是否是闰年——is_leap_year()

//再根据月计算这个月的天数——get days of month

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_month(int y,int m)//某年某月有多少天 { int days[] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 };//从0开始,月份正好和下标对应 int day = days[m]; if ((is_leap_year(y) == 1) && m == 2)//嵌套调用 day += 1;//闰年的2月有29天 return day; } int main() { int year = 0; int month = 0; scanf("%d%d", &year, &month); int d = get_days_of_month(year,month); printf("%d\n", d); return 0; }

(2) 链式访问:将一个函数的返回值作为另一个函数的参数,像链条一样串起来

int main() { /*6*/ printf("%d\n", strlen("abcdef"));//链式访问 return 0; }
int main() { printf("%d ", printf("%d ", printf("%d ", 46)));//46 3 2 return 0; } //printf()的返回值是字符个数 //注意有没有空格
int main() { printf("%d", printf("%d", printf("%d", 46)));//4621 return 0; }

8.函数的声明和定义

8.1单个文件

一般在写函数的时候会将函数的定义放在函数调用之前,如下:

橙色部分是函数的定义,绿色部分是函数的调用

这种写法没有问题,但是当把函数的定义放在函数调用之后,就会产生问题

这是因为C语⾔编译器对源代码进⾏编译的时候,从第⼀⾏往下扫描的,当遇到第7⾏的函数调⽤的时候,并没有发现前⾯有 is_leap_year的定义,就报出了上述的警告

解决方法:

只要在调用前声明一下is_leap_year这个函数就可以了,声明函数要交代清楚函数名,函数的返回类型和函数的参数。函数声明中参数只保留类型,省略掉名字也是可以的。

如上述例子中:将函数的声明 bool is_leap_year(int y); 放在函数调用之前就可以解决

总结:函数的使用一定要先声明后定义

函数的定义也是一种特殊的声明,所以如果函数定义放在调用之前也是可以的

8.2多个文件

一般情况下,函数的声明、类型的声明放在头文件(.h)中,函数的实现是放在源文件(.c)文件中。

那么该如何使用呢?只要在调用函数之前包含头文件就可以了,如下:

8.3 static和extern

先了解作用域和生命周期

(1)作用域和生命周期

作用域:⼀段程序代码中所⽤到的名字并不总是有效(可⽤) 的,⽽限定这个名字的可⽤性的代码范围就是这个名字的作⽤域

1.局部变量的作用域是变量所在的局部范围

2.全局变量的作用域是整个工程(项目)

生命周期:指的是变量的创建(申请内存)到销毁(收回内存)之间的一个时间段

1.局部变量的生命周期是:进入作用域,变量创建,生命周期开始,出作用域生命周期结束

2.全局变量的生命周期是:整个程序的生命周期

(2)static和extern

static和extern都是C语言中的关键字

static可以:

  • 修饰局部变量
  • 修饰全局变量
  • 修饰函数

extern是用来声明外部符号的

1 - extern

extern是用来声明外部符号的

如果一个全局的符号在A文件中定义,在B文件想使用,就可以使用extern进行声明

//声明来自外部的变量

//声明来自外部的函数

1 - static修饰局部变量

在上面代码中,n 是局部变量,当进入 test() 这个函数之后,变量n才创建,此时生命周期开始;出这个函数之后,变量销毁,生命周期结束,所以这里只会打印 11 11 11 11 11

而static修饰局部变量可以改变生命周期,如下:

static拉长了生命周期

总结:static修饰局部变量,可以拉长生命周期(相当于全局变量的生命周期),但是作用域不会改变。(生命周期的改变本质是改变了变量的存储类型,本来局部变量是存在内存的栈区的,但是被static修饰后存到了静态区。存在静态区的变量和全局变量是一样的,生命周期就和程序的生命周期一样了)

使用建议:未来⼀个局部变量出了函数后,我们还想保留值,等下次进⼊函数继续使用,就可以使用 static 修饰

2 - static修饰全局变量

全局变量默认具有外部链接属性

在上述代码中,文件 lesson_test.c 中,test变量被static修饰,则该变量在文件lesson_8_4_20.c中不能被使用;

结论:一个全局变量被static修饰,这个全局变量就只能在该文件中使用,不能在其他文件中使用

(全局变量默认具有外部链接属性,而被static修饰的全局变量,变成内部链接属性)

使用建议:如果一个全局变量,只想在所在的源文件内部使用,不想被其他文件发现,就可以用static修饰

3 - static修饰函数

结论:static修饰函数和修饰全局变量是一样的,一个函数被static修饰后变成了内部链接属性,使函数只能在自己所在的源文件内部使用

使用建议:一个函数只想在所在的源文件内部使用,不想被其他源文件使用,就可以用static修饰

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询