概述
C语言中的函数是一个独立的、可重用的代码块,它执行特定任务并可以接受输入参数,并可能返回一个结果。函数是C语言程序设计的核心组件之一,它们有助于将大型复杂的程序分解为小的、易于管理及测试的部分。
定义与声明
函数定义包含了函数体(实际执行的代码)以及函数头,函数头中包括函数名、参数列表和返回类型。
返回类型 函数名(参数列表) {
// 函数体(包含执行的语句)
}
例:
// 定义一个计算两数之和的函数,返回值类型为int,函数名为addNumbers,接受两个整型参数a和b
int addNumbers(int a, int b) {
// 函数体,实现加法操作并返回结果
int sum = a + b;
return sum;
}
函数声明(也称为函数原型)提供给编译器函数的基本信息,以便在调用函数之前进行类型检查。声明通常出现在函数调用之前或在源文件头部。例:
// 声明addNumbers函数,告知编译器该函数的存在、返回类型以及参数列表
int addNumbers(int a, int b);
函数分类
库函数:C语言标准库提供的内置函数,如数学函数
abs()
、acos()
等。
自定义函数:程序员根据需求自定义的函数,用于完成特定功能。
参数传递
实际参数(实参)是在调用函数时传入的实际值。
形式参数(形参)是在函数定义中接收实参的变量。
例:
// 函数定义:声明一个名为addNumbers的函数,它有两个形参x和y,返回值类型为int
int addNumbers(int x, int y) {
// 函数体:计算两个数之和,并返回结果
return x + y;
}
int main() {
// 定义两个整型变量作为实参
int a = 10;
int b = 20;
// 函数调用:将a和b作为实参传递给addNumbers函数
int result = addNumbers(a, b);
// 输出函数调用的结果
printf("The sum is: %d\n", result);
return 0;
}
在这个例子中:
形参:
int x
和int y
是在函数addNumbers
的定义中的形参。实参:当在
main
函数中调用addNumbers(a, b)
时,a
和b
就是传递给addNumbers
函数的实参,它们分别与形参x
和y
对应。
返回值
函数可以有返回值,通过return
语句从函数内部返回到调用处。例:
// 函数定义,该函数计算两数之积,并返回结果
int multiplyNumbers(int num1, int num2) {
int product = num1 * num2;
return product; // 返回乘积作为函数的结果
}
int main() {
int m = 3;
int n = 4;
// 调用multiplyNumbers函数并将返回值打印出来
int result = multiplyNumbers(m, n);
printf("The product is: %d\n", result);
return 0;
}
若函数没有返回值,则其返回类型应为void
。例:
#include <stdio.h>
// 定义一个无返回值函数(void类型)
void welcomeMessage() {
printf("Welcome to our program!\n");
printf("We are glad you're here.\n");
}
int main() {
// 调用无返回值函数
welcomeMessage();
return 0;
}
调用方式
传值调用:函数接收到的是实参的副本。例:
#include <stdio.h>
// 定义一个通过传值调用来计算两个整数之和的函数
int addNumbers(int a, int b) {
// 计算两个整数的和
int sum = a + b;
// 返回结果
return sum;
}
int main() {
// 定义并初始化两个整数
int num1 = 5;
int num2 = 10;
// 调用addNumbers函数,传递的是num1和num2的值
int result = addNumbers(num1, num2);
// 输出结果
printf("The sum of %d and %d is: %d\n", num1, num2, result);
return 0;
}
在这个示例中,addNumbers
函数接收两个 int
类型的参数 a
和 b
。在 main
函数中调用 addNumbers(num1, num2)
时,将实际参数 num1
和 num2
的值复制给形式参数 a
和 b
。由于是传值调用,所以对 a
和 b
的任何修改都不会影响到 main
函数中的 num1
和 num2
值。函数计算两个参数的和后,将结果通过 return
语句返回给主调函数(这里是 main
)。
传址调用:通过指针传递,函数可以直接修改实参所指向的内存区域。例:
#include <stdio.h>
// 定义一个通过传址调用来交换两个整数的函数
void swapNumbers(int* a, int* b) {
// 临时保存其中一个数的值
int temp = *a;
// 将一个数的值赋给另一个数
*a = *b;
// 再将临时变量的值赋回原位置
*b = temp;
}
int main() {
// 定义并初始化两个整数
int num1 = 5;
int num2 = 10;
printf("Before swap: num1 = %d, num2 = %d\n", num1, num2);
// 调用swapNumbers函数,传入的是num1和num2的地址
swapNumbers(&num1, &num2);
printf("After swap: num1 = %d, num2 = %d\n", num1, num2);
return 0;
}
在这个示例中,swapNumbers
函数接受两个 int
类型的指针作为参数,当我们从 main
函数调用它时,我们传递的是 num1
和 num2
的地址(通过取地址符 &
获取)。在函数内部,通过对指针解引用 (*a
和 *b
) 来访问和修改原始变量的值,实现了不返回任何值但能改变外部状态的效果。
嵌套调用与递归
嵌套调用:在一个函数内部调用另一个函数。例:
#include <stdio.h>
// 定义一个函数A,它在内部调用了另一个函数B
void functionA(int n) {
printf("Inside functionA\n");
// 嵌套调用函数B
functionB(n);
}
// 定义被嵌套调用的函数B
void functionB(int n) {
if (n > 0) {
printf("Inside functionB, n = %d\n", n);
// 减小参数值并递归调用自身(这里演示嵌套而非递归)
functionB(n - 1);
}
}
int main() {
int num = 3;
// 调用函数A
functionA(num);
return 0;
}
functionA()
内部调用了 functionB()
,这是一种直接的函数嵌套调用,但并没有形成递归关系。
递归调用:函数直接或间接地调用自身来解决问题。
#include <stdio.h>
// 定义一个计算阶乘的递归函数
int factorial(int n) {
// 递归边界条件:0和1的阶乘都为1
if (n == 0 || n == 1)
return 1;
// 递归调用自身
return n * factorial(n - 1);
}
int main() {
int number = 5;
// 计算并输出5的阶乘
printf("The factorial of %d is: %d\n", number, factorial(number));
return 0;
}
factorial()
函数在其定义内部调用了自身,并通过一个或多个终止条件(即递归边界)来结束递归过程。在这个例子中,当 n
等于0
或1
时,递归停止,否则继续调用自身以计算下一个较小数的阶乘。
作用域和生命周期
函数内声明的变量只在该函数内部有效,具有局部作用域。例:
#include <stdio.h>
// 全局作用域的变量和函数声明
int globalVar = 10;
void globalFunction() {
printf("Inside global function.\n");
}
void localFunction() {
// 局部作用域的变量声明
int localVar = 20;
printf("globalVar inside localFunction: %d\n", globalVar);
printf("localVar inside localFunction: %d\n", localVar);
// 函数内部调用全局函数
globalFunction();
}
int main() {
// 在main函数中可以访问全局作用域的变量和函数
printf("globalVar in main: %d\n", globalVar);
// 调用局部作用域的函数
localFunction();
// 尝试访问局部作用域的变量会出错(在这个上下文中不存在)
// printf("localVar in main: %d\n", localVar); // 这行代码会导致编译错误
return 0;
}
在上述示例中:
globalVar
和globalFunction
的作用域是全局的,它们在整个程序文件中都可见。localVar
的作用域是localFunction
函数内部,它只在该函数执行期间有效。在函数外部尝试访问 localVar 是非法的。
全局变量在整个程序范围内都是可见的。
在C语言中,局部静态变量具有特殊的生命周期,它的作用域仍然是局部的,但生命周期从其定义时开始直到程序结束。
#include <stdio.h>
void localStaticFunction() {
// 局部静态变量声明,生命周期从第一次进入此函数开始至程序结束
static int staticLocalVar = 0;
staticLocalVar++;
printf("staticLocalVar: %d (lifetime from first call until program ends)\n", staticLocalVar);
}
int main() {
for (int i = 0; i < 5; ++i) {
localStaticFunction();
}
return 0;
}
在上述示例中:
staticLocalVar
是一个局部静态变量,在localStaticFunction
函数内定义,但它每次被调用时都会保留上次调用结束时的状态,即它的生命周期从首次进入函数开始,持续到整个程序运行结束。
内联函数与匿名函数
内联函数(inline)是一种优化手段,编译器尝试将其展开以减少函数调用开销。
// C++ 示例
#include <iostream>
// 内联函数声明和定义
inline int add(int a, int b) {
return a + b;
}
int main() {
std::cout << "The sum is: " << add(5, 7) << std::endl;
return 0;
}
C99及之后的标准引入了 func_ _
关键字用于定义匿名函数,但严格意义上的匿名函数(如Lambda表达式)不是C语言的特性,而是C++等其他语言支持的。
// C++ Lambda 示例
#include <iostream>
int main() {
// 定义一个接受两个整数参数并返回其和的lambda表达式
auto addLambda = [](int a, int b) -> int {
return a + b;
};
// 使用lambda函数计算和并输出结果
int sum = addLambda(5, 7);
std::cout << "The sum using lambda is: " << sum << std::endl;
return 0;
}
在C语言中没有原生支持内联函数和匿名函数的概念。尽管有一些编译器提供了对内联函数的扩展支持,但并不能像C++那样直接声明和使用lambda表达式。
通过使用函数,C语言程序得以模块化,提高了代码的复用性、可读性和可维护性。同时,函数使得逻辑结构更加清晰,有利于复杂问题的解决。
示例
#include <stdio.h>
// 从终端保存数字输入到数组
void inputToArray(int arr[], int n);
// 打印 int 数组
void printIntArray(int arr[], int n);
// 从小到大排序
void sortIntArray(int arr[], int n);
int main(int argc, char const *argv[])
{
int arr[5] = {0};
int n = sizeof(arr) / sizeof(arr[0]);
inputToArray(arr, n);
sortIntArray(arr, n);
printIntArray(arr, n);
return 0;
}
void inputToArray(int arr[], int n)
{
for (int i = 0; i < n; i++)
{
printf("请输入第 %d 个数字:\n", i + 1);
scanf("%d", &arr[i]);
}
return;
}
void printIntArray(int arr[], int n)
{
for (int i = 0; i < n; i++)
{
printf("%d ", arr[i]);
}
printf("\n");
return;
}
void sortIntArray(int arr[], int n)
{
for (int i = 0; i < n - 1; ++i)
{ // 外层循环控制遍历次数
for (int j = 0; j < n - 1 - i; ++j)
{ // 内层循环进行元素比较和交换
if (arr[j] > arr[j + 1])
{ // 如果前一个元素大于后一个元素,则交换它们
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
return;
}
评论区