1.函数的作用
- 避免重复性代码
- 让程序更加模块化
2.函数的三种形式
函数的定义、函数的调用、函数的声明
1.函数的定义
定义一个函数的实现
1.定义形式
函数类型 函数名(数据类型1 形式参数1, 数据类型2 形式参数2, 数据类型3 形式参数3,
...)
{
函数体;
return 返回值;
}
int fun(int x, int y)
{
函数体;
return 0;
}
函数类型:函数运行结果的类型 int void char double float
函数名:与变量名要求保持一致
形式参数:对数据操作的方式(函数定义时形参必须有类型)
返回值:函数的运行结果
2.函数的调用
使用形式
函数名(实参1, 实参2);
该表达式最终结果为函数return后面的值
该表达式最终类型为函数类
示例
int add(int a, int b) {
return a + b;
}
int main() {
int result = add(3, 5); // 传递两个整数参数
printf("Result: %d\n", result);
return 0;
}
-
主调函数:主动调用别的函数的函数称为主调函数
-
被调函数:被主调函数调用的函数称为被调函数
3.函数的声明
在C语言中,函数声明(也称为函数原型)用于告诉编译器函数的存在及其参数和返回类型,通常出现在函数定义之前。
声明方式:
如果被调函数在主调函数的下方定义需要再主调函数上方声明
如果被调函数在主调函数的上方定义,定义时已经完成函数的声明
return_type function_name(parameter_list);
-
return_type
:函数返回值的数据类型(如int
、float
、void
等)。 -
parameter_list
:参数类型和名称(可选),多个参数用逗号分隔。
示例
// 头文件 math_utils.h
#ifndef MATH_UTILS_H
#define MATH_UTILS_H
int add(int x, int y); // 声明加法函数
double sqrt(double value); // 声明平方根函数
#endif
// 源文件 main.c
#include "math_utils.h"
#include <stdio.h>
int main() {
printf("Sum: %d\n", add(3, 5));
return 0;
}
3.变量的作用域和生存周期
在C语言中,变量的作用域和生存周期由存储类别(auto
、static
、register
、extern
)和声明位置(全局或局部)决定。以下是分类总结:
1. 局部变量(自动变量)
- 声明方式: 在函数或块内声明,默认为
auto
(可省略)。 - 作用域: 仅限于声明它的函数或块内。
- 生存周期: 从进入函数或块时创建,到退出时销毁。
- 示例:
void func() { int x = 10; // 局部变量 }
2. 静态局部变量
- 声明方式: 在函数或块内用
static
修饰。 - 作用域: 仅限于声明它的函数或块内。
- 生存周期: 程序运行时创建,程序结束时销毁(仅初始化一次)。
- 示例:
void func() { static int count = 0; // 静态局部变量 }
3. 全局变量
- 声明方式: 在函数外声明,默认为
extern
(可省略)。 - 作用域: 从声明处到文件末尾,其他文件通过
extern
引用。 - 生存周期: 程序运行时创建,程序结束时销毁。
- 示例:
int global = 100; // 全局变量 void func() { extern int global; // 引用其他文件的全局变量 }
4. 静态全局变量
- 声明方式: 在函数外用
static
修饰。 - 作用域: 仅限于当前文件(不可被其他文件
extern
引用)。 - 生存周期: 程序运行时创建,程序结束时销毁。
- 示例:
static int file_scope = 200; // 静态全局变量
5. 寄存器变量
- 声明方式: 用
register
修饰(建议编译器将变量存储在寄存器中)。 - 作用域: 与局部变量相同(仅限于声明它的块内)。
- 生存周期: 与局部变量相同(块结束时销毁)。
- 示例:
void func() { register int i; // 寄存器变量 }
总结表格
变量类型 | 作用域 | 生存周期 | 存储类别 |
---|---|---|---|
局部变量 | 函数/块内 | 函数/块执行期间 | auto |
静态局部变量 | 函数/块内 | 整个程序运行期间 | static |
全局变量 | 文件或跨文件 | 整个程序运行期间 | extern |
静态全局变量 | 当前文件内 | 整个程序运行期间 | static |
寄存器变量 | 函数/块内 | 函数/块执行期间 | register |
关键区别
- 静态变量(
static
)的生存周期为整个程序运行期间,但作用域取决于声明位置。 - 全局变量默认跨文件可见,而静态全局变量仅限当前文件。
- 寄存器变量是局部变量的优化建议,实际是否存入寄存器由编译器决定。
6.代码运行时,内部空间分配
C语言程序运行时内存空间分配
C语言程序在运行时,内存通常分为以下几个主要区域,其分配方式和管理特点如下:
内存区域 | 存储内容 | 生命周期 | 分配/释放方式 | 典型大小限制 |
---|---|---|---|---|
代码区 | 编译后的机器指令(二进制代码) | 整个程序运行期间 | 编译器/操作系统静态分配 | 由系统剩余内存决定 |
全局/静态区 | 全局变量、静态变量(static 修饰) | 整个程序运行期间 | 编译时固定地址 | 系统限制(通常较大) |
常量区 | 字符串常量、const 全局变量 | 整个程序运行期间 | 编译器分配 | 系统限制 |
栈区 | 局部变量、函数参数、返回地址 | 函数调用开始到结束 | 编译器自动管理 | 较小(Linux默认8MB,Windows1MB) |
堆区 | 动态分配内存(malloc /calloc 等) | 程序员控制(free 释放前持续存在) | 手动分配/释放 | 受系统虚拟内存空间限制 |
4.递归函数
递归函数是指在函数体内调用自身的函数。递归通常用于解决可以分解为相似子问题的情况,例如阶乘、斐波那契数列、汉诺塔等问题。递归函数需要满足两个条件:
- 基线条件(Base Case):递归终止的条件,防止无限递归。
- 递归条件(Recursive Case):函数调用自身,逐步向基线条件靠近
递归函数的实现示例
示例1:计算阶乘
#include <stdio.h>
int fun (int num)
{
if (1 == num)
{
return 1;
}
else
{
return num * fun (num - 1);
}
}
int main(void)
{
int ret = 0;
int a = 0;
scanf("%d",&a);
ret = fun(a);
printf("ret = %d\n",ret);
return 0;
}
示例二:汉诺塔
1 #include <stdio.h>
2
3 int hannuota(int n,char A,char B,char C)
4 {
5 if(1 == n)
6 {
7 printf("%c -> %c\n",A,C);
8 }
9 else
10 {
11 hannuota(n-1,A,C,B);
12 printf("%c -> %c\n",A,C);
13 hannuota(n-1,B,A,C);
14 }
15
16 return 0;
17 }
18
19 int main(void)
20 {
21 int n = 0;
22
23 printf("请输入有多少个盘子:");
24
25 scanf("%d",&n);
26
27 hannuota(n,'A','B','c');
28
29 return 0;
30 }
5.函数传参
1.值传递
函数调用时,将实参的值复制一份传给形参。形参是实参的副本,修改形参不会影响实参。
void swap(int a, int b) {
int temp = a;
a = b;
b = temp;
}
int main() {
int x = 1, y = 2;
swap(x, y); // x和y的值不会被交换
}
2.整型数组传递
1.两种形式
int fun(int array[5]);
int fun(int array[], int len);
注意:数组传递到函数体内部与外部操作的是同一数组
示例
#include <stdio.h>
int sortfun(int a[],int len)
{
int j = 0;
int i = 0;
27 int tmp = 0;
28 for(j = 0;j < len - 1;j++)
29 {
30 for(i = 0;i < len - 1- j;i++)
31 {
32 if(a[i] > a[i+1])
33 {
34 tmp = a[i];
35 a[i] = a[i + 1];
36 a[i + 1] = tmp;
37 }
38 }
39 }
40 return 0;
41 }
42
43 int putfun(int a[],int len)
44 {
45 int i = 0;
46 for(i = 0;i < len;i++)
47 {
48 scanf("%d",&a[i]);
49 }
50 }
51
52 int dayin(int a[],int len)
53 {
54 int i = 0;
55 for(i = 0;i < len;i++)
56 {
57 printf("a[%d] = %d ",i,a[i]);
58 }
59 printf("\n");
60 return 0;
61 }
62 int main(void)
63 {
64 int num[5] = {0};
65 putfun(num,sizeof(num)/sizeof(num[0]));
66 sortfun(num,sizeof(num)/sizeof(num[0]));
67 dayin(num,sizeof(num)/sizeof(num[0]));
68
73
74 return 0;
75 }
3.字符型数组传递
int fun(char str[]);
示例
封装strlen功能
1 #include <stdio.h>
2
3 int getstrlen(char fstr[])
4 {
5 int i = 0;
6 while(fstr[i] != '\0')
7 {
8 i++;
9 }
10 return i;
11 }
12
13 int main(void)
14 {
15 int len = 0;
16 char str[32] = {0};
17
18 gets(str);
19
20 len = getstrlen(str);
21
22 printf("len = %d\n",len);
23
24 return 0;
25 }